master aac44f75888d cached
41 files
100.2 KB
27.3k tokens
128 symbols
1 requests
Download .txt
Repository: ZQ330093887/RichEditotAndroid
Branch: master
Commit: aac44f75888d
Files: 41
Total size: 100.2 KB

Directory structure:
gitextract_1bj4zycb/

├── .gitignore
├── .idea/
│   ├── dictionaries/
│   │   └── zhouqiong.xml
│   ├── gradle.xml
│   ├── misc.xml
│   ├── modules.xml
│   └── runConfigurations.xml
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── example/
│       │               └── zhouqiong/
│       │                   └── richeditotandroid/
│       │                       └── ExampleInstrumentedTest.java
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── assets/
│       │   │   ├── editor.html
│       │   │   ├── normalize.css
│       │   │   ├── rich_editor.js
│       │   │   └── style.css
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── example/
│       │   │           └── zhouqiong/
│       │   │               └── richeditotandroid/
│       │   │                   ├── ui/
│       │   │                   │   ├── MainActivity.java
│       │   │                   │   └── WebDataActivity.java
│       │   │                   ├── utils/
│       │   │                   │   └── Utils.java
│       │   │                   └── view/
│       │   │                       ├── ColorPickerView.java
│       │   │                       └── RichEditor.java
│       │   └── res/
│       │       ├── anim/
│       │       │   ├── dialog_enter.xml
│       │       │   └── dialog_exit.xml
│       │       ├── drawable/
│       │       │   └── ic_launcher_background.xml
│       │       ├── drawable-v24/
│       │       │   └── ic_launcher_foreground.xml
│       │       ├── layout/
│       │       │   ├── activity_main.xml
│       │       │   └── activity_show_diarys.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       └── values/
│       │           ├── attrs.xml
│       │           ├── colors.xml
│       │           ├── strings.xml
│       │           └── styles.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── example/
│                       └── zhouqiong/
│                           └── richeditotandroid/
│                               └── ExampleUnitTest.java
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild


================================================
FILE: .idea/dictionaries/zhouqiong.xml
================================================
<component name="ProjectDictionaryState">
  <dictionary name="zhouqiong" />
</component>

================================================
FILE: .idea/gradle.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="GradleSettings">
    <option name="linkedExternalProjectsSettings">
      <GradleProjectSettings>
        <option name="distributionType" value="DEFAULT_WRAPPED" />
        <option name="externalProjectPath" value="$PROJECT_DIR$" />
        <option name="modules">
          <set>
            <option value="$PROJECT_DIR$" />
            <option value="$PROJECT_DIR$/app" />
          </set>
        </option>
        <option name="resolveModulePerSourceSet" value="false" />
      </GradleProjectSettings>
    </option>
  </component>
</project>

================================================
FILE: .idea/misc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="NullableNotNullManager">
    <option name="myDefaultNullable" value="android.support.annotation.Nullable" />
    <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
    <option name="myNullables">
      <value>
        <list size="4">
          <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
          <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
          <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
          <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
        </list>
      </value>
    </option>
    <option name="myNotNulls">
      <value>
        <list size="4">
          <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
          <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
          <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
          <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
        </list>
      </value>
    </option>
  </component>
  <component name="ProjectInspectionProfilesVisibleTreeState">
    <entry key="Project Default">
      <profile-state>
        <expanded-state>
          <State>
            <id />
          </State>
          <State>
            <id>Android</id>
          </State>
          <State>
            <id>CorrectnessLintAndroid</id>
          </State>
          <State>
            <id>LintAndroid</id>
          </State>
          <State>
            <id>PerformanceLintAndroid</id>
          </State>
        </expanded-state>
        <selected-state>
          <State>
            <id>Android</id>
          </State>
        </selected-state>
      </profile-state>
    </entry>
  </component>
  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
    <output url="file://$PROJECT_DIR$/build/classes" />
  </component>
  <component name="ProjectType">
    <option name="id" value="Android" />
  </component>
  <component name="masterDetails">
    <states>
      <state key="Copyright.UI">
        <settings>
          <splitter-proportions>
            <option name="proportions">
              <list>
                <option value="0.2" />
              </list>
            </option>
          </splitter-proportions>
        </settings>
      </state>
      <state key="ScopeChooserConfigurable.UI">
        <settings>
          <splitter-proportions>
            <option name="proportions">
              <list>
                <option value="0.2" />
              </list>
            </option>
          </splitter-proportions>
        </settings>
      </state>
    </states>
  </component>
</project>

================================================
FILE: .idea/modules.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/RichEditotAndroid.iml" filepath="$PROJECT_DIR$/RichEditotAndroid.iml" />
      <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
    </modules>
  </component>
</project>

================================================
FILE: .idea/runConfigurations.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="RunConfigurationProducerService">
    <option name="ignoredProducers">
      <set>
        <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
      </set>
    </option>
  </component>
</project>

================================================
FILE: README.md
================================================
# RichEditotAndroid
WebView + JavaScript 方式的实现一个android富文本编辑器
>最近产品提出在移动端实现富文本编辑器功能,需要能编辑文字加粗、颜色、插入图片、下划线等一系列功能等,最后要生成一个html格式然后base64转码后上传服务端,最后在前端网页和移动端显示。
关于作者 的 《简书》 主页点击这里:[Android富文本编辑器](https://www.jianshu.com/p/9c2c1416d894)


这里的功能需要根据需求实现,通过insertImage传入一个URL或者本地图片路径都可以,这里用户可以自己调用本地相或者拍照获取图片,传图本地图片路径,也可以将本地图片路径上传到服务器(自己的服务器或者免费的七牛服务器),返回在服务端的URL地址,将地址传如即可(我这里传了一张写死的图片URL,如果你插入的图片不现实,请检查你是否添加网络请求权限<uses-permission android:name="android.permission.INTERNET" />)

![](https://upload-images.jianshu.io/upload_images/3278692-937a13db04c28c9f.gif?imageMogr2/auto-orient/strip)
![](https://upload-images.jianshu.io/upload_images/3278692-d309391fbdf9c3f6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](https://upload-images.jianshu.io/upload_images/3278692-960adde8b912f4af.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](https://upload-images.jianshu.io/upload_images/3278692-bf1fbb1028586dba.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)



================================================
FILE: app/.gitignore
================================================
/build


================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.example.zhouqiong.richeditotandroid"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}


================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile


================================================
FILE: app/src/androidTest/java/com/example/zhouqiong/richeditotandroid/ExampleInstrumentedTest.java
================================================
package com.example.zhouqiong.richeditotandroid;

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

/**
 * Instrumented test, which will execute on an Android device.
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
    @Test
    public void useAppContext() throws Exception {
        // Context of the app under test.
        Context appContext = InstrumentationRegistry.getTargetContext();

        assertEquals("com.example.zhouqiong.richeditotandroid", appContext.getPackageName());
    }
}


================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.zhouqiong.richeditotandroid">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:windowSoftInputMode="adjustResize">
        <activity android:name=".ui.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".ui.WebDataActivity" />
    </application>

</manifest>

================================================
FILE: app/src/main/assets/editor.html
================================================
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="user-scalable=no">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <link rel="stylesheet" type="text/css" href="normalize.css">
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="editor" contenteditable="true"></div>
<script type="text/javascript" src="rich_editor.js"></script>
</body>
</html>


================================================
FILE: app/src/main/assets/normalize.css
================================================
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */

/**
 * 1. Set default font family to sans-serif.
 * 2. Prevent iOS text size adjust after orientation change, without disabling
 *    user zoom.
 */

html {
  font-family: sans-serif; /* 1 */
  -webkit-text-size-adjust: 100%; /* 2 */
}

/**
 * Remove default margin.
 */

body {
  margin: 0;
}

/* HTML5 display definitions
   ========================================================================== */

/**
 * Correct `block` display not defined for any HTML5 element in IE 8/9.
 * Correct `block` display not defined for `details` or `summary` in IE 10/11
 * and Firefox.
 * Correct `block` display not defined for `main` in IE 11.
 */

article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
  display: block;
}

/**
 * 1. Correct `inline-block` display not defined in IE 8/9.
 * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
 */

audio,
canvas,
progress,
video {
  display: inline-block; /* 1 */
  vertical-align: baseline; /* 2 */
}

/**
 * Prevent modern browsers from displaying `audio` without controls.
 * Remove excess height in iOS 5 devices.
 */

audio:not([controls]) {
  display: none;
  height: 0;
}

/**
 * Address `[hidden]` styling not present in IE 8/9/10.
 * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
 */

[hidden],
template {
  display: none;
}

/* Links
   ========================================================================== */

/**
 * Remove the gray background color from active links in IE 10.
 */

a {
  background-color: transparent;
}

/**
 * Improve readability when focused and also mouse hovered in all browsers.
 */

a:active,
a:hover {
  outline: 0;
}

/* Text-level semantics
   ========================================================================== */

/**
 * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
 */

abbr[title] {
  border-bottom: 1px dotted;
}

/**
 * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
 */

b,
strong {
  font-weight: bold;
}

/**
 * Address styling not present in Safari and Chrome.
 */

dfn {
  font-style: italic;
}

/**
 * Address variable `h1` font-size and margin within `section` and `article`
 * contexts in Firefox 4+, Safari, and Chrome.
 */

h1 {
  font-size: 2em;
  margin: 0.67em 0;
}

/**
 * Address styling not present in IE 8/9.
 */

mark {
  background: #ff0;
  color: #000;
}

/**
 * Address inconsistent and variable font size in all browsers.
 */

small {
  font-size: 80%;
}

/**
 * Prevent `sub` and `sup` affecting `line-height` in all browsers.
 */

sub,
sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

sup {
  top: -0.5em;
}

sub {
  bottom: -0.25em;
}

/* Embedded content
   ========================================================================== */

/**
 * Remove border when inside `a` element in IE 8/9/10.
 */

img {
  border: 0;
}

/**
 * Correct overflow not hidden in IE 9/10/11.
 */

svg:not(:root) {
  overflow: hidden;
}

/* Grouping content
   ========================================================================== */

/**
 * Address margin not present in IE 8/9 and Safari.
 */

figure {
  margin: 1em 40px;
}

/**
 * Address differences between Firefox and other browsers.
 */

hr {
  box-sizing: content-box;
  height: 0;
}

/**
 * Contain overflow in all browsers.
 */

pre {
  overflow: auto;
}

/**
 * Address odd `em`-unit font size rendering in all browsers.
 */

code,
kbd,
pre,
samp {
  font-family: monospace, monospace;
  font-size: 1em;
}

/* Forms
   ========================================================================== */

/**
 * Known limitation: by default, Chrome and Safari on OS X allow very limited
 * styling of `select`, unless a `border` property is set.
 */

/**
 * 1. Correct color not being inherited.
 *    Known issue: affects color of disabled elements.
 * 2. Correct font properties not being inherited.
 * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
 */

button,
input,
optgroup,
select,
textarea {
  color: inherit; /* 1 */
  font: inherit; /* 2 */
  margin: 0; /* 3 */
}

/**
 * Address `overflow` set to `hidden` in IE 8/9/10/11.
 */

button {
  overflow: visible;
}

/**
 * Address inconsistent `text-transform` inheritance for `button` and `select`.
 * All other form control elements do not inherit `text-transform` values.
 * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
 * Correct `select` style inheritance in Firefox.
 */

button,
select {
  text-transform: none;
}

/**
 * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
 *    and `video` controls.
 * 2. Correct inability to style clickable `input` types in iOS.
 * 3. Improve usability and consistency of cursor style between image-type
 *    `input` and others.
 */

button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
  -webkit-appearance: button; /* 2 */
  cursor: pointer; /* 3 */
}

/**
 * Re-set default cursor for disabled elements.
 */

button[disabled],
html input[disabled] {
  cursor: default;
}

/**
 * Address Firefox 4+ setting `line-height` on `input` using `!important` in
 * the UA stylesheet.
 */

input {
  line-height: normal;
}

/**
 * It's recommended that you don't attempt to style these elements.
 * Firefox's implementation doesn't respect box-sizing, padding, or width.
 *
 * 1. Address box sizing set to `content-box` in IE 8/9/10.
 * 2. Remove excess padding in IE 8/9/10.
 */

input[type="checkbox"],
input[type="radio"] {
  box-sizing: border-box; /* 1 */
  padding: 0; /* 2 */
}

/**
 * Fix the cursor style for Chrome's increment/decrement buttons. For certain
 * `font-size` values of the `input`, it causes the cursor style of the
 * decrement button to change from `default` to `text`.
 */

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  height: auto;
}

/**
 * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
 * 2. Address `box-sizing` set to `border-box` in Safari and Chrome
 */

input[type="search"] {
  -webkit-appearance: textfield; /* 1 */
  -webkit-box-sizing: content-box; /* 2 */
  box-sizing: content-box;
}

/**
 * Remove inner padding and search cancel button in Safari and Chrome on OS X.
 * Safari (but not Chrome) clips the cancel button when the search input has
 * padding (and `textfield` appearance).
 */

input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
}

/**
 * Define consistent border, margin, and padding.
 */

fieldset {
  border: 1px solid #c0c0c0;
  margin: 0 2px;
  padding: 0.35em 0.625em 0.75em;
}

/**
 * 1. Correct `color` not being inherited in IE 8/9/10/11.
 * 2. Remove padding so people aren't caught out if they zero out fieldsets.
 */

legend {
  border: 0; /* 1 */
  padding: 0; /* 2 */
}

/**
 * Remove default vertical scrollbar in IE 8/9/10/11.
 */

textarea {
  overflow: auto;
}

/**
 * Don't inherit the `font-weight` (applied by a rule above).
 * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
 */

optgroup {
  font-weight: bold;
}

/* Tables
   ========================================================================== */

/**
 * Remove most spacing between table cells.
 */

table {
  border-collapse: collapse;
  border-spacing: 0;
}

td,
th {
  padding: 0;
}

================================================
FILE: app/src/main/assets/rich_editor.js
================================================
/**
 * Copyright (C) 2017 Wasabeef
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var RE = {};

RE.currentSelection = {
    "startContainer": 0,
    "startOffset": 0,
    "endContainer": 0,
    "endOffset": 0};

RE.editor = document.getElementById('editor');

document.addEventListener("selectionchange", function() { RE.backuprange(); });

// Initializations
RE.callback = function() {
    window.location.href = "re-callback://" + encodeURI(RE.getHtml());
}

RE.setHtml = function(contents) {
    RE.editor.innerHTML = decodeURIComponent(contents.replace(/\+/g, '%20'));
}

RE.getHtml = function() {
    return RE.editor.innerHTML;
}

RE.getText = function() {
    return RE.editor.innerText;
}

RE.setBaseTextColor = function(color) {
    RE.editor.style.color  = color;
}

RE.setBaseFontSize = function(size) {
    RE.editor.style.fontSize = size;
}

RE.setPadding = function(left, top, right, bottom) {
  RE.editor.style.paddingLeft = left;
  RE.editor.style.paddingTop = top;
  RE.editor.style.paddingRight = right;
  RE.editor.style.paddingBottom = bottom;
}

RE.setBackgroundColor = function(color) {
    document.body.style.backgroundColor = color;
}

RE.setBackgroundImage = function(image) {
    RE.editor.style.backgroundImage = image;
}

RE.setWidth = function(size) {
    RE.editor.style.minWidth = size;
}

RE.setHeight = function(size) {
    RE.editor.style.height = size;
}

RE.setTextAlign = function(align) {
    RE.editor.style.textAlign = align;
}

RE.setVerticalAlign = function(align) {
    RE.editor.style.verticalAlign = align;
}

RE.setPlaceholder = function(placeholder) {
    RE.editor.setAttribute("placeholder", placeholder);
}

RE.setInputEnabled = function(inputEnabled) {
    RE.editor.contentEditable = String(inputEnabled);
}

RE.undo = function() {
    document.execCommand('undo', false, null);
}

RE.redo = function() {
    document.execCommand('redo', false, null);
}

RE.setBold = function() {
    document.execCommand('bold', false, null);
}

RE.setItalic = function() {
    document.execCommand('italic', false, null);
}

RE.setSubscript = function() {
    document.execCommand('subscript', false, null);
}

RE.setSuperscript = function() {
    document.execCommand('superscript', false, null);
}

RE.setStrikeThrough = function() {
    document.execCommand('strikeThrough', false, null);
}

RE.setUnderline = function() {
    document.execCommand('underline', false, null);
}

RE.setBullets = function() {
    document.execCommand('insertUnorderedList', false, null);
}

RE.setNumbers = function() {
    document.execCommand('insertOrderedList', false, null);
}

RE.setTextColor = function(color) {
    RE.restorerange();
    document.execCommand("styleWithCSS", null, true);
    document.execCommand('foreColor', false, color);
    document.execCommand("styleWithCSS", null, false);
}

RE.setTextBackgroundColor = function(color) {
    RE.restorerange();
    document.execCommand("styleWithCSS", null, true);
    document.execCommand('hiliteColor', false, color);
    document.execCommand("styleWithCSS", null, false);
}

RE.setFontSize = function(fontSize){
    document.execCommand("fontSize", false, fontSize);
}

RE.setHeading = function(heading) {
    document.execCommand('formatBlock', false, '<h'+heading+'>');
}

RE.setIndent = function() {
    document.execCommand('indent', false, null);
}

RE.setOutdent = function() {
    document.execCommand('outdent', false, null);
}

RE.setJustifyLeft = function() {
    document.execCommand('justifyLeft', false, null);
}

RE.setJustifyCenter = function() {
    document.execCommand('justifyCenter', false, null);
}

RE.setJustifyRight = function() {
    document.execCommand('justifyRight', false, null);
}

RE.setBlockquote = function() {
    document.execCommand('formatBlock', false, '<blockquote>');
}

RE.insertImage = function(url, alt) {
    var html = '<img src="' + url + '" alt="' + alt + '" />';
    RE.insertHTML(html);
}

RE.insertHTML = function(html) {
    RE.restorerange();
    document.execCommand('insertHTML', false, html);
}

RE.insertLink = function(url, title) {
    RE.restorerange();
    var sel = document.getSelection();
    if (sel.toString().length == 0) {
        document.execCommand("insertHTML",false,"<a href='"+url+"'>"+title+"</a>");
    } else if (sel.rangeCount) {
       var el = document.createElement("a");
       el.setAttribute("href", url);
       el.setAttribute("title", title);

       var range = sel.getRangeAt(0).cloneRange();
       range.surroundContents(el);
       sel.removeAllRanges();
       sel.addRange(range);
   }
    RE.callback();
}

RE.setTodo = function(text) {
    var html = '<input type="checkbox" name="'+ text +'" value="'+ text +'"/> &nbsp;';
    document.execCommand('insertHTML', false, html);
}

RE.prepareInsert = function() {
    RE.backuprange();
}

RE.backuprange = function(){
    var selection = window.getSelection();
    if (selection.rangeCount > 0) {
      var range = selection.getRangeAt(0);
      RE.currentSelection = {
          "startContainer": range.startContainer,
          "startOffset": range.startOffset,
          "endContainer": range.endContainer,
          "endOffset": range.endOffset};
    }
}

RE.restorerange = function(){
    var selection = window.getSelection();
    selection.removeAllRanges();
    var range = document.createRange();
    range.setStart(RE.currentSelection.startContainer, RE.currentSelection.startOffset);
    range.setEnd(RE.currentSelection.endContainer, RE.currentSelection.endOffset);
    selection.addRange(range);
}

RE.enabledEditingItems = function(e) {
    var items = [];
    if (document.queryCommandState('bold')) {
        items.push('bold');
    }
    if (document.queryCommandState('italic')) {
        items.push('italic');
    }
    if (document.queryCommandState('subscript')) {
        items.push('subscript');
    }
    if (document.queryCommandState('superscript')) {
        items.push('superscript');
    }
    if (document.queryCommandState('strikeThrough')) {
        items.push('strikeThrough');
    }
    if (document.queryCommandState('underline')) {
        items.push('underline');
    }
    if (document.queryCommandState('insertOrderedList')) {
        items.push('orderedList');
    }
    if (document.queryCommandState('insertUnorderedList')) {
        items.push('unorderedList');
    }
    if (document.queryCommandState('justifyCenter')) {
        items.push('justifyCenter');
    }
    if (document.queryCommandState('justifyFull')) {
        items.push('justifyFull');
    }
    if (document.queryCommandState('justifyLeft')) {
        items.push('justifyLeft');
    }
    if (document.queryCommandState('justifyRight')) {
        items.push('justifyRight');
    }
    if (document.queryCommandState('insertHorizontalRule')) {
        items.push('horizontalRule');
    }
    var formatBlock = document.queryCommandValue('formatBlock');
    if (formatBlock.length > 0) {
        items.push(formatBlock);
    }

    window.location.href = "re-state://" + encodeURI(items.join(','));
}

RE.focus = function() {
    var range = document.createRange();
    range.selectNodeContents(RE.editor);
    range.collapse(false);
    var selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(range);
    RE.editor.focus();
}

RE.blurFocus = function() {
    RE.editor.blur();
}

RE.removeFormat = function() {
    document.execCommand('removeFormat', false, null);
}

// Event Listeners
RE.editor.addEventListener("input", RE.callback);
RE.editor.addEventListener("keyup", function(e) {
    var KEY_LEFT = 37, KEY_RIGHT = 39;
    if (e.which == KEY_LEFT || e.which == KEY_RIGHT) {
        RE.enabledEditingItems(e);
    }
});
RE.editor.addEventListener("click", RE.enabledEditingItems);


================================================
FILE: app/src/main/assets/style.css
================================================
/**
 * Copyright (C) 2017 Wasabeef
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

@charset "UTF-8";


html {
  height: 100%;
}

body {
  overflow: scroll;
  display: table;
  table-layout: fixed;
  width: 100%;
  min-height:100%;
}

#editor {
  display: table-cell;
  outline: 0px solid transparent;
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}

#editor[placeholder]:empty:not(:focus):before {
  content: attr(placeholder);
  opacity: .5;
}}


================================================
FILE: app/src/main/java/com/example/zhouqiong/richeditotandroid/ui/MainActivity.java
================================================
package com.example.zhouqiong.richeditotandroid.ui;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.example.zhouqiong.richeditotandroid.R;
import com.example.zhouqiong.richeditotandroid.view.ColorPickerView;
import com.example.zhouqiong.richeditotandroid.view.RichEditor;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    /********************View**********************/
    //文本编辑器
    private RichEditor mEditor;
    //加粗按钮
    private ImageView mBold;
    //颜色编辑器
    private TextView mTextColor;
    //显示显示View
    private LinearLayout llColorView;
    //预览按钮
    private TextView mPreView;
    //图片按钮
    private TextView mImage;
    //按序号排列(ol)
    private ImageView mListOL;
    //按序号排列(ul)
    private ImageView mListUL;
    //字体下划线
    private ImageView mLean;
    //字体倾斜
    private ImageView mItalic;
    //字体左对齐
    private ImageView mAlignLeft;
    //字体右对齐
    private ImageView mAlignRight;
    //字体居中对齐
    private ImageView mAlignCenter;
    //字体缩进
    private ImageView mIndent;
    //字体较少缩进
    private ImageView mOutdent;
    //字体索引
    private ImageView mBlockquote;
    //字体中划线
    private ImageView mStrikethrough;
    //字体上标
    private ImageView mSuperscript;
    //字体下标
    private ImageView mSubscript;
    /********************boolean开关**********************/
    //是否加粗
    boolean isClickBold = false;
    //是否正在执行动画
    boolean isAnimating = false;
    //是否按ol排序
    boolean isListOl = false;
    //是否按ul排序
    boolean isListUL = false;
    //是否下划线字体
    boolean isTextLean = false;
    //是否下倾斜字体
    boolean isItalic = false;
    //是否左对齐
    boolean isAlignLeft = false;
    //是否右对齐
    boolean isAlignRight = false;
    //是否中对齐
    boolean isAlignCenter = false;
    //是否缩进
    boolean isIndent = false;
    //是否较少缩进
    boolean isOutdent = false;
    //是否索引
    boolean isBlockquote = false;
    //字体中划线
    boolean isStrikethrough = false;
    //字体上标
    boolean isSuperscript = false;
    //字体下标
    boolean isSubscript = false;
    /********************变量**********************/
    //折叠视图的宽高
    private int mFoldedViewMeasureHeight;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initClickListener();
    }

    /**
     * 初始化View
     */
    private void initView() {
        initEditor();
        initMenu();
        initColorPicker();
    }

    /**
     * 初始化文本编辑器
     */
    private void initEditor() {
        mEditor = findViewById(R.id.re_main_editor);
        //mEditor.setEditorHeight(400);
        //输入框显示字体的大小
        mEditor.setEditorFontSize(18);
        //输入框显示字体的颜色
        mEditor.setEditorFontColor(Color.BLACK);
        //输入框背景设置
        mEditor.setEditorBackgroundColor(Color.WHITE);
        //mEditor.setBackgroundColor(Color.BLUE);
        //mEditor.setBackgroundResource(R.drawable.bg);
        //mEditor.setBackground("https://raw.githubusercontent.com/wasabeef/art/master/chip.jpg");
        //输入框文本padding
        mEditor.setPadding(10, 10, 10, 10);
        //输入提示文本
        mEditor.setPlaceholder("请输入编辑内容");
        //是否允许输入
        //mEditor.setInputEnabled(false);
        //文本输入框监听事件
        mEditor.setOnTextChangeListener(new RichEditor.OnTextChangeListener() {
            @Override
            public void onTextChange(String text) {
                Log.d("mEditor", "html文本:" + text);
            }
        });
    }

    /**
     * 初始化颜色选择器
     */
    private void initColorPicker() {
        ColorPickerView right = findViewById(R.id.cpv_main_color);
        right.setOnColorPickerChangeListener(new ColorPickerView.OnColorPickerChangeListener() {
            @Override
            public void onColorChanged(ColorPickerView picker, int color) {
                mTextColor.setBackgroundColor(color);
                mEditor.setTextColor(color);
            }

            @Override
            public void onStartTrackingTouch(ColorPickerView picker) {

            }

            @Override
            public void onStopTrackingTouch(ColorPickerView picker) {

            }
        });
    }

    /**
     * 初始化菜单按钮
     */
    private void initMenu() {
        mBold = findViewById(R.id.button_bold);
        mTextColor = findViewById(R.id.button_text_color);
        llColorView = findViewById(R.id.ll_main_color);
        mPreView = findViewById(R.id.tv_main_preview);
        mImage = findViewById(R.id.button_image);
        mListOL = findViewById(R.id.button_list_ol);
        mListUL = findViewById(R.id.button_list_ul);
        mLean = findViewById(R.id.button_underline);
        mItalic = findViewById(R.id.button_italic);
        mAlignLeft = findViewById(R.id.button_align_left);
        mAlignRight = findViewById(R.id.button_align_right);
        mAlignCenter = findViewById(R.id.button_align_center);
        mIndent = findViewById(R.id.button_indent);
        mOutdent = findViewById(R.id.button_outdent);
        mBlockquote = findViewById(R.id.action_blockquote);
        mStrikethrough = findViewById(R.id.action_strikethrough);
        mSuperscript = findViewById(R.id.action_superscript);
        mSubscript = findViewById(R.id.action_subscript);
        getViewMeasureHeight();
    }

    /**
     * 获取控件的高度
     */
    private void getViewMeasureHeight() {
        //获取像素密度
        float mDensity = getResources().getDisplayMetrics().density;
        //获取布局的高度
        int w = View.MeasureSpec.makeMeasureSpec(0,
                View.MeasureSpec.UNSPECIFIED);
        int h = View.MeasureSpec.makeMeasureSpec(0,
                View.MeasureSpec.UNSPECIFIED);
        llColorView.measure(w, h);
        int height = llColorView.getMeasuredHeight();
        mFoldedViewMeasureHeight = (int) (mDensity * height + 0.5);
    }

    private void initClickListener() {
        mBold.setOnClickListener(this);
        mTextColor.setOnClickListener(this);
        mPreView.setOnClickListener(this);
        mImage.setOnClickListener(this);
        mListOL.setOnClickListener(this);
        mListUL.setOnClickListener(this);
        mLean.setOnClickListener(this);
        mItalic.setOnClickListener(this);
        mAlignLeft.setOnClickListener(this);
        mAlignRight.setOnClickListener(this);
        mAlignCenter.setOnClickListener(this);
        mIndent.setOnClickListener(this);
        mOutdent.setOnClickListener(this);
        mBlockquote.setOnClickListener(this);
        mStrikethrough.setOnClickListener(this);
        mSuperscript.setOnClickListener(this);
        mSubscript.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        if (id == R.id.button_bold) {//字体加粗
            if (isClickBold) {
                mBold.setImageResource(R.mipmap.bold);
            } else {  //加粗
                mBold.setImageResource(R.mipmap.bold_);
            }
            isClickBold = !isClickBold;
            mEditor.setBold();
        } else if (id == R.id.button_text_color) {//设置字体颜色
            //如果动画正在执行,直接return,相当于点击无效了,不会出现当快速点击时,
            // 动画的执行和ImageButton的图标不一致的情况
            if (isAnimating) return;
            //如果动画没在执行,走到这一步就将isAnimating制为true , 防止这次动画还没有执行完毕的
            //情况下,又要执行一次动画,当动画执行完毕后会将isAnimating制为false,这样下次动画又能执行
            isAnimating = true;

            if (llColorView.getVisibility() == View.GONE) {
                //打开动画
                animateOpen(llColorView);
            } else {
                //关闭动画
                animateClose(llColorView);
            }
        } else if (id == R.id.button_image) {//插入图片
            //这里的功能需要根据需求实现,通过insertImage传入一个URL或者本地图片路径都可以,这里用户可以自己调用本地相
            //或者拍照获取图片,传图本地图片路径,也可以将本地图片路径上传到服务器(自己的服务器或者免费的七牛服务器),
            //返回在服务端的URL地址,将地址传如即可(我这里传了一张写死的图片URL,如果你插入的图片不现实,请检查你是否添加
            // 网络请求权限<uses-permission android:name="android.permission.INTERNET" />)
            mEditor.insertImage("http://www.1honeywan.com/dachshund/image/7.21/7.21_3_thumb.JPG",
                    "dachshund");
        } else if (id == R.id.button_list_ol) {
            if (isListOl) {
                mListOL.setImageResource(R.mipmap.list_ol);
            } else {
                mListOL.setImageResource(R.mipmap.list_ol_);
            }
            isListOl = !isListOl;
            mEditor.setNumbers();
        } else if (id == R.id.button_list_ul) {
            if (isListUL) {
                mListUL.setImageResource(R.mipmap.list_ul);
            } else {
                mListUL.setImageResource(R.mipmap.list_ul_);
            }
            isListUL = !isListUL;
            mEditor.setBullets();
        } else if (id == R.id.button_underline) {
            if (isTextLean) {
                mLean.setImageResource(R.mipmap.underline);
            } else {
                mLean.setImageResource(R.mipmap.underline_);
            }
            isTextLean = !isTextLean;
            mEditor.setUnderline();
        } else if (id == R.id.button_italic) {
            if (isItalic) {
                mItalic.setImageResource(R.mipmap.lean);
            } else {
                mItalic.setImageResource(R.mipmap.lean_);
            }
            isItalic = !isItalic;
            mEditor.setItalic();
        } else if (id == R.id.button_align_left) {
            if (isAlignLeft) {
                mAlignLeft.setImageResource(R.mipmap.align_left);
            } else {
                mAlignLeft.setImageResource(R.mipmap.align_left_);
            }
            isAlignLeft = !isAlignLeft;
            mEditor.setAlignLeft();
        } else if (id == R.id.button_align_right) {
            if (isAlignRight) {
                mAlignRight.setImageResource(R.mipmap.align_right);
            } else {
                mAlignRight.setImageResource(R.mipmap.align_right_);
            }
            isAlignRight = !isAlignRight;
            mEditor.setAlignRight();
        } else if (id == R.id.button_align_center) {
            if (isAlignCenter) {
                mAlignCenter.setImageResource(R.mipmap.align_center);
            } else {
                mAlignCenter.setImageResource(R.mipmap.align_center_);
            }
            isAlignCenter = !isAlignCenter;
            mEditor.setAlignCenter();
        } else if (id == R.id.button_indent) {
            if (isIndent) {
                mIndent.setImageResource(R.mipmap.indent);
            } else {
                mIndent.setImageResource(R.mipmap.indent_);
            }
            isIndent = !isIndent;
            mEditor.setIndent();
        } else if (id == R.id.button_outdent) {
            if (isOutdent) {
                mOutdent.setImageResource(R.mipmap.outdent);
            } else {
                mOutdent.setImageResource(R.mipmap.outdent_);
            }
            isOutdent = !isOutdent;
            mEditor.setOutdent();
        } else if (id == R.id.action_blockquote) {
            if (isBlockquote) {
                mBlockquote.setImageResource(R.mipmap.blockquote);
            } else {
                mBlockquote.setImageResource(R.mipmap.blockquote_);
            }
            isBlockquote = !isBlockquote;
            mEditor.setBlockquote();
        } else if (id == R.id.action_strikethrough) {
            if (isStrikethrough) {
                mStrikethrough.setImageResource(R.mipmap.strikethrough);
            } else {
                mStrikethrough.setImageResource(R.mipmap.strikethrough_);
            }
            isStrikethrough = !isStrikethrough;
            mEditor.setStrikeThrough();
        } else if (id == R.id.action_superscript) {
            if (isSuperscript) {
                mSuperscript.setImageResource(R.mipmap.superscript);
            } else {
                mSuperscript.setImageResource(R.mipmap.superscript_);
            }
            isSuperscript = !isSuperscript;
            mEditor.setSuperscript();
        } else if (id == R.id.action_subscript) {
            if (isSubscript) {
                mSubscript.setImageResource(R.mipmap.subscript);
            } else {
                mSubscript.setImageResource(R.mipmap.subscript_);
            }
            isSubscript = !isSubscript;
            mEditor.setSubscript();
        }

        //H1--H6省略,需要的自己添加

        else if (id == R.id.tv_main_preview) {//预览
            Intent intent = new Intent(MainActivity.this, WebDataActivity.class);
            intent.putExtra("diarys", mEditor.getHtml());
            startActivity(intent);
        }
    }

    /**
     * 开启动画
     *
     * @param view 开启动画的view
     */
    private void animateOpen(LinearLayout view) {
        view.setVisibility(View.VISIBLE);
        ValueAnimator animator = createDropAnimator(view, 0, mFoldedViewMeasureHeight);
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                isAnimating = false;
            }
        });
        animator.start();
    }

    /**
     * 关闭动画
     *
     * @param view 关闭动画的view
     */
    private void animateClose(final LinearLayout view) {
        int origHeight = view.getHeight();
        ValueAnimator animator = createDropAnimator(view, origHeight, 0);
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                view.setVisibility(View.GONE);
                isAnimating = false;
            }
        });
        animator.start();
    }


    /**
     * 创建动画
     *
     * @param view  开启和关闭动画的view
     * @param start view的高度
     * @param end   view的高度
     * @return ValueAnimator对象
     */
    private ValueAnimator createDropAnimator(final View view, int start, int end) {
        ValueAnimator animator = ValueAnimator.ofInt(start, end);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
                layoutParams.height = value;
                view.setLayoutParams(layoutParams);
            }
        });
        return animator;
    }
}


================================================
FILE: app/src/main/java/com/example/zhouqiong/richeditotandroid/ui/WebDataActivity.java
================================================
package com.example.zhouqiong.richeditotandroid.ui;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

import com.example.zhouqiong.richeditotandroid.R;
import com.example.zhouqiong.richeditotandroid.ui.MainActivity;

/**
 * Created by ZQiong on 2018/3/20.
 */

public class WebDataActivity extends AppCompatActivity {


    //自己制造的一些假数据。外加筛选图片样式
    /****  3333333333 ***************************************************/
//    private String dataStr="<html><body><style>img{ width:100% !important;}</style>"+"<img src=\"https://ss0.bdstatic.com/-0U0bnSm1A5BphGlnYG/tam-ogel/dd9d1d686cdc814db9653b254e00402e_259_194.jpg\" alt=\"\" /> \r<p style=\"text-align:right;\">\r\t品类定位的思考\r</p>\r<h3>\r\t<strong><span style=\"color:#00D5FF;\">品类定</span></strong>\n" +
//            "<h3>\r\t<a href='JavaScript:android.returnAndroid(\"要返回给APP的数据\")'>点击我跳回APP</a>"+"</body></html>";


    /***  11111111111  **************************************************************************/
    //这个数据的外层不加两层<html><body>标签,不过在下面一个地方加上一个div和图片样式
//    private String dataStr = "<img src=\"https://ss0.bdstatic.com/-0U0bnSm1A5BphGlnYG/tam-ogel/dd9d1d686cdc814db9653b254e00402e_259_194.jpg\" alt=\"\" /> \r<p style=\"text-align:right;\">\r\t品类定位的思考\r</p>\r<h3>\r\t<strong><span style=\"color:#00D5FF;\">品类定";

    /****  11111111111  *************************************************************************/

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_show_diarys);
        final String dataStr = getIntent().getStringExtra("diarys");
        initWebView(dataStr);
    }

    public void initWebView(String data) {
        WebView mWebView = findViewById(R.id.showdiarys);
        WebSettings settings = mWebView.getSettings();

        //settings.setUseWideViewPort(true);//调整到适合webview的大小,不过尽量不要用,有些手机有问题
        settings.setLoadWithOverviewMode(true);//设置WebView是否使用预览模式加载界面。
        mWebView.setVerticalScrollBarEnabled(false);//不能垂直滑动
        mWebView.setHorizontalScrollBarEnabled(false);//不能水平滑动
        settings.setTextSize(WebSettings.TextSize.NORMAL);//通过设置WebSettings,改变HTML中文字的大小
        settings.setJavaScriptCanOpenWindowsAutomatically(true);//支持通过JS打开新窗口
        //设置WebView属性,能够执行Javascript脚本
        mWebView.getSettings().setJavaScriptEnabled(true);//设置js可用
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.addJavascriptInterface(new AndroidJavaScript(getApplication()), "android");//设置js接口
        settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);//支持内容重新布局


/******  22222222  ***********************************************************************/
        data = "</Div><head><style>img{ width:100% !important;}</style></head>" + data;//给图片设置一个样式,宽满屏
/******  2222222222  ***********************************************************************/

        mWebView.loadDataWithBaseURL(null, data, "text/html", "utf-8", null);
    }

    /**
     * AndroidJavaScript
     * 本地与h5页面交互的js类,这里写成内部类了
     * returnAndroid方法上@JavascriptInterface一定不能漏了
     */
    private class AndroidJavaScript {
        Context mContxt;

        public AndroidJavaScript(Context mContxt) {
            this.mContxt = mContxt;
        }

        @JavascriptInterface
        public void returnAndroid(String name) {//从网页跳回到APP,这个方法已经在上面的HTML中写上了
            if (name.isEmpty() || name.equals("")) {
                return;
            }
            Toast.makeText(getApplication(), name, Toast.LENGTH_SHORT).show();
            //这里写你的操作///////////////////////
            //MainActivity就是一个空页面,不影响
            Intent intent = new Intent(WebDataActivity.this, MainActivity.class);
            intent.putExtra("name", name);
            startActivity(intent);
        }
    }
}


================================================
FILE: app/src/main/java/com/example/zhouqiong/richeditotandroid/utils/Utils.java
================================================
package com.example.zhouqiong.richeditotandroid.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Base64;

import java.io.ByteArrayOutputStream;

/**
 * Created by ZQiong on 2018/3/22.
 */

public final class Utils {

    private Utils() throws InstantiationException {
        throw new InstantiationException("This class is not for instantiation");
    }

    public static String toBase64(Bitmap bitmap) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
        byte[] bytes = baos.toByteArray();

        return Base64.encodeToString(bytes, Base64.NO_WRAP);
    }

    public static Bitmap toBitmap(Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        int width = drawable.getIntrinsicWidth();
        width = width > 0 ? width : 1;
        int height = drawable.getIntrinsicHeight();
        height = height > 0 ? height : 1;

        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }

    public static Bitmap decodeResource(Context context, int resId) {
        return BitmapFactory.decodeResource(context.getResources(), resId);
    }

    public static long getCurrentTime() {
        return System.currentTimeMillis();
    }
}


================================================
FILE: app/src/main/java/com/example/zhouqiong/richeditotandroid/view/ColorPickerView.java
================================================
package com.example.zhouqiong.richeditotandroid.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.example.zhouqiong.richeditotandroid.R;

/**
 * Created by ZQiong on 2018/3/22.
 * <p>
 * 指示点的半径是颜色条宽度的 4/3 ,两端会有指示点半径长的距离是空白的,这是为了给指示点滑动到端点时留出足够的显示空间,
 * 这也导致当控件宽高的差值太小时,指示点会完全覆盖住颜色条,使颜色条不可见,<strong>因此应尽量根据控件的不同模式使长
 * 边与段边的比例不小于 3 : 1</strong>
 * <br>
 * <p>
 * 两种模式:
 * <p>
 * HORIZONTAL 水平
 * <p>
 * VERTICAL 竖直
 */

public class ColorPickerView extends View {

    /**
     * 指示点颜色
     */
    private int mIndicatorColor;
    /**
     * 是否启用指示点
     */
    private boolean mIndicatorEnable;

    /**
     * View 和 bitmapForColor 的画笔
     */
    private final Paint paint;

    /**
     * 指示点专用画笔,这样可以避免 mIndicatorColor 有 alpha 时,alpha 作用于 View
     */
    private final Paint paintForIndicator;

    private LinearGradient linearGradient;

    /**
     * 除去上下 padding 的端点坐标
     */
    private int mTop, mLeft, mRight, mBottom;

    /**
     * 颜色条圆角矩形边界
     */
    private final Rect rect = new Rect();

    /**
     * bitmapForIndicator 在 View 上的绘制位置
     */
    private final Rect rectForIndicator = new Rect();

    /**
     * 指示点半径
     */
    private int mRadius;

    /**
     * 控件方向
     */
    private Orientation orientation;

    // 默认状态下长边与短边的比例为 6 :1
    private static final int defaultSizeShort = 70; // * 6
    private static final int defaultSizeLong = 420;

    // 不直接绘制在 View 提供的画布上的原因是:选取颜色时需要提取 Bitmap 上的颜色,View 的 Bitmap 无法获取,
    // 而且有指示点时指示点会覆盖主颜色条(重绘颜色条的颜色)
    private Bitmap bitmapForColor;
    private Bitmap bitmapForIndicator;

    /**
     * 是否需要绘制颜色条(指示点),颜色条在选取颜色时不需要再次生成(bitmapForColor),直接绘制就行
     */
    private boolean needReDrawColorTable = true;
    private boolean needReDrawIndicator = true;

    /**
     * 手指在颜色条上的坐标
     */
    private int curX, curY;

    private int[] colors = null;

    private int currentColor;

    /**
     * 控件方向
     */
    public enum Orientation {
        /**
         * 水平
         */
        HORIZONTAL, // 0

        /**
         * 竖直
         */
        VERTICAL // 1

    }

    {
        bitmapForColor = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
        bitmapForIndicator = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);

        //Android4.0(API14)之后硬件加速功能就被默认开启了,setShadowLayer 在开启硬件加速的情况下无效,需要关闭硬件加速
        this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

        paint = new Paint();
        paint.setAntiAlias(true);

        paintForIndicator = new Paint();
        paintForIndicator.setAntiAlias(true);

        curX = curY = Integer.MAX_VALUE;
    }

    public ColorPickerView(Context context) {
        super(context);
    }

    public ColorPickerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ColorPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        final TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ColorPickerView, defStyleAttr, 0);
        mIndicatorColor = array.getColor(R.styleable.ColorPickerView_indicatorColor, Color.WHITE);

        int or = array.getInteger(R.styleable.ColorPickerView_orientation, 0);
        orientation = or == 0 ? Orientation.HORIZONTAL : Orientation.VERTICAL;

        mIndicatorEnable = array.getBoolean(R.styleable.ColorPickerView_indicatorEnable, true);

        array.recycle();

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;

        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {//xml中宽度设为warp_content
            width = getSuggestedMinimumWidth() + getPaddingLeft() + getPaddingRight();
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = getSuggestedMinimumHeight() + getPaddingTop() + getPaddingBottom();
        }

        width = Math.max(width, orientation == Orientation.HORIZONTAL ? defaultSizeLong : defaultSizeShort);
        height = Math.max(height, orientation == Orientation.HORIZONTAL ? defaultSizeShort : defaultSizeLong);

        setMeasuredDimension(width, height);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mTop = getPaddingTop();
        mLeft = getPaddingLeft();
        mBottom = getMeasuredHeight() - getPaddingBottom();
        mRight = getMeasuredWidth() - getPaddingRight();

        if (curX == curY || curY == Integer.MAX_VALUE) {
            curX = getWidth() / 2;
            curY = getHeight() / 2;
        }

        calculBounds();
        if (colors == null) {
            setColors(createDefaultColorTable());
        } else {
            setColors(colors);
        }
        createBitmap();

        if (mIndicatorEnable) {
            needReDrawIndicator = true;
        }

    }

    private void createBitmap() {

        int hc = rect.height();
        int wc = rect.width();
        int hi = mRadius * 2;
        int wi = hi;

        if (bitmapForColor != null) {
            if (!bitmapForColor.isRecycled()) {
                bitmapForColor.recycle();
                bitmapForColor = null;
            }
        }

        if (bitmapForIndicator != null) {
            if (!bitmapForIndicator.isRecycled()) {
                bitmapForIndicator.recycle();
                bitmapForIndicator = null;
            }
        }

        bitmapForColor = Bitmap.createBitmap(wc, hc, Bitmap.Config.ARGB_8888);
        bitmapForIndicator = Bitmap.createBitmap(wi, hi, Bitmap.Config.ARGB_8888);

    }

    /**
     * 计算颜色条边界
     */
    private void calculBounds() {

        /*
         * 将控件可用高度(除去上下 padding )均分为 6 份,以此计算指示点半径,颜色条宽高
         * 控件方向为 HORIZONTAL 时,从上往下依次占的份额为:
         * 1/9 留白
         * 2/9 颜色条上面部分圆
         * 3/9 颜色条宽
         * 2/9 颜色条上面部分圆
         * 1/9 留白
         */
        final int average = 9;

        /*
         * 每一份的高度
         */
        int each;

        int h = mBottom - mTop;
        int w = mRight - mLeft;
        int size = Math.min(w, h);

        if (orientation == Orientation.HORIZONTAL) {
            if (w <= h) { // HORIZONTAL 模式,然而宽却小于高,以 6 :1 的方式重新计算高
                size = w / 6;
            }
        } else {
            if (w >= h) {
                size = h / 6;
            }
        }

        each = size / average;
        mRadius = each * 7 / 2;

        int t, l, b, r;
        final int s = each * 3 / 2;

        if (orientation == Orientation.HORIZONTAL) {
            l = mLeft + mRadius;
            r = mRight - mRadius;

            t = (getHeight() / 2) - s;
            b = (getHeight() / 2) + s;
        } else {
            t = mTop + mRadius;
            b = mBottom - mRadius;

            l = getWidth() / 2 - s;
            r = getWidth() / 2 + s;
        }

        rect.set(l, t, r, b);
    }

    /**
     * 设置颜色条的渐变颜色,不支持具有 alpha 的颜色,{@link Color#TRANSPARENT}会被当成 {@link Color#BLACK}处理
     * 如果想设置 alpha ,可以在{@link OnColorPickerChangeListener#onColorChanged(ColorPickerView, int)} 回调
     * 中调用{@link android.support.v4.graphics.ColorUtils#setAlphaComponent(int, int)}方法添加 alpha 值。
     *
     * @param colors 颜色值
     */
    public void setColors(int... colors) {
        linearGradient = null;
        this.colors = colors;

        if (orientation == Orientation.HORIZONTAL) {
            linearGradient = new LinearGradient(
                    rect.left, rect.top,
                    rect.right, rect.top,
                    colors,
                    null,
                    Shader.TileMode.CLAMP
            );
        } else {
            linearGradient = new LinearGradient(
                    rect.left, rect.top,
                    rect.left, rect.bottom,
                    colors,
                    null,
                    Shader.TileMode.CLAMP
            );
        }

        needReDrawColorTable = true;
        invalidate();
    }

    public int[] createDefaultColorTable() {

//        int[] cs = {
//                Color.rgb(0, 0, 0),//白色
//                Color.rgb(255, 0, 0),//红色
//                Color.rgb(0, 255, 0),//绿色
//                Color.rgb(0, 255, 255),//青色
//                Color.rgb(0, 0, 255),//蓝色
//                Color.rgb(255, 0, 255),//紫色
//                Color.rgb(255, 255, 255)//黑色
//        };
        int[] cs = {
                Color.rgb(0, 0, 0),
                Color.rgb(255, 0, 0),//红色
                Color.rgb(255, 255, 0),
                Color.rgb(0, 255, 0),//绿色
                Color.rgb(0, 255, 255),//青色
                Color.rgb(0, 0, 255),//蓝色
                Color.rgb(255, 0, 255),
                Color.rgb(255, 0, 0)//红色
        };
        return cs;
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (needReDrawColorTable) {
            createColorTableBitmap();
        }
        // 绘制颜色条
        canvas.drawBitmap(bitmapForColor, null, rect, paint);

        if (mIndicatorEnable) {
            if (needReDrawIndicator) {
                createIndicatorBitmap();
            }

            // 绘制指示点
            rectForIndicator.set(curX - mRadius, curY - mRadius, curX + mRadius, curY + mRadius);
            canvas.drawBitmap(bitmapForIndicator, null, rectForIndicator, paint);
        }
    }

    private void createIndicatorBitmap() {

        paintForIndicator.setColor(mIndicatorColor);
        int radius = 3;
        paintForIndicator.setShadowLayer(radius, 0, 0, Color.GRAY);

        Canvas c = new Canvas(bitmapForIndicator);
        c.drawCircle(mRadius, mRadius, mRadius - radius, paintForIndicator);

        needReDrawIndicator = false;
    }

    private void createColorTableBitmap() {

        Canvas c = new Canvas(bitmapForColor);
        RectF rf = new RectF(0, 0, bitmapForColor.getWidth(), bitmapForColor.getHeight());

        // 圆角大小
        int r;
        if (orientation == Orientation.HORIZONTAL) {
            r = bitmapForColor.getHeight() / 2;
        } else {
            r = bitmapForColor.getWidth() / 2;
        }
        // 先绘制黑色背景,否则有 alpha 时绘制不正常
        paint.setColor(Color.BLACK);
        c.drawRoundRect(rf, r, r, paint);

        paint.setShader(linearGradient);
        c.drawRoundRect(rf, r, r, paint);
        paint.setShader(null);

        needReDrawColorTable = false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int ex = (int) event.getX();
        int ey = (int) event.getY();

        if (!inBoundOfColorTable(ex, ey)) {
            return true;
        }

        if (orientation == Orientation.HORIZONTAL) {
            curX = ex;
            curY = getHeight() / 2;
        } else {
            curX = getWidth() / 2;
            curY = ey;
        }

        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
            if (colorPickerChangeListener != null) {
                colorPickerChangeListener.onStartTrackingTouch(this);
                calcuColor();
                colorPickerChangeListener.onColorChanged(this, currentColor);
            }

        } else if (event.getActionMasked() == MotionEvent.ACTION_UP) { //手抬起
            if (colorPickerChangeListener != null) {
                colorPickerChangeListener.onStopTrackingTouch(this);
                calcuColor();
                colorPickerChangeListener.onColorChanged(this, currentColor);
            }

        } else { //按着+拖拽
            if (colorPickerChangeListener != null) {
                calcuColor();
                colorPickerChangeListener.onColorChanged(this, currentColor);
            }
        }

        invalidate();
        return true;
    }

    /**
     * 获得当前指示点所指颜色
     *
     * @return 颜色值
     */
    public int getColor() {
        return calcuColor();
    }

    private boolean inBoundOfColorTable(int ex, int ey) {
        if (orientation == Orientation.HORIZONTAL) {
            if (ex <= mLeft + mRadius || ex >= mRight - mRadius) {
                return false;
            }
        } else {
            if (ey <= mTop + mRadius || ey >= mBottom - mRadius) {
                return false;
            }
        }
        return true;
    }

    private int calcuColor() {
        int x, y;
        if (orientation == Orientation.HORIZONTAL) { // 水平
            y = (rect.bottom - rect.top) / 2;
            if (curX < rect.left) {
                x = 1;
            } else if (curX > rect.right) {
                x = bitmapForColor.getWidth() - 1;
            } else {
                x = curX - rect.left;
            }
        } else { // 竖直
            x = (rect.right - rect.left) / 2;
            if (curY < rect.top) {
                y = 1;
            } else if (curY > rect.bottom) {
                y = bitmapForColor.getHeight() - 1;
            } else {
                y = curY - rect.top;
            }
        }
        int pixel = bitmapForColor.getPixel(x, y);
        currentColor = pixelToColor(pixel);
        return currentColor;
    }

    private int pixelToColor(int pixel) {

        int alpha = Color.alpha(pixel);
        int red = Color.red(pixel);
        int green = Color.green(pixel);
        int blue = Color.blue(pixel);

        return Color.argb(alpha, red, green, blue);
    }

    private OnColorPickerChangeListener colorPickerChangeListener;

    public void setOnColorPickerChangeListener(OnColorPickerChangeListener l) {
        this.colorPickerChangeListener = l;
    }

    public interface OnColorPickerChangeListener {

        /**
         * 选取的颜色值改变时回调
         *
         * @param picker ColorPickerView
         * @param color  颜色
         */
        void onColorChanged(ColorPickerView picker, int color);

        /**
         * 开始颜色选取
         *
         * @param picker ColorPickerView
         */
        void onStartTrackingTouch(ColorPickerView picker);

        /**
         * 停止颜色选取
         *
         * @param picker ColorPickerView
         */
        void onStopTrackingTouch(ColorPickerView picker);
    }


    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable parcelable = super.onSaveInstanceState();
        SavedState ss = new SavedState(parcelable);
        ss.selX = curX;
        ss.selY = curY;
        ss.color = bitmapForColor;
        if (mIndicatorEnable) {
            ss.indicator = bitmapForIndicator;
        }
        return ss;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());

        curX = ss.selX;
        curY = ss.selY;
        colors = ss.colors;

        bitmapForColor = ss.color;
        if (mIndicatorEnable) {
            bitmapForIndicator = ss.indicator;
            needReDrawIndicator = true;
        }
        needReDrawColorTable = true;

    }

    private class SavedState extends BaseSavedState {
        int selX, selY;
        int[] colors;
        Bitmap color;
        Bitmap indicator = null;

        SavedState(Parcelable source) {
            super(source);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(selX);
            out.writeInt(selY);
            out.writeParcelable(color, flags);
            out.writeIntArray(colors);
            if (indicator != null) {
                out.writeParcelable(indicator, flags);
            }
        }
    }

    public void setPosition(int x, int y) {
        if (inBoundOfColorTable(x, y)) {
            curX = x;
            curY = y;
            if (mIndicatorEnable) {
                needReDrawIndicator = true;
            }
            invalidate();
        }
    }


    /**
     * 显示默认的颜色选择器
     */
    public void showDefaultColorTable() {
        setColors(createDefaultColorTable());
    }

    public int getIndicatorColor() {
        return mIndicatorColor;
    }

    public void setIndicatorColor(int color) {
        this.mIndicatorColor = color;
        needReDrawIndicator = true;
        invalidate();
    }

    public void setOrientation(Orientation orientation) {
        this.orientation = orientation;
        needReDrawIndicator = true;
        needReDrawColorTable = true;
        requestLayout();
    }
}


================================================
FILE: app/src/main/java/com/example/zhouqiong/richeditotandroid/view/RichEditor.java
================================================
package com.example.zhouqiong.richeditotandroid.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;


import com.example.zhouqiong.richeditotandroid.utils.Utils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * 富文本编辑实现类
 * Created by ZQiong on 2018/3/22.
 */

public class RichEditor extends WebView {

    public enum Type {
        BOLD,
        ITALIC,
        SUBSCRIPT,
        SUPERSCRIPT,
        STRIKETHROUGH,
        UNDERLINE,
        H1,
        H2,
        H3,
        H4,
        H5,
        H6,
        ORDEREDLIST,
        UNORDEREDLIST,
        JUSTIFYCENTER,
        JUSTIFYFULL,
        JUSTUFYLEFT,
        JUSTIFYRIGHT
    }

    public interface OnTextChangeListener {

        void onTextChange(String text);
    }

    public interface OnDecorationStateListener {

        void onStateChangeListener(String text, List<Type> types);
    }

    public interface AfterInitialLoadListener {

        void onAfterInitialLoad(boolean isReady);
    }

    private static final String SETUP_HTML = "file:///android_asset/editor.html";
    private static final String CALLBACK_SCHEME = "re-callback://";
    private static final String STATE_SCHEME = "re-state://";
    private boolean isReady = false;
    private String mContents;
    private OnTextChangeListener mTextChangeListener;
    private OnDecorationStateListener mDecorationStateListener;
    private AfterInitialLoadListener mLoadListener;

    public RichEditor(Context context) {
        this(context, null);
    }

    public RichEditor(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.webViewStyle);
    }

    @SuppressLint("SetJavaScriptEnabled")
    public RichEditor(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        setVerticalScrollBarEnabled(false);
        setHorizontalScrollBarEnabled(false);
        getSettings().setJavaScriptEnabled(true);
        setWebChromeClient(new WebChromeClient());
        setWebViewClient(createWebviewClient());
        loadUrl(SETUP_HTML);

        applyAttributes(context, attrs);
    }

    protected EditorWebViewClient createWebviewClient() {
        return new EditorWebViewClient();
    }

    public void setOnTextChangeListener(OnTextChangeListener listener) {
        mTextChangeListener = listener;
    }

    public void setOnDecorationChangeListener(OnDecorationStateListener listener) {
        mDecorationStateListener = listener;
    }

    public void setOnInitialLoadListener(AfterInitialLoadListener listener) {
        mLoadListener = listener;
    }

    private void callback(String text) {
        mContents = text.replaceFirst(CALLBACK_SCHEME, "");
        if (mTextChangeListener != null) {
            mTextChangeListener.onTextChange(mContents);
        }
    }

    private void stateCheck(String text) {
        String state = text.replaceFirst(STATE_SCHEME, "").toUpperCase(Locale.ENGLISH);
        List<Type> types = new ArrayList<>();
        for (Type type : Type.values()) {
            if (TextUtils.indexOf(state, type.name()) != -1) {
                types.add(type);
            }
        }

        if (mDecorationStateListener != null) {
            mDecorationStateListener.onStateChangeListener(state, types);
        }
    }

    private void applyAttributes(Context context, AttributeSet attrs) {
        final int[] attrsArray = new int[]{
                android.R.attr.gravity
        };
        TypedArray ta = context.obtainStyledAttributes(attrs, attrsArray);

        int gravity = ta.getInt(0, NO_ID);
        switch (gravity) {
            case Gravity.LEFT:
                exec("javascript:RE.setTextAlign(\"left\")");
                break;
            case Gravity.RIGHT:
                exec("javascript:RE.setTextAlign(\"right\")");
                break;
            case Gravity.TOP:
                exec("javascript:RE.setVerticalAlign(\"top\")");
                break;
            case Gravity.BOTTOM:
                exec("javascript:RE.setVerticalAlign(\"bottom\")");
                break;
            case Gravity.CENTER_VERTICAL:
                exec("javascript:RE.setVerticalAlign(\"middle\")");
                break;
            case Gravity.CENTER_HORIZONTAL:
                exec("javascript:RE.setTextAlign(\"center\")");
                break;
            case Gravity.CENTER:
                exec("javascript:RE.setVerticalAlign(\"middle\")");
                exec("javascript:RE.setTextAlign(\"center\")");
                break;
        }

        ta.recycle();
    }

    public void setHtml(String contents) {
        if (contents == null) {
            contents = "";
        }
        try {
            exec("javascript:RE.setHtml('" + URLEncoder.encode(contents, "UTF-8") + "');");
        } catch (UnsupportedEncodingException e) {
            // No handling
        }
        mContents = contents;
    }

    public String getHtml() {
        return mContents;
    }

    public void setEditorFontColor(int color) {
        String hex = convertHexColorString(color);
        exec("javascript:RE.setBaseTextColor('" + hex + "');");
    }

    public void setEditorFontSize(int px) {
        exec("javascript:RE.setBaseFontSize('" + px + "px');");
    }

    @Override
    public void setPadding(int left, int top, int right, int bottom) {
        super.setPadding(left, top, right, bottom);
        exec("javascript:RE.setPadding('" + left + "px', '" + top + "px', '" + right + "px', '" + bottom
                + "px');");
    }

    @Override
    public void setPaddingRelative(int start, int top, int end, int bottom) {
        // still not support RTL.
        setPadding(start, top, end, bottom);
    }

    public void setEditorBackgroundColor(int color) {
        setBackgroundColor(color);
    }

    @Override
    public void setBackgroundColor(int color) {
        super.setBackgroundColor(color);
    }

    @Override
    public void setBackgroundResource(int resid) {
        Bitmap bitmap = Utils.decodeResource(getContext(), resid);
        String base64 = Utils.toBase64(bitmap);
        bitmap.recycle();

        exec("javascript:RE.setBackgroundImage('url(data:image/png;base64," + base64 + ")');");
    }

    @Override
    public void setBackground(Drawable background) {
        Bitmap bitmap = Utils.toBitmap(background);
        String base64 = Utils.toBase64(bitmap);
        bitmap.recycle();

        exec("javascript:RE.setBackgroundImage('url(data:image/png;base64," + base64 + ")');");
    }

    public void setBackground(String url) {
        exec("javascript:RE.setBackgroundImage('url(" + url + ")');");
    }

    public void setEditorWidth(int px) {
        exec("javascript:RE.setWidth('" + px + "px');");
    }

    public void setEditorHeight(int px) {
        exec("javascript:RE.setHeight('" + px + "px');");
    }

    public void setPlaceholder(String placeholder) {
        exec("javascript:RE.setPlaceholder('" + placeholder + "');");
    }

    public void setInputEnabled(Boolean inputEnabled) {
        exec("javascript:RE.setInputEnabled(" + inputEnabled + ")");
    }

    public void loadCSS(String cssFile) {
        String jsCSSImport = "(function() {" +
                "    var head  = document.getElementsByTagName(\"head\")[0];" +
                "    var link  = document.createElement(\"link\");" +
                "    link.rel  = \"stylesheet\";" +
                "    link.type = \"text/css\";" +
                "    link.href = \"" + cssFile + "\";" +
                "    link.media = \"all\";" +
                "    head.appendChild(link);" +
                "}) ();";
        exec("javascript:" + jsCSSImport + "");
    }

    public void undo() {
        exec("javascript:RE.undo();");
    }

    public void redo() {
        exec("javascript:RE.redo();");
    }

    public void setBold() {
        exec("javascript:RE.setBold();");
    }

    public void setItalic() {
        exec("javascript:RE.setItalic();");
    }

    public void setSubscript() {
        exec("javascript:RE.setSubscript();");
    }

    public void setSuperscript() {
        exec("javascript:RE.setSuperscript();");
    }

    public void setStrikeThrough() {
        exec("javascript:RE.setStrikeThrough();");
    }

    public void setUnderline() {
        exec("javascript:RE.setUnderline();");
    }

    public void setTextColor(int color) {
        exec("javascript:RE.prepareInsert();");

        String hex = convertHexColorString(color);
        exec("javascript:RE.setTextColor('" + hex + "');");
    }

    public void setTextBackgroundColor(int color) {
        exec("javascript:RE.prepareInsert();");

        String hex = convertHexColorString(color);
        exec("javascript:RE.setTextBackgroundColor('" + hex + "');");
    }

    public void setFontSize(int fontSize) {
        if (fontSize > 7 || fontSize < 1) {
            Log.e("RichEditor", "Font size should have a value between 1-7");
        }
        exec("javascript:RE.setFontSize('" + fontSize + "');");
    }

    public void removeFormat() {
        exec("javascript:RE.removeFormat();");
    }

    public void setHeading(int heading) {
        exec("javascript:RE.setHeading('" + heading + "');");
    }

    public void setIndent() {
        exec("javascript:RE.setIndent();");
    }

    public void setOutdent() {
        exec("javascript:RE.setOutdent();");
    }

    public void setAlignLeft() {
        exec("javascript:RE.setJustifyLeft();");
    }

    public void setAlignCenter() {
        exec("javascript:RE.setJustifyCenter();");
    }

    public void setAlignRight() {
        exec("javascript:RE.setJustifyRight();");
    }

    public void setBlockquote() {
        exec("javascript:RE.setBlockquote();");
    }

    public void setBullets() {
        exec("javascript:RE.setBullets();");
    }

    public void setNumbers() {
        exec("javascript:RE.setNumbers();");
    }

    public void insertImage(String url, String alt) {
        exec("javascript:RE.prepareInsert();");
        exec("javascript:RE.insertImage('" + url + "', '" + alt + "');");
    }

    public void insertLink(String href, String title) {
        exec("javascript:RE.prepareInsert();");
        exec("javascript:RE.insertLink('" + href + "', '" + title + "');");
    }

    public void insertTodo() {
        exec("javascript:RE.prepareInsert();");
        exec("javascript:RE.setTodo('" + Utils.getCurrentTime() + "');");
    }

    public void focusEditor() {
        requestFocus();
        exec("javascript:RE.focus();");
    }

    public void clearFocusEditor() {
        exec("javascript:RE.blurFocus();");
    }

    private String convertHexColorString(int color) {
        return String.format("#%06X", (0xFFFFFF & color));
    }

    protected void exec(final String trigger) {
        if (isReady) {
            load(trigger);
        } else {
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    exec(trigger);
                }
            }, 100);
        }
    }

    private void load(String trigger) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            evaluateJavascript(trigger, null);
        } else {
            loadUrl(trigger);
        }
    }

    protected class EditorWebViewClient extends WebViewClient {
        @Override
        public void onPageFinished(WebView view, String url) {
            isReady = url.equalsIgnoreCase(SETUP_HTML);
            if (mLoadListener != null) {
                mLoadListener.onAfterInitialLoad(isReady);
            }
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            String decode;
            try {
                decode = URLDecoder.decode(url, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                // No handling
                return false;
            }

            if (TextUtils.indexOf(url, CALLBACK_SCHEME) == 0) {
                callback(decode);
                return true;
            } else if (TextUtils.indexOf(url, STATE_SCHEME) == 0) {
                stateCheck(decode);
                return true;
            }

            return super.shouldOverrideUrlLoading(view, url);
        }
    }
}


================================================
FILE: app/src/main/res/anim/dialog_enter.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
      <scale   
        android:interpolator="@android:anim/accelerate_interpolator"  
        android:fromXScale="1.0"  
        android:toXScale="1.0"  
        android:fromYScale="0.0"  
        android:toYScale="1.0"  
        android:pivotX="0%"  
        android:pivotY="100%"  
        android:fillAfter="false"  
        android:duration="200"/>
</set>


================================================
FILE: app/src/main/res/anim/dialog_exit.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
      <scale   
        android:interpolator="@android:anim/accelerate_interpolator"  
        android:fromXScale="1.0"  
        android:toXScale="1.0"  
        android:fromYScale="1.0"  
        android:toYScale="0.0"  
        android:pivotX="0%"  
        android:pivotY="100%"  
        android:fillAfter="false"  
        android:duration="200"/>
</set>


================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillColor="#26A69A"
        android:pathData="M0,0h108v108h-108z" />
    <path
        android:fillColor="#00000000"
        android:pathData="M9,0L9,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,0L19,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,0L29,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,0L39,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,0L49,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,0L59,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,0L69,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,0L79,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M89,0L89,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M99,0L99,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,9L108,9"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,19L108,19"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,29L108,29"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,39L108,39"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,49L108,49"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,59L108,59"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,69L108,69"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,79L108,79"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,89L108,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,99L108,99"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,29L89,29"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,39L89,39"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,49L89,49"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,59L89,59"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,69L89,69"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,79L89,79"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,19L29,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,19L39,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,19L49,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,19L59,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,19L69,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,19L79,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
</vector>


================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillType="evenOdd"
        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="78.5885"
                android:endY="90.9159"
                android:startX="48.7653"
                android:startY="61.0927"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0" />
                <item
                    android:color="#00000000"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1" />
</vector>


================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <com.example.zhouqiong.richeditotandroid.view.RichEditor
        android:id="@+id/re_main_editor"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <LinearLayout
        android:id="@+id/ll_main_color"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="#f2f1f6"
        android:visibility="gone">

        <com.example.zhouqiong.richeditotandroid.view.ColorPickerView
            android:id="@+id/cpv_main_color"
            android:layout_width="match_parent"
            android:layout_height="8dp"
            android:layout_gravity="center"
            android:background="#f2f1f6"
            app:indicatorColor="#fff"
            app:indicatorEnable="true"
            app:orientation="horizontal" />
    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:background="#f2f1f6">

        <HorizontalScrollView
            style="@style/EditorIcon"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_toLeftOf="@+id/tv_main_preview"
            android:layout_toStartOf="@+id/tv_main_preview"
            android:scrollbars="none">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/button_image"
                    style="@style/EditorIcon"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@mipmap/photo" />

                <ImageView
                    android:id="@+id/button_bold"
                    style="@style/EditorIcon"
                    android:src="@mipmap/bold" />

                <TextView
                    android:id="@+id/button_text_color"
                    style="@style/EditorIcon"
                    android:layout_width="32dp"
                    android:layout_height="20dp"
                    android:background="@color/colorPrimary" />


                <ImageView
                    android:id="@+id/button_list_ol"
                    style="@style/EditorIcon"
                    android:src="@mipmap/list_ol" />

                <ImageView
                    android:id="@+id/button_list_ul"
                    style="@style/EditorIcon"
                    android:src="@mipmap/list_ul" />

                <ImageView
                    android:id="@+id/button_underline"
                    style="@style/EditorIcon"
                    android:src="@mipmap/underline" />

                <ImageView
                    android:id="@+id/button_italic"
                    style="@style/EditorIcon"
                    android:src="@mipmap/lean" />

                <ImageView
                    android:id="@+id/button_align_left"
                    style="@style/EditorIcon"
                    android:src="@mipmap/align_left" />

                <ImageView
                    android:id="@+id/button_align_center"
                    style="@style/EditorIcon"
                    android:src="@mipmap/align_center" />

                <ImageView
                    android:id="@+id/button_align_right"
                    style="@style/EditorIcon"
                    android:src="@mipmap/align_right" />

                <ImageView
                    android:id="@+id/button_indent"
                    style="@style/EditorIcon"
                    android:src="@mipmap/indent" />

                <ImageView
                    android:id="@+id/button_outdent"
                    style="@style/EditorIcon"
                    android:src="@mipmap/outdent" />

                <ImageView
                    android:id="@+id/action_blockquote"
                    style="@style/EditorIcon"
                    android:src="@mipmap/blockquote" />

                <ImageView
                    android:id="@+id/action_strikethrough"
                    style="@style/EditorIcon"
                    android:src="@mipmap/strikethrough" />

                <ImageView
                    android:id="@+id/action_superscript"
                    style="@style/EditorIcon"
                    android:src="@mipmap/superscript" />

                <ImageView
                    android:id="@+id/action_subscript"
                    style="@style/EditorIcon"
                    android:src="@mipmap/subscript" />
            </LinearLayout>
        </HorizontalScrollView>

        <TextView
            android:id="@+id/tv_main_preview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:paddingLeft="14dp"
            android:paddingRight="14dp"
            android:text="预览"
            android:textColor="#dd3333"
            android:textSize="18sp" />
    </RelativeLayout>

</LinearLayout>

================================================
FILE: app/src/main/res/layout/activity_show_diarys.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <WebView
        android:id="@+id/showdiarys"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</android.support.constraint.ConstraintLayout>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: app/src/main/res/values/attrs.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="ColorPickerView">
        <attr name="indicatorColor" format="color" /><!--指示点颜色-->
        <attr name="indicatorEnable" format="boolean" /><!--是否启用指示点-->
        <attr name="orientation" format="integer">
            <enum name="horizontal" value="0" />
            <enum name="vertical" value="1" />
        </attr>
    </declare-styleable>

</resources>

================================================
FILE: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>


    <color name="button_enabled">#333333</color>
</resources>


================================================
FILE: app/src/main/res/values/strings.xml
================================================
<resources>
    <string name="app_name">RichEditotAndroid</string>


</resources>


================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>


    <style name="EditorIcon">
        <item name="android:layout_width">40dp</item>
        <item name="android:layout_height">20dp</item>
        <item name="android:layout_marginLeft">20dp</item>
        <item name="android:gravity">center</item>
        <item name="android:textColor">@color/button_enabled</item>
    </style>

</resources>


================================================
FILE: app/src/test/java/com/example/zhouqiong/richeditotandroid/ExampleUnitTest.java
================================================
package com.example.zhouqiong.richeditotandroid;

import org.junit.Test;

import static org.junit.Assert.*;

/**
 * Example local unit test, which will execute on the development machine (host).
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
public class ExampleUnitTest {
    @Test
    public void addition_isCorrect() throws Exception {
        assertEquals(4, 2 + 2);
    }
}

================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Wed Mar 21 11:05:34 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip


================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.

# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.

# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true


================================================
FILE: gradlew
================================================
#!/usr/bin/env bash

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn ( ) {
    echo "$*"
}

die ( ) {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
esac

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=$((i+1))
    done
    case $i in
        (0) set -- ;;
        (1) set -- "$args0" ;;
        (2) set -- "$args0" "$args1" ;;
        (3) set -- "$args0" "$args1" "$args2" ;;
        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
    JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"

exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"


================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windowz variants

if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*
goto execute

:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: settings.gradle
================================================
include ':app'
Download .txt
gitextract_1bj4zycb/

├── .gitignore
├── .idea/
│   ├── dictionaries/
│   │   └── zhouqiong.xml
│   ├── gradle.xml
│   ├── misc.xml
│   ├── modules.xml
│   └── runConfigurations.xml
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── example/
│       │               └── zhouqiong/
│       │                   └── richeditotandroid/
│       │                       └── ExampleInstrumentedTest.java
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── assets/
│       │   │   ├── editor.html
│       │   │   ├── normalize.css
│       │   │   ├── rich_editor.js
│       │   │   └── style.css
│       │   ├── java/
│       │   │   └── com/
│       │   │       └── example/
│       │   │           └── zhouqiong/
│       │   │               └── richeditotandroid/
│       │   │                   ├── ui/
│       │   │                   │   ├── MainActivity.java
│       │   │                   │   └── WebDataActivity.java
│       │   │                   ├── utils/
│       │   │                   │   └── Utils.java
│       │   │                   └── view/
│       │   │                       ├── ColorPickerView.java
│       │   │                       └── RichEditor.java
│       │   └── res/
│       │       ├── anim/
│       │       │   ├── dialog_enter.xml
│       │       │   └── dialog_exit.xml
│       │       ├── drawable/
│       │       │   └── ic_launcher_background.xml
│       │       ├── drawable-v24/
│       │       │   └── ic_launcher_foreground.xml
│       │       ├── layout/
│       │       │   ├── activity_main.xml
│       │       │   └── activity_show_diarys.xml
│       │       ├── mipmap-anydpi-v26/
│       │       │   ├── ic_launcher.xml
│       │       │   └── ic_launcher_round.xml
│       │       └── values/
│       │           ├── attrs.xml
│       │           ├── colors.xml
│       │           ├── strings.xml
│       │           └── styles.xml
│       └── test/
│           └── java/
│               └── com/
│                   └── example/
│                       └── zhouqiong/
│                           └── richeditotandroid/
│                               └── ExampleUnitTest.java
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
Download .txt
SYMBOL INDEX (128 symbols across 7 files)

FILE: app/src/androidTest/java/com/example/zhouqiong/richeditotandroid/ExampleInstrumentedTest.java
  class ExampleInstrumentedTest (line 17) | @RunWith(AndroidJUnit4.class)
    method useAppContext (line 19) | @Test

FILE: app/src/main/java/com/example/zhouqiong/richeditotandroid/ui/MainActivity.java
  class MainActivity (line 21) | public class MainActivity extends AppCompatActivity implements View.OnCl...
    method onCreate (line 97) | @Override
    method initView (line 109) | private void initView() {
    method initEditor (line 118) | private void initEditor() {
    method initColorPicker (line 148) | private void initColorPicker() {
    method initMenu (line 172) | private void initMenu() {
    method getViewMeasureHeight (line 197) | private void getViewMeasureHeight() {
    method initClickListener (line 210) | private void initClickListener() {
    method onClick (line 230) | @Override
    method animateOpen (line 383) | private void animateOpen(LinearLayout view) {
    method animateClose (line 400) | private void animateClose(final LinearLayout view) {
    method createDropAnimator (line 422) | private ValueAnimator createDropAnimator(final View view, int start, i...

FILE: app/src/main/java/com/example/zhouqiong/richeditotandroid/ui/WebDataActivity.java
  class WebDataActivity (line 20) | public class WebDataActivity extends AppCompatActivity {
    method onCreate (line 35) | @Override
    method initWebView (line 43) | public void initWebView(String data) {
    class AndroidJavaScript (line 72) | private class AndroidJavaScript {
      method AndroidJavaScript (line 75) | public AndroidJavaScript(Context mContxt) {
      method returnAndroid (line 79) | @JavascriptInterface

FILE: app/src/main/java/com/example/zhouqiong/richeditotandroid/utils/Utils.java
  class Utils (line 17) | public final class Utils {
    method Utils (line 19) | private Utils() throws InstantiationException {
    method toBase64 (line 23) | public static String toBase64(Bitmap bitmap) {
    method toBitmap (line 31) | public static Bitmap toBitmap(Drawable drawable) {
    method decodeResource (line 49) | public static Bitmap decodeResource(Context context, int resId) {
    method getCurrentTime (line 53) | public static long getCurrentTime() {

FILE: app/src/main/java/com/example/zhouqiong/richeditotandroid/view/ColorPickerView.java
  class ColorPickerView (line 37) | public class ColorPickerView extends View {
    type Orientation (line 112) | public enum Orientation {
    method ColorPickerView (line 141) | public ColorPickerView(Context context) {
    method ColorPickerView (line 145) | public ColorPickerView(Context context, @Nullable AttributeSet attrs) {
    method ColorPickerView (line 149) | public ColorPickerView(Context context, @Nullable AttributeSet attrs, ...
    method onMeasure (line 164) | @Override
    method onLayout (line 191) | @Override
    method createBitmap (line 218) | private void createBitmap() {
    method calculBounds (line 247) | private void calculBounds() {
    method setColors (line 309) | public void setColors(int... colors) {
    method createDefaultColorTable (line 335) | public int[] createDefaultColorTable() {
    method onDraw (line 359) | @Override
    method createIndicatorBitmap (line 379) | private void createIndicatorBitmap() {
    method createColorTableBitmap (line 391) | private void createColorTableBitmap() {
    method onTouchEvent (line 414) | @Override
    method getColor (line 462) | public int getColor() {
    method inBoundOfColorTable (line 466) | private boolean inBoundOfColorTable(int ex, int ey) {
    method calcuColor (line 479) | private int calcuColor() {
    method pixelToColor (line 505) | private int pixelToColor(int pixel) {
    method setOnColorPickerChangeListener (line 517) | public void setOnColorPickerChangeListener(OnColorPickerChangeListener...
    type OnColorPickerChangeListener (line 521) | public interface OnColorPickerChangeListener {
      method onColorChanged (line 529) | void onColorChanged(ColorPickerView picker, int color);
      method onStartTrackingTouch (line 536) | void onStartTrackingTouch(ColorPickerView picker);
      method onStopTrackingTouch (line 543) | void onStopTrackingTouch(ColorPickerView picker);
    method onSaveInstanceState (line 547) | @Override
    method onRestoreInstanceState (line 560) | @Override
    class SavedState (line 582) | private class SavedState extends BaseSavedState {
      method SavedState (line 588) | SavedState(Parcelable source) {
      method writeToParcel (line 592) | @Override
    method setPosition (line 605) | public void setPosition(int x, int y) {
    method showDefaultColorTable (line 620) | public void showDefaultColorTable() {
    method getIndicatorColor (line 624) | public int getIndicatorColor() {
    method setIndicatorColor (line 628) | public void setIndicatorColor(int color) {
    method setOrientation (line 634) | public void setOrientation(Orientation orientation) {

FILE: app/src/main/java/com/example/zhouqiong/richeditotandroid/view/RichEditor.java
  class RichEditor (line 32) | public class RichEditor extends WebView {
    type Type (line 34) | public enum Type {
    type OnTextChangeListener (line 55) | public interface OnTextChangeListener {
      method onTextChange (line 57) | void onTextChange(String text);
    type OnDecorationStateListener (line 60) | public interface OnDecorationStateListener {
      method onStateChangeListener (line 62) | void onStateChangeListener(String text, List<Type> types);
    type AfterInitialLoadListener (line 65) | public interface AfterInitialLoadListener {
      method onAfterInitialLoad (line 67) | void onAfterInitialLoad(boolean isReady);
    method RichEditor (line 79) | public RichEditor(Context context) {
    method RichEditor (line 83) | public RichEditor(Context context, AttributeSet attrs) {
    method RichEditor (line 87) | @SuppressLint("SetJavaScriptEnabled")
    method createWebviewClient (line 101) | protected EditorWebViewClient createWebviewClient() {
    method setOnTextChangeListener (line 105) | public void setOnTextChangeListener(OnTextChangeListener listener) {
    method setOnDecorationChangeListener (line 109) | public void setOnDecorationChangeListener(OnDecorationStateListener li...
    method setOnInitialLoadListener (line 113) | public void setOnInitialLoadListener(AfterInitialLoadListener listener) {
    method callback (line 117) | private void callback(String text) {
    method stateCheck (line 124) | private void stateCheck(String text) {
    method applyAttributes (line 138) | private void applyAttributes(Context context, AttributeSet attrs) {
    method setHtml (line 173) | public void setHtml(String contents) {
    method getHtml (line 185) | public String getHtml() {
    method setEditorFontColor (line 189) | public void setEditorFontColor(int color) {
    method setEditorFontSize (line 194) | public void setEditorFontSize(int px) {
    method setPadding (line 198) | @Override
    method setPaddingRelative (line 205) | @Override
    method setEditorBackgroundColor (line 211) | public void setEditorBackgroundColor(int color) {
    method setBackgroundColor (line 215) | @Override
    method setBackgroundResource (line 220) | @Override
    method setBackground (line 229) | @Override
    method setBackground (line 238) | public void setBackground(String url) {
    method setEditorWidth (line 242) | public void setEditorWidth(int px) {
    method setEditorHeight (line 246) | public void setEditorHeight(int px) {
    method setPlaceholder (line 250) | public void setPlaceholder(String placeholder) {
    method setInputEnabled (line 254) | public void setInputEnabled(Boolean inputEnabled) {
    method loadCSS (line 258) | public void loadCSS(String cssFile) {
    method undo (line 271) | public void undo() {
    method redo (line 275) | public void redo() {
    method setBold (line 279) | public void setBold() {
    method setItalic (line 283) | public void setItalic() {
    method setSubscript (line 287) | public void setSubscript() {
    method setSuperscript (line 291) | public void setSuperscript() {
    method setStrikeThrough (line 295) | public void setStrikeThrough() {
    method setUnderline (line 299) | public void setUnderline() {
    method setTextColor (line 303) | public void setTextColor(int color) {
    method setTextBackgroundColor (line 310) | public void setTextBackgroundColor(int color) {
    method setFontSize (line 317) | public void setFontSize(int fontSize) {
    method removeFormat (line 324) | public void removeFormat() {
    method setHeading (line 328) | public void setHeading(int heading) {
    method setIndent (line 332) | public void setIndent() {
    method setOutdent (line 336) | public void setOutdent() {
    method setAlignLeft (line 340) | public void setAlignLeft() {
    method setAlignCenter (line 344) | public void setAlignCenter() {
    method setAlignRight (line 348) | public void setAlignRight() {
    method setBlockquote (line 352) | public void setBlockquote() {
    method setBullets (line 356) | public void setBullets() {
    method setNumbers (line 360) | public void setNumbers() {
    method insertImage (line 364) | public void insertImage(String url, String alt) {
    method insertLink (line 369) | public void insertLink(String href, String title) {
    method insertTodo (line 374) | public void insertTodo() {
    method focusEditor (line 379) | public void focusEditor() {
    method clearFocusEditor (line 384) | public void clearFocusEditor() {
    method convertHexColorString (line 388) | private String convertHexColorString(int color) {
    method exec (line 392) | protected void exec(final String trigger) {
    method load (line 405) | private void load(String trigger) {
    class EditorWebViewClient (line 413) | protected class EditorWebViewClient extends WebViewClient {
      method onPageFinished (line 414) | @Override
      method shouldOverrideUrlLoading (line 422) | @Override

FILE: app/src/test/java/com/example/zhouqiong/richeditotandroid/ExampleUnitTest.java
  class ExampleUnitTest (line 12) | public class ExampleUnitTest {
    method addition_isCorrect (line 13) | @Test
Condensed preview — 41 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (114K chars).
[
  {
    "path": ".gitignore",
    "chars": 118,
    "preview": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n"
  },
  {
    "path": ".idea/dictionaries/zhouqiong.xml",
    "chars": 88,
    "preview": "<component name=\"ProjectDictionaryState\">\n  <dictionary name=\"zhouqiong\" />\n</component>"
  },
  {
    "path": ".idea/gradle.xml",
    "chars": 626,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linke"
  },
  {
    "path": ".idea/misc.xml",
    "chars": 2991,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"NullableNotNullManager\">\n    <option nam"
  },
  {
    "path": ".idea/modules.xml",
    "chars": 371,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n   "
  },
  {
    "path": ".idea/runConfigurations.xml",
    "chars": 564,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <o"
  },
  {
    "path": "README.md",
    "chars": 982,
    "preview": "# RichEditotAndroid\nWebView + JavaScript 方式的实现一个android富文本编辑器\n>最近产品提出在移动端实现富文本编辑器功能,需要能编辑文字加粗、颜色、插入图片、下划线等一系列功能等,最后要生成一个"
  },
  {
    "path": "app/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "chars": 944,
    "preview": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 26\n    defaultConfig {\n        applicationId \"c"
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "app/src/androidTest/java/com/example/zhouqiong/richeditotandroid/ExampleInstrumentedTest.java",
    "chars": 779,
    "preview": "package com.example.zhouqiong.richeditotandroid;\n\nimport android.content.Context;\nimport android.support.test.Instrument"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 912,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "app/src/main/assets/editor.html",
    "chars": 422,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n    <meta name=\"viewport\" content=\"user-scalable=no\">\n    <meta http-equiv=\"Content-Type\" "
  },
  {
    "path": "app/src/main/assets/normalize.css",
    "chars": 7514,
    "preview": "/*! normalize.css v3.0.2 | MIT License | git.io/normalize */\n\n/**\n * 1. Set default font family to sans-serif.\n * 2. Pre"
  },
  {
    "path": "app/src/main/assets/rich_editor.js",
    "chars": 8284,
    "preview": "/**\n * Copyright (C) 2017 Wasabeef\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "app/src/main/assets/style.css",
    "chars": 1000,
    "preview": "/**\n * Copyright (C) 2017 Wasabeef\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "app/src/main/java/com/example/zhouqiong/richeditotandroid/ui/MainActivity.java",
    "chars": 14683,
    "preview": "package com.example.zhouqiong.richeditotandroid.ui;\n\nimport android.animation.Animator;\nimport android.animation.Animato"
  },
  {
    "path": "app/src/main/java/com/example/zhouqiong/richeditotandroid/ui/WebDataActivity.java",
    "chars": 4080,
    "preview": "package com.example.zhouqiong.richeditotandroid.ui;\n\nimport android.content.Context;\nimport android.content.Intent;\nimpo"
  },
  {
    "path": "app/src/main/java/com/example/zhouqiong/richeditotandroid/utils/Utils.java",
    "chars": 1725,
    "preview": "package com.example.zhouqiong.richeditotandroid.utils;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\n"
  },
  {
    "path": "app/src/main/java/com/example/zhouqiong/richeditotandroid/view/ColorPickerView.java",
    "chars": 17263,
    "preview": "package com.example.zhouqiong.richeditotandroid.view;\n\nimport android.content.Context;\nimport android.content.res.TypedA"
  },
  {
    "path": "app/src/main/java/com/example/zhouqiong/richeditotandroid/view/RichEditor.java",
    "chars": 12836,
    "preview": "package com.example.zhouqiong.richeditotandroid.view;\n\nimport android.annotation.SuppressLint;\nimport android.content.Co"
  },
  {
    "path": "app/src/main/res/anim/dialog_enter.xml",
    "chars": 465,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n      <scale   \n"
  },
  {
    "path": "app/src/main/res/anim/dialog_exit.xml",
    "chars": 465,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n      <scale   \n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 5606,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:wi"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "chars": 1880,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    "
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "chars": 5590,
    "preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/r"
  },
  {
    "path": "app/src/main/res/layout/activity_show_diarys.xml",
    "chars": 414,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.constraint.ConstraintLayout xmlns:android=\"http://schemas.androi"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 272,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "chars": 272,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "app/src/main/res/values/attrs.xml",
    "chars": 437,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <declare-styleable name=\"ColorPickerView\">\n        <attr name=\"i"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 259,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"color"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 82,
    "preview": "<resources>\n    <string name=\"app_name\">RichEditotAndroid</string>\n\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "chars": 715,
    "preview": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
  },
  {
    "path": "app/src/test/java/com/example/zhouqiong/richeditotandroid/ExampleUnitTest.java",
    "chars": 417,
    "preview": "package com.example.zhouqiong.richeditotandroid;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Exam"
  },
  {
    "path": "build.gradle",
    "chars": 546,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    \n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 230,
    "preview": "#Wed Mar 21 11:05:34 CST 2018\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "gradle.properties",
    "chars": 730,
    "preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
  },
  {
    "path": "gradlew",
    "chars": 4971,
    "preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start "
  },
  {
    "path": "gradlew.bat",
    "chars": 2314,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
  },
  {
    "path": "settings.gradle",
    "chars": 15,
    "preview": "include ':app'\n"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the ZQ330093887/RichEditotAndroid GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 41 files (100.2 KB), approximately 27.3k tokens, and a symbol index with 128 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!