├── .gitignore
├── .idea
├── caches
│ ├── build_file_checksums.ser
│ └── gradle_models.ser
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── annotation
├── .gitignore
├── build.gradle
└── src
│ └── main
│ └── java
│ └── com
│ └── jscheng
│ └── annotations
│ └── Route.java
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── jscheng
│ │ └── srich
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── jscheng
│ │ │ └── srich
│ │ │ ├── BaseActivity.java
│ │ │ ├── BaseApplication.java
│ │ │ ├── MainActivity.java
│ │ │ ├── NoteService.java
│ │ │ ├── NoteServicePool.java
│ │ │ ├── converter
│ │ │ ├── StyleCode.java
│ │ │ ├── decoder
│ │ │ │ ├── ParagraphDecoder.java
│ │ │ │ └── WordStyleNode.java
│ │ │ └── encoder
│ │ │ │ └── ParagraphEncoder.java
│ │ │ ├── dao
│ │ │ ├── NoteDao.java
│ │ │ └── NoteDboOpener.java
│ │ │ ├── editor
│ │ │ ├── INoteEditorManager.java
│ │ │ ├── NoteEditorClickListener.java
│ │ │ ├── NoteEditorConfig.java
│ │ │ ├── NoteEditorInputConnection.java
│ │ │ ├── NoteEditorManager.java
│ │ │ ├── NoteEditorManagerImpl.java
│ │ │ ├── NoteEditorRender.java
│ │ │ ├── NoteEditorSelectionListener.java
│ │ │ ├── NoteEditorText.java
│ │ │ ├── render
│ │ │ │ ├── NoteLineSpanRender.java
│ │ │ │ ├── NoteWordSpanRender.java
│ │ │ │ ├── line_render
│ │ │ │ │ ├── NoteBulletLineSpanRender.java
│ │ │ │ │ ├── NoteCheckBoxSpanRender.java
│ │ │ │ │ ├── NoteDividingSpanRender.java
│ │ │ │ │ ├── NoteImageSpanRender.java
│ │ │ │ │ ├── NoteIndentationSpanRender.java
│ │ │ │ │ ├── NoteNumSpanRender.java
│ │ │ │ │ └── NoteUncheckBoxSpanRender.java
│ │ │ │ └── word_render
│ │ │ │ │ ├── NoteBackgroundSpanRender.java
│ │ │ │ │ ├── NoteBoldSpanRender.java
│ │ │ │ │ ├── NoteItalicSpanRender.java
│ │ │ │ │ ├── NoteStrikethroughSpanRender.java
│ │ │ │ │ ├── NoteSubscriptSpanRender.java
│ │ │ │ │ ├── NoteSuperscriptSpanRender.java
│ │ │ │ │ └── NoteUnderlineSpanRender.java
│ │ │ └── spans
│ │ │ │ ├── NoteBackgroundSpan.java
│ │ │ │ ├── NoteBoldSpan.java
│ │ │ │ ├── NoteBulletSpan.java
│ │ │ │ ├── NoteCheckBoxSpan.java
│ │ │ │ ├── NoteClickSpan.java
│ │ │ │ ├── NoteDividingLineSpan.java
│ │ │ │ ├── NoteImageSpan.java
│ │ │ │ ├── NoteIndentationSpan.java
│ │ │ │ ├── NoteItalicSpan.java
│ │ │ │ ├── NoteNumSpan.java
│ │ │ │ ├── NoteStrikethroughSpan.java
│ │ │ │ ├── NoteSubscriptSpan.java
│ │ │ │ ├── NoteSuperscriptSpan.java
│ │ │ │ ├── NoteTypefaceSpan.java
│ │ │ │ ├── NoteUnderLineSpan.java
│ │ │ │ └── NoteUriSpan.java
│ │ │ ├── image_loader
│ │ │ ├── DownSampler.java
│ │ │ ├── FileImageJob.java
│ │ │ ├── HttpImageJob.java
│ │ │ ├── ImageDiskCache.java
│ │ │ ├── ImageFetcher.java
│ │ │ ├── ImageFetcherCallback.java
│ │ │ ├── ImageGlobalListener.java
│ │ │ ├── ImageJob.java
│ │ │ ├── ImageJobCallback.java
│ │ │ ├── ImageJobFactory.java
│ │ │ ├── ImageKeyFactory.java
│ │ │ ├── ImageLoader.java
│ │ │ ├── ImageMemoryCache.java
│ │ │ ├── ImageTarget.java
│ │ │ └── ImageViewTarget.java
│ │ │ ├── image_preview
│ │ │ └── ImagePreviewActivity.java
│ │ │ ├── model
│ │ │ ├── Note.java
│ │ │ ├── NoteBuilder.java
│ │ │ ├── NoteModel.java
│ │ │ ├── NoteSnap.java
│ │ │ ├── NoteSnapBuilder.java
│ │ │ ├── Options.java
│ │ │ ├── OutLine.java
│ │ │ ├── Paragraph.java
│ │ │ ├── ParagraphBuilder.java
│ │ │ └── Style.java
│ │ │ ├── mvp
│ │ │ ├── IModel.java
│ │ │ ├── IPresenter.java
│ │ │ └── IView.java
│ │ │ ├── note_edit
│ │ │ ├── EditNoteActivity.java
│ │ │ ├── EditNoteFormatDialog.java
│ │ │ ├── EditNoteInputDialog.java
│ │ │ ├── EditNoteMode.java
│ │ │ ├── EditNotePresenter.java
│ │ │ ├── EditNoteToolbar.java
│ │ │ ├── FloatEditButton.java
│ │ │ └── NoteEditorBar.java
│ │ │ ├── outline
│ │ │ ├── FloatNewButton.java
│ │ │ ├── OutLineCenterDialog.java
│ │ │ ├── OutLineModel.java
│ │ │ ├── OutLinePresenter.java
│ │ │ ├── OutLinesActivity.java
│ │ │ └── OutLinesAdapter.java
│ │ │ ├── revoke
│ │ │ └── NoteRevocationManager.java
│ │ │ ├── route
│ │ │ ├── ActivityInterceptor.java
│ │ │ ├── IRouteInfo.java
│ │ │ ├── LogInterceptor.java
│ │ │ ├── RouteInfoUtil.java
│ │ │ ├── Router.java
│ │ │ ├── RouterChain.java
│ │ │ ├── RouterInterceptor.java
│ │ │ ├── RouterRequest.java
│ │ │ ├── RouterResponse.java
│ │ │ └── UriAnalyzerInterceptor.java
│ │ │ ├── utils
│ │ │ ├── ClipboardUtil.java
│ │ │ ├── DateUtil.java
│ │ │ ├── DisplayUtil.java
│ │ │ ├── FontUtil.java
│ │ │ ├── KeyboardUtil.java
│ │ │ ├── MdUtil.java
│ │ │ ├── OsUtil.java
│ │ │ ├── PermissionUtil.java
│ │ │ ├── StorageUtil.java
│ │ │ ├── UriPathUtil.java
│ │ │ └── VersionUtil.java
│ │ │ └── widget
│ │ │ ├── CircularProgressView.java
│ │ │ └── PinchImageView.java
│ └── res
│ │ ├── anim
│ │ ├── edit_note_bottom_in_anim.xml
│ │ └── edit_note_bottom_out_anim.xml
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── editor_bar_format_select.xml
│ │ ├── editor_bar_redo_select.xml
│ │ ├── editor_bar_undo_select.xml
│ │ ├── editor_note_bottom_dialog_bg.xml
│ │ ├── editor_note_dialog_edit.xml
│ │ ├── editor_note_dialog_input.xml
│ │ ├── editor_note_dialog_text_left.xml
│ │ ├── editor_note_dialog_text_right.xml
│ │ ├── editor_view_select.xml
│ │ ├── ic_launcher_background.xml
│ │ └── outline_toolbar_line.xml
│ │ ├── layout
│ │ ├── activity_editnote.xml
│ │ ├── activity_image_preview.xml
│ │ ├── activity_main.xml
│ │ ├── activity_outline.xml
│ │ ├── edit_note_bottom_dialog.xml
│ │ ├── edit_note_editor_bar.xml
│ │ ├── edit_note_input_dialog.xml
│ │ ├── edit_note_toolbar.xml
│ │ ├── outline_center_dialog.xml
│ │ ├── outline_item_view_date.xml
│ │ ├── outline_item_view_note.xml
│ │ ├── outline_toolbar.xml
│ │ └── outline_toolbar2.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_compose.png
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ ├── ic_note_edit_attach.png
│ │ ├── ic_note_edit_back.png
│ │ ├── ic_note_edit_backward.png
│ │ ├── ic_note_edit_check.png
│ │ ├── ic_note_edit_edit.png
│ │ ├── ic_note_edit_error.png
│ │ ├── ic_note_edit_format_off.png
│ │ ├── ic_note_edit_forward.png
│ │ ├── ic_note_edit_loading.png
│ │ ├── ic_note_edit_more.png
│ │ ├── ic_note_edit_redo.png
│ │ ├── ic_note_edit_redo_disabled.png
│ │ ├── ic_note_edit_tick.png
│ │ ├── ic_note_edit_uncheck.png
│ │ ├── ic_note_edit_undo.png
│ │ └── ic_note_edit_undo_disabled.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── ic_note_edit_format_on.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── jscheng
│ └── srich
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── processor
├── .gitignore
├── build.gradle
└── src
│ └── main
│ └── java
│ └── com
│ └── jscheng
│ └── processor
│ ├── AnnotatedClass.java
│ ├── RouteClass.java
│ └── RouterProcessor.java
├── readme.md
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/libraries
5 | /.idea/modules.xml
6 | /.idea/workspace.xml
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 |
--------------------------------------------------------------------------------
/.idea/caches/build_file_checksums.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/.idea/caches/build_file_checksums.ser
--------------------------------------------------------------------------------
/.idea/caches/gradle_models.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/.idea/caches/gradle_models.ser
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/annotation/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/annotation/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java-library'
2 |
3 | dependencies {
4 | implementation fileTree(dir: 'libs', include: ['*.jar'])
5 | }
6 |
7 | sourceCompatibility = "1.8"
8 | targetCompatibility = "1.8"
9 |
--------------------------------------------------------------------------------
/annotation/src/main/java/com/jscheng/annotations/Route.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.annotations;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Created By Chengjunsen on 2019/3/18
10 | */
11 | @Target(ElementType.TYPE)
12 | @Retention(RetentionPolicy.CLASS)
13 | public @interface Route {
14 | String value() default "";
15 | }
16 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.jscheng.srich"
7 | minSdkVersion 23
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | javaCompileOptions{
13 | annotationProcessorOptions.includeCompileClasspath = true
14 | }
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | implementation fileTree(dir: 'libs', include: ['*.jar'])
26 | implementation 'com.android.support:appcompat-v7:28.0.0'
27 | implementation 'com.android.support:recyclerview-v7:28.0.0'
28 | implementation 'com.android.support:design:28.0.0'
29 | implementation 'com.android.support.constraint:constraint-layout:1.0.2'
30 | testImplementation 'junit:junit:4.12'
31 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
32 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
33 | implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
34 | implementation 'com.squareup.okio:okio:2.1.0'
35 | implementation 'com.squareup.okhttp3:okhttp:3.12.0'
36 | annotationProcessor "android.arch.lifecycle:compiler:1.1.1"
37 | implementation 'com.jakewharton:disklrucache:2.0.2'
38 | implementation "io.reactivex.rxjava2:rxjava:2.2.7"
39 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
40 | implementation 'com.android.support:exifinterface:28.0.0'
41 | implementation project(':annotation')
42 | annotationProcessor project(':processor')
43 | }
44 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/jscheng/srich/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.jscheng.srich", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/2/20
7 | */
8 | public abstract class BaseActivity extends AppCompatActivity {
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/BaseApplication.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich;
2 |
3 | import android.app.Application;
4 |
5 | import com.jscheng.srich.route.Router;
6 |
7 | public class BaseApplication extends Application {
8 |
9 | @Override
10 | public void onCreate() {
11 | super.onCreate();
12 | Router.init(this);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich;
2 |
3 | import android.os.Bundle;
4 |
5 | import com.jscheng.annotations.Route;
6 |
7 | @Route("main")
8 | public class MainActivity extends BaseActivity {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | setContentView(R.layout.activity_main);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/NoteService.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.app.Service;
6 | import android.content.Intent;
7 | import android.os.Bundle;
8 | import android.os.IBinder;
9 | import android.support.annotation.Nullable;
10 |
11 | /**
12 | * Created By Chengjunsen on 2019/3/7
13 | */
14 | public class NoteService extends Service implements Application.ActivityLifecycleCallbacks{
15 |
16 | @Override
17 | public void onCreate() {
18 | super.onCreate();
19 | getApplication().registerActivityLifecycleCallbacks(this);
20 | }
21 |
22 | @Override
23 | public int onStartCommand(Intent intent, int flags, int startId) {
24 | return Service.START_STICKY;
25 | }
26 |
27 | @Nullable
28 | @Override
29 | public IBinder onBind(Intent intent) {
30 | return null;
31 | }
32 |
33 | @Override
34 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
35 |
36 | }
37 |
38 | @Override
39 | public void onActivityStarted(Activity activity) {
40 |
41 | }
42 |
43 | @Override
44 | public void onActivityResumed(Activity activity) {
45 |
46 | }
47 |
48 | @Override
49 | public void onActivityPaused(Activity activity) {
50 |
51 | }
52 |
53 | @Override
54 | public void onActivityStopped(Activity activity) {
55 |
56 | }
57 |
58 | @Override
59 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
60 |
61 | }
62 |
63 | @Override
64 | public void onActivityDestroyed(Activity activity) {
65 |
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/NoteServicePool.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import com.jscheng.srich.model.Note;
6 |
7 | import java.util.concurrent.ArrayBlockingQueue;
8 | import java.util.concurrent.ThreadFactory;
9 | import java.util.concurrent.ThreadPoolExecutor;
10 | import java.util.concurrent.TimeUnit;
11 |
12 | /**
13 | * Created By Chengjunsen on 2019/3/7
14 | */
15 | public class NoteServicePool {
16 |
17 | private ThreadPoolExecutor mExecutors;
18 | private int corePoolSize = 2;
19 | private int maximumPoolSize = 5;
20 | private int keepLive = 10;
21 |
22 | public static NoteServicePool mInstance;
23 |
24 | public static NoteServicePool getInstance() {
25 | if (mInstance == null) {
26 | synchronized (NoteServicePool.class) {
27 | if (mInstance == null) {
28 | mInstance = new NoteServicePool();
29 | }
30 | }
31 | }
32 | return mInstance;
33 | }
34 |
35 | public NoteServicePool() {
36 | mExecutors = new ThreadPoolExecutor(corePoolSize,
37 | maximumPoolSize,
38 | keepLive,
39 | TimeUnit.SECONDS,
40 | new ArrayBlockingQueue(50), new ThreadFactory() {
41 | @Override
42 | public Thread newThread(@NonNull Runnable r) {
43 | Thread thread = new Thread(r);
44 | thread.setName("NoteService " + thread.getId());
45 | return thread;
46 | }
47 | });
48 | }
49 |
50 | public void updateNote(Note mNote) {
51 | mExecutors.execute(new Runnable() {
52 | @Override
53 | public void run() {
54 |
55 | }
56 | });
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/converter/StyleCode.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.converter;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/3/11
5 | */
6 | public class StyleCode {
7 | public static final String BoldBegin = "";
8 | public static final String BoldEnd = "";
9 |
10 | public static final String ItalicBegin = "";
11 | public static final String ItalicEnd = "";
12 |
13 | public static final String UnderLineBegin = "";
14 | public static final String UnderLineEnd = "";
15 |
16 | public static final String StrikethroughBegin = "";
17 | public static final String StrikethroughEnd = "";
18 |
19 | public static final String BackgroudColorBegin = "";
20 | public static final String BackgroudColorEnd = "";
21 |
22 | public static final String SuperScriptBegin = "";
23 | public static final String SuperScriptEnd = "";
24 |
25 | public static final String SubScriptBegin = "";
26 | public static final String SubScriptEnd = "";
27 |
28 | public static final String ImageBegin = "
";
29 | public static final String ImageEnd = "";
30 |
31 | public static final String CheckBox = "";
32 |
33 | public static final String UnCheckBox = "";
34 |
35 | public static final String NumList = "";
36 |
37 | public static final String Bullet = "";
38 |
39 | public static final String DividingLine = "
";
40 |
41 | public static final String Indentation = "";
42 |
43 | public static final String Paragraph = "
";
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/converter/decoder/WordStyleNode.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.converter.decoder;
2 |
3 | import android.text.TextUtils;
4 |
5 | import com.jscheng.srich.model.Style;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * Created By Chengjunsen on 2019/3/11
12 | */
13 | public class WordStyleNode {
14 | private int style;
15 | private String content;
16 |
17 | public WordStyleNode(int lineStyle, String content) {
18 | this.style = lineStyle;
19 | this.content = content;
20 | }
21 |
22 | public List splitStyle(List nodes, String beginStyleCode, String endStyleCode, int flag) {
23 | List list = new ArrayList<>();
24 | for (WordStyleNode node: nodes) {
25 | list.addAll(node.splitStyle(beginStyleCode, endStyleCode, flag));
26 | }
27 | return list;
28 | }
29 |
30 | public List splitStyle(String beginStyleCode, String endStyleCode, int flag) {
31 | List childs = new ArrayList<>();
32 | String[] temp = content.split(beginStyleCode + "|" + endStyleCode);
33 | for (int i = 0; i< temp.length; i++) {
34 | if (!TextUtils.isEmpty(temp[i])) {
35 | style = Style.setWordStyle(style, (i % 2 != 0), flag);
36 | childs.add(new WordStyleNode(style, temp[i]));
37 | }
38 | }
39 | return childs;
40 | }
41 |
42 | public int getStyle() {
43 | return style;
44 | }
45 |
46 | public String getContent() {
47 | return content;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/dao/NoteDboOpener.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.dao;
2 |
3 | import android.content.Context;
4 | import android.database.sqlite.SQLiteDatabase;
5 | import android.database.sqlite.SQLiteOpenHelper;
6 |
7 | public class NoteDboOpener extends SQLiteOpenHelper {
8 | public static final String DataBaseName = "srich.db";
9 | public static final int DataBaseVersion = 1;
10 | public static final String NoteTable = "note";
11 |
12 | public static String DataCreateTableSql = "create table " + NoteTable + " (" +
13 | "id varchar(200) primary key not null, " +
14 | "title varchar(200) , " +
15 | "createTime bigint , " +
16 | "modifyTime bigint ," +
17 | "summary varchar(200) , " +
18 | "summaryImageUrl varchar(200) , " +
19 | "localPath varchar(200)" +
20 | ");";
21 |
22 | public NoteDboOpener(Context context) {
23 | super(context, DataBaseName, null, DataBaseVersion);
24 | }
25 |
26 | @Override
27 | public void onCreate(SQLiteDatabase db) {
28 | db.execSQL(DataCreateTableSql);
29 | }
30 |
31 | @Override
32 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/INoteEditorManager.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor;
2 |
3 | import android.net.Uri;
4 |
5 | import com.jscheng.srich.model.Note;
6 | import com.jscheng.srich.model.NoteSnap;
7 |
8 | /**
9 | * Created By Chengjunsen on 2019/3/11
10 | */
11 | public interface INoteEditorManager {
12 |
13 | void commandImage(Uri uri, boolean draw);
14 |
15 | void commandImage(String url, boolean draw);
16 |
17 | void commandColor(boolean isSelected, boolean draw);
18 |
19 | void commandUnderline(boolean isSelected, boolean draw);
20 |
21 | void commandItalic(boolean isSelected, boolean draw);
22 |
23 | void commandBold(boolean isSelected, boolean draw);
24 |
25 | void commandSuperscript(boolean isSelected, boolean draw);
26 |
27 | void commandSubscript(boolean isSelected, boolean draw);
28 |
29 | void commandStrikeThrough(boolean isSelected, boolean draw);
30 |
31 | void commandDividingLine(boolean draw);
32 |
33 | void commandBulletList(boolean isSelected, boolean draw);
34 |
35 | void commandNumList(boolean isSelected, boolean draw);
36 |
37 | void commandCheckBox(boolean isSelected, boolean draw);
38 |
39 | void commandIndentation(boolean draw);
40 |
41 | void commandReduceIndentation(boolean draw);
42 |
43 | void commandDeleteSelection(boolean draw);
44 |
45 | void commandDelete(boolean draw);
46 |
47 | void commandDelete(int num, boolean draw);
48 |
49 | void commandPaste(String content, boolean draw);
50 |
51 | void commandEnter(boolean draw);
52 |
53 | void commandInput(CharSequence content, boolean draw);
54 |
55 | NoteSnap commandRetroke();
56 |
57 | NoteSnap commandRecover();
58 |
59 | void addSelectionChangeListener(NoteEditorSelectionListener listener);
60 |
61 | void removeSelectionChangeListener(NoteEditorSelectionListener listener);
62 |
63 | void addClickListener(NoteEditorClickListener listener);
64 |
65 | void requestDraw();
66 |
67 | void apply(Note mNote, int selectionStart, int selectionEnd);
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/NoteEditorClickListener.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/3/11
7 | */
8 | public interface NoteEditorClickListener {
9 | void onClickImage(List urls, int index);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/NoteEditorConfig.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/2/26
5 | */
6 | public class NoteEditorConfig {
7 |
8 | public static final int HighLightColor = 0xFFCBE5EF;
9 |
10 | public static final int HighLightBackgroundColor = 0xf5f59555;
11 |
12 | public static final int CursorColor = 0xFF000000;
13 |
14 | public static final int HandleColor = 0xFF01A832;
15 |
16 | public static final int TextSizeSp = 16;
17 |
18 | public static final float LetterSpacing = 0.01f;
19 |
20 | public static final int LineSpacing = 15;
21 |
22 | public static final String EndCode = "\n";
23 |
24 | public static final char EndCodeChar = '\n';
25 |
26 | public static final String PlaceHoldChar = "\u200B";
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/NoteEditorInputConnection.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor;
2 |
3 | import android.util.Log;
4 | import android.view.KeyEvent;
5 | import android.view.inputmethod.InputConnectionWrapper;
6 |
7 | /**
8 | * Created By Chengjunsen on 2019/3/1
9 | */
10 | public class NoteEditorInputConnection extends InputConnectionWrapper {
11 | private static final String TAG = "InputConnection";
12 | private INoteEditorManager mStyleManager;
13 |
14 | public NoteEditorInputConnection(INoteEditorManager mStyleManager) {
15 | super(null, true);
16 | this.mStyleManager = mStyleManager;
17 | }
18 |
19 | @Override
20 | public boolean commitText(CharSequence text, int newCursorPosition) {
21 | Log.d(TAG, "commitText: " + newCursorPosition + " count: " + text.length() + " -> " + text);
22 | if (text.length() == 0) {
23 | mStyleManager.commandDelete(true);
24 | } else {
25 | mStyleManager.commandInput(text, true);
26 | }
27 | return true;
28 | }
29 |
30 | /**
31 | * 当软键盘删除文本之前,会调用这个方法通知输入框,我们可以重写这个方法并判断是否要拦截这个删除事件。
32 | * 在谷歌输入法上,点击退格键的时候不会调用{@link #sendKeyEvent(KeyEvent event)},
33 | * 而是直接回调这个方法,所以也要在这个方法上做拦截;
34 | **/
35 | @Override
36 | public boolean deleteSurroundingText(int beforeLength, int afterLength) {
37 | mStyleManager.commandDelete(beforeLength - afterLength, true);
38 | //super.deleteSurroundingText(beforeLength, afterLength);
39 | return true;
40 | }
41 |
42 | @Override
43 | public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
44 | Log.e(TAG, "deleteSurroundingTextInCodePoints: " + (afterLength - beforeLength));
45 | return super.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
46 | }
47 |
48 | /**
49 | * 当在软件盘上点击某些按钮(比如退格键,数字键,回车键等),该方法可能会被触发(取决于输入法的开发者),
50 | * 所以也可以重写该方法并拦截这些事件,这些事件就不会被分发到输入框了
51 | **/
52 | @Override
53 | public boolean sendKeyEvent(KeyEvent event) {
54 | if (event.getAction() == KeyEvent.ACTION_DOWN) {
55 | switch (event.getKeyCode()) {
56 | case KeyEvent.KEYCODE_DEL:
57 | mStyleManager.commandDelete(true);
58 | return true;
59 | case KeyEvent.KEYCODE_ENTER:
60 | mStyleManager.commandEnter(true);
61 | return true;
62 | default:
63 | Log.e(TAG, "sendKeyEvent: " + event.getKeyCode());
64 | return super.sendKeyEvent(event);
65 | }
66 | }
67 | return super.sendKeyEvent(event);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/NoteEditorSelectionListener.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor;
2 |
3 | import com.jscheng.srich.model.Options;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/2/27
7 | */
8 | public interface NoteEditorSelectionListener {
9 | void onStyleChange(int start, int end, Options options);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/NoteLineSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render;
2 |
3 | import android.text.Editable;
4 | import android.text.SpannableStringBuilder;
5 | import android.text.Spanned;
6 | import android.text.style.LeadingMarginSpan;
7 | import android.widget.EditText;
8 |
9 | import com.jscheng.srich.editor.NoteEditorConfig;
10 | import com.jscheng.srich.editor.NoteEditorRender;
11 | import com.jscheng.srich.model.Paragraph;
12 |
13 | /**
14 | * 绘制行样式
15 | * Created By Chengjunsen on 2019/3/4
16 | */
17 | public abstract class NoteLineSpanRender {
18 |
19 | public void draw(int globalPos, int num, Paragraph paragraph, SpannableStringBuilder builder) {
20 | if (isLineStyle(paragraph) && paragraph.isPlaceHolder()) {
21 | String url = paragraph.getImageUrl();
22 | int level = paragraph.getIndentation();
23 |
24 | T span = createSpan(num, level, url);
25 | int start = globalPos;
26 | int end = 0;
27 |
28 | if (LeadingMarginSpan.class.isAssignableFrom(span.getClass())) {
29 | end = globalPos + paragraph.getLength();
30 | } else {
31 | end = globalPos + NoteEditorConfig.PlaceHoldChar.length();
32 | }
33 | builder.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
34 | }
35 | }
36 |
37 | protected abstract boolean isLineStyle(Paragraph paragraph);
38 |
39 | protected abstract T createSpan(int num, int level, String url);
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/NoteWordSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render;
2 |
3 | import android.text.Editable;
4 | import android.text.SpannableStringBuilder;
5 | import android.text.Spanned;
6 | import android.text.style.CharacterStyle;
7 | import android.widget.EditText;
8 |
9 | import com.jscheng.srich.model.Paragraph;
10 |
11 | import java.util.List;
12 |
13 | /**
14 | * 绘制字体样式
15 | * Created By Chengjunsen on 2019/3/4
16 | */
17 | public abstract class NoteWordSpanRender {
18 |
19 | public void draw(int globalPos, Paragraph paragraph, SpannableStringBuilder builder) {
20 | List wordStyles = paragraph.getWordStyles();
21 | int start = -1;
22 | for (int i = 0; i < wordStyles.size(); i++) {
23 | if (isStyle(wordStyles.get(i))) {
24 | if (start == -1) { start = i; }
25 | } else if (start > -1) {
26 | draw(globalPos, start, i, builder);
27 | start = -1;
28 | }
29 | }
30 | if (start > -1) {
31 | draw(globalPos, start, wordStyles.size(), builder);
32 | }
33 | }
34 |
35 | private void draw(int globalPos, int start, int end, SpannableStringBuilder builder) {
36 | builder.setSpan(createSpan(), start + globalPos, end + globalPos, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
37 | }
38 |
39 | protected abstract T createSpan();
40 |
41 | protected abstract int getStyle();
42 |
43 | protected abstract boolean isStyle(int style);
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/line_render/NoteBulletLineSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.line_render;
2 |
3 | import com.jscheng.srich.editor.render.NoteLineSpanRender;
4 | import com.jscheng.srich.editor.spans.NoteBulletSpan;
5 | import com.jscheng.srich.model.Paragraph;
6 | import com.jscheng.srich.model.Style;
7 |
8 | /**
9 | * Created By Chengjunsen on 2019/3/4
10 | */
11 | public class NoteBulletLineSpanRender extends NoteLineSpanRender {
12 |
13 | public NoteBulletLineSpanRender() {
14 | }
15 |
16 | @Override
17 | protected boolean isLineStyle(Paragraph paragraph) {
18 | return paragraph.isBulletList();
19 | }
20 |
21 | @Override
22 | protected Object createSpan(int num, int level, String url) {
23 | return NoteBulletSpan.create();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/line_render/NoteCheckBoxSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.line_render;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.view.View;
6 |
7 | import com.jscheng.srich.R;
8 | import com.jscheng.srich.editor.render.NoteLineSpanRender;
9 | import com.jscheng.srich.editor.spans.NoteCheckBoxSpan;
10 | import com.jscheng.srich.model.Paragraph;
11 | import com.jscheng.srich.model.Style;
12 |
13 | /**
14 | * Created By Chengjunsen on 2019/3/6
15 | */
16 | public class NoteCheckBoxSpanRender extends NoteLineSpanRender {
17 |
18 | private View mView;
19 | private Bitmap mBitmap;
20 |
21 | public NoteCheckBoxSpanRender(View view) {
22 | this.mView = view;
23 | this.mBitmap = BitmapFactory.decodeResource(view.getContext().getResources(), R.mipmap.ic_note_edit_check);
24 | }
25 |
26 | @Override
27 | protected boolean isLineStyle(Paragraph paragraph) {
28 | return paragraph.isCheckbox();
29 | }
30 |
31 | @Override
32 | protected NoteCheckBoxSpan createSpan(int num, int level, String url) {
33 | return NoteCheckBoxSpan.create(mBitmap);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/line_render/NoteDividingSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.line_render;
2 |
3 | import android.view.View;
4 |
5 | import com.jscheng.srich.editor.render.NoteLineSpanRender;
6 | import com.jscheng.srich.editor.spans.NoteDividingLineSpan;
7 | import com.jscheng.srich.model.Paragraph;
8 | import com.jscheng.srich.model.Style;
9 |
10 | /**
11 | * Created By Chengjunsen on 2019/3/4
12 | */
13 | public class NoteDividingSpanRender extends NoteLineSpanRender {
14 | private View view;
15 |
16 | public NoteDividingSpanRender(View view) {
17 | this.view = view;
18 | }
19 |
20 | @Override
21 | protected boolean isLineStyle(Paragraph paragraph) {
22 | return paragraph.isDividingLine();
23 | }
24 |
25 | @Override
26 | protected NoteDividingLineSpan createSpan(int num, int level, String url) {
27 | int width = view.getWidth() - view.getPaddingLeft() - view.getPaddingRight();
28 | return NoteDividingLineSpan.create(width);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/line_render/NoteImageSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.line_render;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.drawable.BitmapDrawable;
6 | import android.graphics.drawable.Drawable;
7 | import android.view.View;
8 |
9 | import com.jscheng.srich.R;
10 | import com.jscheng.srich.editor.render.NoteLineSpanRender;
11 | import com.jscheng.srich.editor.spans.NoteImageSpan;
12 | import com.jscheng.srich.image_loader.ImageLoader;
13 | import com.jscheng.srich.model.Paragraph;
14 |
15 | /**
16 | * Created By Chengjunsen on 2019/3/6
17 | */
18 | public class NoteImageSpanRender extends NoteLineSpanRender {
19 |
20 | private int margin = 50;
21 | private View mView;
22 |
23 | public NoteImageSpanRender(View view) {
24 | this.mView = view;
25 | }
26 |
27 | @Override
28 | protected boolean isLineStyle(Paragraph paragraph) {
29 | return paragraph.isImage();
30 | }
31 |
32 | @Override
33 | protected NoteImageSpan createSpan(int num, int level, String url) {
34 | int width = mView.getWidth() - mView.getPaddingLeft() - mView.getPaddingRight();
35 | Bitmap bitmap = ImageLoader.with(mView.getContext()).get(url, width);
36 | if (bitmap == null) {
37 | bitmap = BitmapFactory.decodeResource(mView.getResources(), R.mipmap.ic_note_edit_loading);
38 | }
39 |
40 | int actualWidth = Math.min(width, bitmap.getWidth());
41 | int actualHeight = (int) ((float) actualWidth / bitmap.getWidth() * bitmap.getHeight());
42 |
43 | BitmapDrawable drawable = new BitmapDrawable(mView.getResources(), bitmap);
44 | drawable.setBounds(0, 0, actualWidth, actualHeight);
45 |
46 | return new NoteImageSpan(drawable);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/line_render/NoteIndentationSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.line_render;
2 |
3 | import com.jscheng.srich.editor.render.NoteLineSpanRender;
4 | import com.jscheng.srich.editor.spans.NoteIndentationSpan;
5 | import com.jscheng.srich.model.Paragraph;
6 |
7 | /**
8 | * Created By Chengjunsen on 2019/3/7
9 | */
10 | public class NoteIndentationSpanRender extends NoteLineSpanRender {
11 |
12 | @Override
13 | protected boolean isLineStyle(Paragraph paragraph) {
14 | return paragraph.getIndentation() > 0;
15 | }
16 |
17 | @Override
18 | protected NoteIndentationSpan createSpan(int num, int level, String url) {
19 | return NoteIndentationSpan.create(level);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/line_render/NoteNumSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.line_render;
2 |
3 | import android.view.View;
4 |
5 | import com.jscheng.srich.editor.render.NoteLineSpanRender;
6 | import com.jscheng.srich.editor.spans.NoteNumSpan;
7 | import com.jscheng.srich.model.Paragraph;
8 | import com.jscheng.srich.model.Style;
9 | import com.jscheng.srich.utils.DisplayUtil;
10 |
11 | /**
12 | * Created By Chengjunsen on 2019/3/6
13 | */
14 | public class NoteNumSpanRender extends NoteLineSpanRender {
15 |
16 | public NoteNumSpanRender(){
17 | }
18 |
19 | @Override
20 | protected boolean isLineStyle(Paragraph paragraph) {
21 | return paragraph.isNumList();
22 | }
23 |
24 | @Override
25 | protected NoteNumSpan createSpan(int num, int level, String url) {
26 | return NoteNumSpan.create(num);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/line_render/NoteUncheckBoxSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.line_render;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.view.View;
6 |
7 | import com.jscheng.srich.R;
8 | import com.jscheng.srich.editor.render.NoteLineSpanRender;
9 | import com.jscheng.srich.editor.spans.NoteCheckBoxSpan;
10 | import com.jscheng.srich.model.Paragraph;
11 | import com.jscheng.srich.model.Style;
12 |
13 | /**
14 | * Created By Chengjunsen on 2019/3/6
15 | */
16 | public class NoteUncheckBoxSpanRender extends NoteLineSpanRender {
17 |
18 | private View mView;
19 | private Bitmap mBitmap;
20 |
21 | public NoteUncheckBoxSpanRender(View view) {
22 | this.mView = view;
23 | this.mBitmap = BitmapFactory.decodeResource(view.getContext().getResources(), R.mipmap.ic_note_edit_uncheck);
24 | }
25 |
26 | @Override
27 | protected boolean isLineStyle(Paragraph paragraph) {
28 | return paragraph.isUnCheckbox();
29 | }
30 |
31 | @Override
32 | protected NoteCheckBoxSpan createSpan(int num, int level, String url) {
33 | return NoteCheckBoxSpan.create(mBitmap);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/word_render/NoteBackgroundSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.word_render;
2 |
3 | import com.jscheng.srich.editor.render.NoteWordSpanRender;
4 | import com.jscheng.srich.editor.spans.NoteBackgroundSpan;
5 | import com.jscheng.srich.model.Style;
6 |
7 | /**
8 | * Created By Chengjunsen on 2019/3/4
9 | */
10 | public class NoteBackgroundSpanRender extends NoteWordSpanRender {
11 | @Override
12 | protected NoteBackgroundSpan createSpan() {
13 | return NoteBackgroundSpan.create();
14 | }
15 |
16 | @Override
17 | protected int getStyle() {
18 | return Style.BackgroudColor;
19 | }
20 |
21 | @Override
22 | protected boolean isStyle(int style) {
23 | return Style.isWordStyle(style, getStyle());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/word_render/NoteBoldSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.word_render;
2 |
3 | import com.jscheng.srich.editor.render.NoteWordSpanRender;
4 | import com.jscheng.srich.editor.spans.NoteBoldSpan;
5 | import com.jscheng.srich.model.Style;
6 |
7 | /**
8 | * Created By Chengjunsen on 2019/3/4
9 | */
10 | public class NoteBoldSpanRender extends NoteWordSpanRender {
11 |
12 | @Override
13 | protected NoteBoldSpan createSpan() {
14 | return NoteBoldSpan.create();
15 | }
16 |
17 | @Override
18 | protected int getStyle() {
19 | return Style.Bold;
20 | }
21 |
22 | @Override
23 | protected boolean isStyle(int style) {
24 | return Style.isWordStyle(style, getStyle());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/word_render/NoteItalicSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.word_render;
2 |
3 | import com.jscheng.srich.editor.render.NoteWordSpanRender;
4 | import com.jscheng.srich.editor.spans.NoteItalicSpan;
5 | import com.jscheng.srich.model.Style;
6 |
7 | /**
8 | * Created By Chengjunsen on 2019/3/4
9 | */
10 | public class NoteItalicSpanRender extends NoteWordSpanRender {
11 |
12 | @Override
13 | protected NoteItalicSpan createSpan() {
14 | return NoteItalicSpan.create();
15 | }
16 |
17 | @Override
18 | protected int getStyle() {
19 | return Style.Italic;
20 | }
21 |
22 | @Override
23 | protected boolean isStyle(int style) {
24 | return Style.isWordStyle(style, getStyle());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/word_render/NoteStrikethroughSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.word_render;
2 |
3 | import com.jscheng.srich.editor.render.NoteWordSpanRender;
4 | import com.jscheng.srich.editor.spans.NoteStrikethroughSpan;
5 | import com.jscheng.srich.model.Style;
6 |
7 | /**
8 | * Created By Chengjunsen on 2019/3/4
9 | */
10 | public class NoteStrikethroughSpanRender extends NoteWordSpanRender {
11 |
12 | @Override
13 | protected NoteStrikethroughSpan createSpan() {
14 | return NoteStrikethroughSpan.create();
15 | }
16 |
17 | @Override
18 | protected int getStyle() {
19 | return Style.Strikethrough;
20 | }
21 |
22 | @Override
23 | protected boolean isStyle(int style) {
24 | return Style.isWordStyle(style, getStyle());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/word_render/NoteSubscriptSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.word_render;
2 |
3 | import com.jscheng.srich.editor.render.NoteWordSpanRender;
4 | import com.jscheng.srich.editor.spans.NoteSubscriptSpan;
5 | import com.jscheng.srich.model.Style;
6 |
7 | /**
8 | * Created By Chengjunsen on 2019/3/4
9 | */
10 | public class NoteSubscriptSpanRender extends NoteWordSpanRender {
11 | @Override
12 | protected NoteSubscriptSpan createSpan() {
13 | return NoteSubscriptSpan.create();
14 | }
15 |
16 | @Override
17 | protected int getStyle() {
18 | return Style.SubScript;
19 | }
20 |
21 | @Override
22 | protected boolean isStyle(int style) {
23 | return Style.isWordStyle(style, getStyle());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/word_render/NoteSuperscriptSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.word_render;
2 |
3 | import com.jscheng.srich.editor.render.NoteWordSpanRender;
4 | import com.jscheng.srich.editor.spans.NoteSuperscriptSpan;
5 | import com.jscheng.srich.model.Style;
6 |
7 | /**
8 | * Created By Chengjunsen on 2019/3/4
9 | */
10 | public class NoteSuperscriptSpanRender extends NoteWordSpanRender {
11 | @Override
12 | protected NoteSuperscriptSpan createSpan() {
13 | return NoteSuperscriptSpan.create();
14 | }
15 |
16 | @Override
17 | protected int getStyle() {
18 | return Style.SuperScript;
19 | }
20 |
21 | @Override
22 | protected boolean isStyle(int style) {
23 | return Style.isWordStyle(style, getStyle());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/render/word_render/NoteUnderlineSpanRender.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.render.word_render;
2 |
3 | import com.jscheng.srich.editor.render.NoteWordSpanRender;
4 | import com.jscheng.srich.editor.spans.NoteUnderLineSpan;
5 | import com.jscheng.srich.model.Style;
6 |
7 | /**
8 | * Created By Chengjunsen on 2019/3/4
9 | */
10 | public class NoteUnderlineSpanRender extends NoteWordSpanRender {
11 | @Override
12 | protected NoteUnderLineSpan createSpan() {
13 | return NoteUnderLineSpan.create();
14 | }
15 |
16 | @Override
17 | protected int getStyle() {
18 | return Style.UnderLine;
19 | }
20 |
21 | @Override
22 | protected boolean isStyle(int style) {
23 | return Style.isWordStyle(style, getStyle());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteBackgroundSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | import android.graphics.Color;
4 | import android.text.style.BackgroundColorSpan;
5 |
6 | import com.jscheng.srich.editor.NoteEditorConfig;
7 |
8 | /**
9 | * Created By Chengjunsen on 2019/2/25
10 | */
11 | public class NoteBackgroundSpan extends BackgroundColorSpan{
12 |
13 | private final static int color = NoteEditorConfig.HighLightBackgroundColor;
14 |
15 | public NoteBackgroundSpan() {
16 | super(color);
17 | }
18 |
19 | public static NoteBackgroundSpan create() {
20 | return new NoteBackgroundSpan();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteBoldSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | import android.graphics.Typeface;
4 | import android.text.style.StyleSpan;
5 |
6 | /**
7 | * Created By Chengjunsen on 2019/2/25
8 | */
9 | public class NoteBoldSpan extends StyleSpan {
10 |
11 | public NoteBoldSpan() {
12 | super(Typeface.BOLD);
13 | }
14 |
15 | public static NoteBoldSpan create() {
16 | return new NoteBoldSpan();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteBulletSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.text.Layout;
9 | import android.text.style.BulletSpan;
10 | import android.text.style.LeadingMarginSpan;
11 | import android.text.style.ReplacementSpan;
12 |
13 | /**
14 | * Created By Chengjunsen on 2019/2/25
15 | */
16 | public class NoteBulletSpan extends BulletSpan {
17 |
18 | private int mRadius = 5;
19 | private int mMargin = 25;
20 |
21 | public static NoteBulletSpan create() {
22 | return new NoteBulletSpan();
23 | }
24 |
25 | @Override
26 | public int getLeadingMargin(boolean first) {
27 | return (mMargin + mRadius) * 2;
28 | }
29 |
30 | @Override
31 | public void drawLeadingMargin(Canvas canvas, Paint paint, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
32 | if (first) {
33 | Paint.Style style = paint.getStyle();
34 | paint.setStyle(Paint.Style.FILL);
35 | paint.setColor(Color.BLACK);
36 |
37 | int transY = top + (bottom - top) / 2 - mRadius;
38 | int transX = x + dir + mMargin;
39 |
40 | canvas.save();
41 | canvas.translate(transX, transY);
42 | canvas.drawCircle(0, 0, mRadius, paint);
43 | canvas.restore();
44 |
45 | paint.setStyle(style);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteCheckBoxSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 | import android.support.annotation.NonNull;
9 | import android.support.annotation.Nullable;
10 | import android.text.Layout;
11 | import android.text.Spanned;
12 | import android.text.style.LeadingMarginSpan;
13 | import android.text.style.ReplacementSpan;
14 |
15 | /**
16 | * Created By Chengjunsen on 2019/3/6
17 | */
18 | public class NoteCheckBoxSpan implements NoteClickSpan, LeadingMarginSpan {
19 | private Bitmap mBitmap;
20 | private int mMargin = 12;
21 | private int mWidth = 50;
22 |
23 |
24 | public NoteCheckBoxSpan(Bitmap bitmap) {
25 | mBitmap = bitmap;
26 | }
27 |
28 | @Override
29 | public int getLeadingMargin(boolean first) {
30 | return mWidth + mMargin;
31 | }
32 |
33 | @Override
34 | public void drawLeadingMargin(Canvas canvas, Paint paint, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
35 | if (((Spanned)text).getSpanStart(this) == start) {
36 | paint.setColor(Color.BLACK);
37 | Paint.Style style = paint.getStyle();
38 | paint.setStyle(Paint.Style.FILL);
39 | int transX = x + dir;
40 | int transY = top;
41 | canvas.save();
42 | canvas.translate(transX, transY);
43 | Rect resRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
44 | Rect destrect = new Rect(0, 0, mWidth, mWidth);
45 | canvas.drawBitmap(mBitmap, resRect, destrect, paint);
46 | canvas.restore();
47 | paint.setStyle(style);
48 | }
49 | }
50 |
51 | public static NoteCheckBoxSpan create(Bitmap bitmap) {
52 | return new NoteCheckBoxSpan(bitmap);
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteClickSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/3/6
5 | */
6 | public interface NoteClickSpan {
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteDividingLineSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.text.Layout;
9 | import android.text.style.AlignmentSpan;
10 | import android.text.style.ReplacementSpan;
11 |
12 | /**
13 | * Created By Chengjunsen on 2019/2/25
14 | */
15 | public class NoteDividingLineSpan extends ReplacementSpan implements AlignmentSpan {
16 |
17 | private int mlineWidth;
18 | private int mMargin;
19 |
20 | public NoteDividingLineSpan(int width) {
21 | this.mMargin = 50;
22 | this.mlineWidth = width - 2 * mMargin;
23 | }
24 |
25 | @Override
26 | public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) {
27 | return mlineWidth;
28 | }
29 |
30 | @Override
31 | public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {
32 | paint.setColor(Color.BLACK);
33 | paint.setStrokeWidth(2);
34 | canvas.drawLine(x , y, x + mlineWidth, y, paint);
35 | }
36 |
37 | public static NoteDividingLineSpan create(int mWidth) {
38 | return new NoteDividingLineSpan(mWidth);
39 | }
40 |
41 | @Override
42 | public Layout.Alignment getAlignment() {
43 | return Layout.Alignment.ALIGN_CENTER;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteImageSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 | import android.graphics.Canvas;
3 | import android.graphics.Paint;
4 | import android.graphics.Rect;
5 | import android.graphics.drawable.BitmapDrawable;
6 | import android.graphics.drawable.Drawable;
7 | import android.support.annotation.IntRange;
8 | import android.support.annotation.NonNull;
9 | import android.support.annotation.Nullable;
10 | import android.text.Layout;
11 | import android.text.style.AlignmentSpan;
12 | import android.text.style.ImageSpan;
13 |
14 | /**
15 | * Created By Chengjunsen on 2019/3/6
16 | */
17 | public class NoteImageSpan extends ImageSpan implements AlignmentSpan, NoteClickSpan{
18 |
19 | private int margin = 30;
20 |
21 | public NoteImageSpan(BitmapDrawable drawable) {
22 | super(drawable, ALIGN_BASELINE);
23 | }
24 |
25 | @Override
26 | public Layout.Alignment getAlignment() {
27 | return Layout.Alignment.ALIGN_CENTER;
28 | }
29 |
30 | @Override
31 | public int getSize(@NonNull Paint paint, CharSequence text,
32 | @IntRange(from = 0) int start, @IntRange(from = 0) int end,
33 | @Nullable Paint.FontMetricsInt fm) {
34 | Drawable d = getDrawable();
35 | Rect rect = d.getBounds();
36 |
37 | if (fm != null) {
38 | fm.ascent = -rect.bottom - margin * 2;
39 | fm.descent = 0;
40 |
41 | fm.top = fm.ascent;
42 | fm.bottom = 0;
43 | }
44 |
45 | return rect.right;
46 | }
47 |
48 | @Override
49 | public void draw(@NonNull Canvas canvas, CharSequence text,
50 | @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
51 | int top, int y, int bottom, @NonNull Paint paint) {
52 | BitmapDrawable d = (BitmapDrawable)getDrawable();
53 | Rect rect = d.getBounds();
54 | canvas.save();
55 |
56 | int transY = bottom - rect.bottom - margin;
57 | if (mVerticalAlignment == ALIGN_BASELINE) {
58 | transY -= paint.getFontMetricsInt().descent;
59 | }
60 |
61 | canvas.translate(x, transY);
62 | d.draw(canvas);
63 | canvas.restore();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteIndentationSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 |
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.text.Layout;
7 | import android.text.style.LeadingMarginSpan;
8 |
9 | /**
10 | * Created By Chengjunsen on 2019/3/7
11 | */
12 | public class NoteIndentationSpan implements LeadingMarginSpan {
13 |
14 | private int mLevel;
15 | private int mMargin;
16 |
17 | public NoteIndentationSpan(int level) {
18 | this.mLevel = level;
19 | this.mMargin = 50;
20 | }
21 |
22 | public static NoteIndentationSpan create(int level) {
23 | return new NoteIndentationSpan(level);
24 | }
25 |
26 | @Override
27 | public int getLeadingMargin(boolean first) {
28 | return mMargin * mLevel;
29 | }
30 |
31 | @Override
32 | public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteItalicSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | import android.graphics.Typeface;
4 | import android.text.style.StyleSpan;
5 |
6 | /**
7 | * Created By Chengjunsen on 2019/2/25
8 | */
9 | public class NoteItalicSpan extends StyleSpan{
10 |
11 | public NoteItalicSpan() {
12 | super(Typeface.ITALIC);
13 | }
14 |
15 | public static NoteItalicSpan create() {
16 | return new NoteItalicSpan();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteNumSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import android.text.Layout;
7 | import android.text.Spanned;
8 | import android.text.style.LeadingMarginSpan;
9 |
10 | /**
11 | * Created By Chengjunsen on 2019/3/6
12 | */
13 | public class NoteNumSpan implements LeadingMarginSpan {
14 | private int mNum;
15 | private int mMargin = 15;
16 | private int mWidth = 40;
17 |
18 | public NoteNumSpan(int num) {
19 | this.mNum = num;
20 | }
21 |
22 | public static NoteNumSpan create(int num) {
23 | return new NoteNumSpan(num);
24 | }
25 |
26 | @Override
27 | public int getLeadingMargin(boolean first) {
28 | return mWidth + mMargin * 2;
29 | }
30 |
31 | @Override
32 | public void drawLeadingMargin(Canvas canvas, Paint paint, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
33 | if (((Spanned)text).getSpanStart(this) == start) {
34 | paint.setColor(Color.BLACK);
35 | Paint.Style style = paint.getStyle();
36 | paint.setStyle(Paint.Style.FILL);
37 | int transX = x + dir + mMargin;
38 | int transY = baseline;
39 | canvas.save();
40 | canvas.translate(transX, transY);
41 | canvas.drawText(String.valueOf(mNum) + ".", 0, 0, paint);
42 | canvas.restore();
43 | paint.setStyle(style);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteStrikethroughSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | import android.text.style.StrikethroughSpan;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/2/25
7 | */
8 | public class NoteStrikethroughSpan extends StrikethroughSpan {
9 |
10 | public static NoteStrikethroughSpan create() {
11 | return new NoteStrikethroughSpan();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteSubscriptSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/2/25
5 | * 下标
6 | */
7 | public class NoteSubscriptSpan extends android.text.style.SubscriptSpan {
8 |
9 | public static NoteSubscriptSpan create() {
10 | return new NoteSubscriptSpan();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteSuperscriptSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/2/25
5 | * 下标
6 | */
7 | public class NoteSuperscriptSpan extends android.text.style.SuperscriptSpan {
8 |
9 | public static NoteSuperscriptSpan create() {
10 | return new NoteSuperscriptSpan();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteTypefaceSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | import android.text.style.TypefaceSpan;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/2/28
7 | */
8 | public class NoteTypefaceSpan extends TypefaceSpan {
9 |
10 | public NoteTypefaceSpan(String family) {
11 | super(family);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteUnderLineSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | import android.text.style.UnderlineSpan;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/2/25
7 | */
8 | public class NoteUnderLineSpan extends UnderlineSpan {
9 | public static NoteUnderLineSpan create() {
10 | return new NoteUnderLineSpan();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/editor/spans/NoteUriSpan.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.editor.spans;
2 |
3 | import android.text.style.URLSpan;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/2/25
7 | */
8 | public class NoteUriSpan extends URLSpan{
9 |
10 | public NoteUriSpan(String url) {
11 | super(url);
12 | }
13 |
14 | public static NoteUriSpan create(String url) {
15 | return new NoteUriSpan(url);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/DownSampler.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.support.media.ExifInterface;
6 | import android.util.Size;
7 |
8 | import java.io.BufferedInputStream;
9 | import java.io.IOException;
10 |
11 | /**
12 | * Created By Chengjunsen on 2019/3/21
13 | */
14 | public class DownSampler {
15 |
16 | private static final int MARK_POSITION = 10 * 1024 * 1024;
17 |
18 | public static Size getDimensions(BufferedInputStream is, BitmapFactory.Options options) {
19 | options.inJustDecodeBounds = true;
20 | decodeStream(is, options);
21 | options.inJustDecodeBounds = false;
22 | return new Size(options.outWidth, options.outHeight);
23 | }
24 |
25 | public static boolean isScaling(BitmapFactory.Options options) {
26 | return options.inTargetDensity > 0 && options.inDensity > 0
27 | && options.inTargetDensity != options.inDensity;
28 | }
29 |
30 | public static Bitmap decodeStream(BufferedInputStream is, BitmapFactory.Options options) {
31 | if (options.inJustDecodeBounds) {
32 | is.mark(MARK_POSITION);
33 | }
34 | final Bitmap result = BitmapFactory.decodeStream(is, null, options);
35 | if (options.inJustDecodeBounds) {
36 | try {
37 | is.reset();
38 | } catch (IOException e) {
39 | e.printStackTrace();
40 | }
41 | }
42 | return result;
43 | }
44 |
45 | public static int getRotate(BufferedInputStream is) {
46 | try {
47 | int orientation = getOrientation(is);
48 | return getExifOrientationDegrees(orientation);
49 | } catch (IOException e) {
50 | e.printStackTrace();
51 | }
52 | return 0;
53 | }
54 |
55 | public static int getOrientation(BufferedInputStream is) throws IOException {
56 | ExifInterface exifInterface = null;
57 | is.mark(MARK_POSITION);
58 | exifInterface = new ExifInterface(is);
59 | int result = exifInterface.getAttributeInt(
60 | ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
61 | try {
62 | is.reset();
63 | } catch (IOException e) {
64 | e.printStackTrace();
65 | }
66 | if (result == ExifInterface.ORIENTATION_UNDEFINED) {
67 | return 0;
68 | }
69 | return result;
70 | }
71 |
72 | public static int getExifOrientationDegrees(int exifOrientation) {
73 | final int degreesToRotate;
74 | switch (exifOrientation) {
75 | case ExifInterface.ORIENTATION_TRANSPOSE:
76 | case ExifInterface.ORIENTATION_ROTATE_90:
77 | degreesToRotate = 90;
78 | break;
79 | case ExifInterface.ORIENTATION_ROTATE_180:
80 | case ExifInterface.ORIENTATION_FLIP_VERTICAL:
81 | degreesToRotate = 180;
82 | break;
83 | case ExifInterface.ORIENTATION_TRANSVERSE:
84 | case ExifInterface.ORIENTATION_ROTATE_270:
85 | degreesToRotate = 270;
86 | break;
87 | default:
88 | degreesToRotate = 0;
89 | break;
90 | }
91 | return degreesToRotate;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/FileImageJob.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 | import android.text.TextUtils;
6 | import java.io.BufferedInputStream;
7 | import java.io.File;
8 | import java.io.FileInputStream;
9 | import java.io.FileNotFoundException;
10 |
11 | import com.jscheng.srich.utils.UriPathUtil;
12 |
13 | /**
14 | * Created By Chengjunsen on 2019/3/22
15 | */
16 | public class FileImageJob extends ImageJob {
17 |
18 | public FileImageJob(Context context, String key, String url, ImageJobCallback callback, ImageDiskCache diskCache) {
19 | super(context, key, url, callback, diskCache);
20 | }
21 |
22 | @Override
23 | public void run() {
24 | String path = null;
25 | if (isContentUrl(url)) {
26 | path = UriPathUtil.getAbsulotePath(context, Uri.parse(url));
27 | } else if (isFileUrl(url)){
28 | path = url;
29 | }
30 | if (TextUtils.isEmpty(path)) {
31 | failed("url is not valid");
32 | } else {
33 | try {
34 | BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(path));
35 | successed(inputStream);
36 | } catch (FileNotFoundException e) {
37 | e.printStackTrace();
38 | failed(e.toString());
39 | }
40 | }
41 | }
42 |
43 | private boolean isFileUrl(String url) {
44 | return new File(url).exists();
45 | }
46 |
47 | private boolean isContentUrl(String url) {
48 | return url.toLowerCase().startsWith("content");
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/HttpImageJob.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | import android.content.Context;
4 |
5 | import com.jscheng.srich.utils.StorageUtil;
6 |
7 | import java.io.BufferedInputStream;
8 | import java.io.File;
9 | import java.io.IOException;
10 |
11 | import okhttp3.Cache;
12 | import okhttp3.Call;
13 | import okhttp3.OkHttpClient;
14 | import okhttp3.Request;
15 | import okhttp3.Response;
16 |
17 | /**
18 | * Created By Chengjunsen on 2019/3/22
19 | */
20 | public class HttpImageJob extends ImageJob {
21 | /**
22 | * okhttp
23 | */
24 | private static OkHttpClient mOkhttpClient;
25 | /**
26 | * okhttp 文件名字
27 | */
28 | private static final String OkhttpCacheDirName = "okhttp";
29 |
30 | public HttpImageJob(Context context, String key, String url, ImageJobCallback callback, ImageDiskCache diskCache) {
31 | super(context, key, url, callback, diskCache);
32 |
33 | int mOkhttpCacheSize = 10 * 1024 * 1024;
34 | File okhttpCacheFile = getDiskCachePath(context, OkhttpCacheDirName);
35 | mOkhttpClient = new OkHttpClient.Builder()
36 | .cache(new Cache(okhttpCacheFile, mOkhttpCacheSize))
37 | .build();
38 | }
39 |
40 | @Override
41 | public void run() {
42 | try {
43 | Request bitmapRequest = new Request.Builder().get().url(url).build();
44 | Call call = mOkhttpClient.newCall(bitmapRequest);
45 | Response response = call.execute();
46 | if (response.isSuccessful()) {
47 | BufferedInputStream inputStream = new BufferedInputStream(response.body().byteStream());
48 | successed(inputStream);
49 | } else {
50 | failed(response.message());
51 | }
52 | } catch (IOException e) {
53 | e.printStackTrace();
54 | failed(e.toString());
55 | }
56 | }
57 |
58 | private File getDiskCachePath(Context context, String uniqueName){
59 | String cachePath = StorageUtil.getDiskCachePath(context);
60 | return new File( cachePath + File.separator +uniqueName);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/ImageFetcher.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import java.util.ArrayList;
6 | import java.util.HashMap;
7 | import java.util.List;
8 | import java.util.concurrent.ExecutorService;
9 | import java.util.concurrent.Executors;
10 |
11 | /**
12 | * Created By Chengjunsen on 2019/3/21
13 | */
14 | class ImageFetcher implements ImageJobCallback{
15 | private static final String TAG = "TAG";
16 |
17 | /**
18 | * 失败最大重试次数
19 | */
20 | private static final int MaxUrlFailedTime = 2;
21 |
22 | /**
23 | * 正在请求下载的url集合
24 | */
25 | private volatile static List mRequestingUrls;
26 |
27 | /**
28 | * 失败的URL的key和失败次数
29 | */
30 | private static HashMap mFailedUrls;
31 |
32 | /**
33 | * 线程池
34 | */
35 | private static ExecutorService mExecutors;
36 |
37 | private ImageDiskCache mDiskCache;
38 |
39 | private Context mAppContext;
40 |
41 | private ImageFetcherCallback mCallback;
42 |
43 | public ImageFetcher(Context context, ImageDiskCache cache, ImageFetcherCallback callback) {
44 | mDiskCache = cache;
45 | mAppContext = context;
46 | mCallback = callback;
47 | mRequestingUrls = new ArrayList<>();
48 | mFailedUrls = new HashMap<>();
49 | mExecutors = Executors.newFixedThreadPool(3);
50 | }
51 |
52 | public synchronized void load(String url, String key) {
53 | if (mRequestingUrls.contains(url)) {
54 | Log.e(TAG, "load: url is requesting");
55 | return;
56 | }
57 | if (mFailedUrls.containsKey(key) && mFailedUrls.get(key) > MaxUrlFailedTime) {
58 | onJobFailed(url, key, "load: url is failed max time");
59 | return;
60 | }
61 | mRequestingUrls.add(url);
62 | ImageJob job = ImageJobFactory.build(mAppContext, url, key, this, mDiskCache);
63 | mExecutors.submit(job);
64 | }
65 |
66 | @Override
67 | public synchronized void onJobFailed(final String url, final String key, final String err) {
68 | mRequestingUrls.remove(url);
69 | Integer time = mFailedUrls.get(key);
70 | time = time == null ? 1 : time + 1;
71 | mFailedUrls.put(key, time);
72 |
73 | mCallback.onFetchFailed(url, key, err);
74 | }
75 |
76 | @Override
77 | public synchronized void onJobSuccess(final String url, final String key) {
78 | mRequestingUrls.remove(url);
79 |
80 | mCallback.onFetchSuccess(url, key);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/ImageFetcherCallback.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/3/22
5 | */
6 | public interface ImageFetcherCallback {
7 | void onFetchFailed(final String url, final String key, final String err);
8 | void onFetchSuccess(final String url, final String key);
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/ImageGlobalListener.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/3/22
5 | */
6 | public interface ImageGlobalListener {
7 | void onImageLoadSuccess(String url);
8 | void onImageLoadFailed(String url, String err);
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/ImageJob.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.util.Size;
7 |
8 | import java.io.BufferedInputStream;
9 |
10 | /**
11 | * Created By Chengjunsen on 2019/3/22
12 | */
13 | public abstract class ImageJob implements Runnable{
14 |
15 | protected String key;
16 |
17 | protected String url;
18 |
19 | protected Context context;
20 |
21 | private ImageJobCallback callback;
22 |
23 | private ImageDiskCache mDiskCache;
24 |
25 | public ImageJob(Context context, String key, String url, ImageJobCallback callback, ImageDiskCache diskCache) {
26 | this.key = key;
27 | this.url = url;
28 | this.callback = callback;
29 | this.mDiskCache = diskCache;
30 | this.context = context;
31 | }
32 |
33 | protected void successed(BufferedInputStream inputStream) {
34 | Size size = DownSampler.getDimensions(inputStream, new BitmapFactory.Options());
35 | mDiskCache.put(inputStream, key);
36 | Bitmap bitmap = mDiskCache.get(key, 0);
37 | callback.onJobSuccess(url, key);
38 | }
39 |
40 | protected void failed(String err) {
41 | callback.onJobFailed(url, key, err);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/ImageJobCallback.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/3/22
5 | */
6 | public interface ImageJobCallback {
7 | void onJobFailed(final String url, final String key, final String err);
8 | void onJobSuccess(final String url, final String key);
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/ImageJobFactory.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/3/22
7 | * 简单的工厂方法
8 | */
9 | public class ImageJobFactory {
10 |
11 | public static ImageJob build(Context context, String url, String key, ImageJobCallback callback, ImageDiskCache mCache) {
12 | if (url.startsWith("http")) {
13 | return new HttpImageJob(context, key, url, callback, mCache);
14 | } else {
15 | return new FileImageJob(context, key, url, callback, mCache);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/ImageKeyFactory.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | import com.jscheng.srich.utils.MdUtil;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/3/21
7 | */
8 | public class ImageKeyFactory {
9 |
10 | public static String generateKey(String url) {
11 | return MdUtil.encode(url);
12 | }
13 |
14 | public static String generateKey(String url, int width) {
15 | return MdUtil.encode(url + width);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/ImageMemoryCache.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | import android.graphics.Bitmap;
4 | import android.util.LruCache;
5 |
6 | /**
7 | * Created By Chengjunsen on 2019/3/21
8 | */
9 | public class ImageMemoryCache {
10 | /**
11 | * 最大内存缓存空间
12 | */
13 | private static final int mMemoryCacheSize = (int)Runtime.getRuntime().maxMemory() / 8;
14 | /**
15 | * 内存图片缓存
16 | */
17 | private static LruCache mMemoryCache;
18 |
19 | public ImageMemoryCache() {
20 | mMemoryCache = new LruCache(mMemoryCacheSize) {
21 | @Override
22 | protected int sizeOf(String key, Bitmap value) {
23 | return value.getByteCount();
24 | }
25 | };
26 | }
27 |
28 | public synchronized Bitmap get(String key, int width) {
29 | return mMemoryCache.get(key + width);
30 | }
31 |
32 | public synchronized void remove(String key, int width) {
33 | mMemoryCache.remove(key + width);
34 | }
35 |
36 | public synchronized void put(String key, int width, Bitmap bitmap) {
37 | mMemoryCache.put(key + width, bitmap);
38 | }
39 |
40 | public synchronized boolean isCache(String key, int width) {
41 | return mMemoryCache.get(key + width) != null;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/ImageTarget.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/3/21
5 | */
6 | public interface ImageTarget {
7 |
8 | void onResourceReady(String url, String key);
9 |
10 | void onResourceFailed(String url, String key, String err);
11 |
12 | String getUrl();
13 |
14 | int getMaxWidth();
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_loader/ImageViewTarget.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_loader;
2 |
3 | import android.graphics.Bitmap;
4 | import android.util.Log;
5 | import android.view.ViewTreeObserver;
6 | import android.widget.ImageView;
7 | import java.lang.ref.WeakReference;
8 |
9 | /**
10 | * Created By Chengjunsen on 2019/3/21
11 | */
12 | public class ImageViewTarget implements ImageTarget {
13 | private WeakReference mImageView;
14 | private String url;
15 | private String key;
16 |
17 | public ImageViewTarget(ImageView imageView, String key, String url) {
18 | imageView.setTag(key);
19 | this.mImageView = new WeakReference(imageView) ;
20 | this.key = key;
21 | this.url = url;
22 | }
23 |
24 | @Override
25 | public void onResourceReady(final String url, final String key) {
26 | final ImageView imageView = mImageView.get();
27 | if (imageView == null) {
28 | return;
29 | }
30 | if (imageView.getMeasuredWidth() > 0) {
31 | setResource(url, key);
32 | } else {
33 | final ViewTreeObserver observer = imageView.getViewTreeObserver();
34 | observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
35 | @Override
36 | public boolean onPreDraw() {
37 | setResource(url, key);
38 | imageView.getViewTreeObserver().removeOnPreDrawListener(this);
39 | return false;
40 | }
41 | });
42 | }
43 | }
44 |
45 | private void setResource(String url, String key) {
46 | final ImageView imageView = mImageView.get();
47 | if (imageView == null) {
48 | return;
49 | }
50 | Bitmap bitmap = ImageLoader.with(imageView.getContext()).getCache(url, imageView.getMeasuredWidth());
51 | if (bitmap != null) {
52 | imageView.setImageBitmap(bitmap);
53 | }
54 | }
55 |
56 | @Override
57 | public void onResourceFailed(String url, String key, String err) {
58 | Log.e("TAG", "loadBitmap faied: " + url + " " + err);
59 | }
60 |
61 | @Override
62 | public String getUrl() {
63 | return url;
64 | }
65 |
66 | @Override
67 | public int getMaxWidth() {
68 | final ImageView imageView = mImageView.get();
69 | if (imageView != null) {
70 | return imageView.getMeasuredWidth();
71 | }
72 | return 0;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/image_preview/ImagePreviewActivity.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.image_preview;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.view.WindowManager;
7 |
8 | import com.jscheng.annotations.Route;
9 | import com.jscheng.srich.BaseActivity;
10 | import com.jscheng.srich.R;
11 | import com.jscheng.srich.image_loader.ImageLoader;
12 | import com.jscheng.srich.widget.PinchImageView;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * 图片预览
18 | * Created By Chengjunsen on 2019/3/15
19 | */
20 | @Route("imagePreview")
21 | public class ImagePreviewActivity extends BaseActivity {
22 |
23 | PinchImageView mImageView;
24 |
25 | @Override
26 | protected void onCreate(@Nullable Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | setContentView(R.layout.activity_image_preview);
29 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
30 | getWindow().setStatusBarColor(getColor(R.color.image_preview_background_color));
31 |
32 | mImageView = findViewById(R.id.preview_imageview);
33 | mImageView.setImageResource(R.mipmap.ic_note_edit_loading);
34 | loadImage();
35 | }
36 |
37 | private void loadImage() {
38 | Intent intent = getIntent();
39 | List urls = intent.getStringArrayListExtra("urls");
40 | int index = intent.getIntExtra("index", 0);
41 | if (urls.isEmpty() || index >= urls.size()) {
42 | return;
43 | }
44 | String url = urls.get(index);
45 | ImageLoader.with(this).load(url, mImageView);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/model/Note.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.model;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * Created By Chengjunsen on 2019/2/20
8 | */
9 | public class Note {
10 | private String id;
11 |
12 | private String title;
13 |
14 | private long createTime;
15 |
16 | private long modifyTime;
17 |
18 | private String summary;
19 |
20 | private String summaryImageUrl;
21 |
22 | private String localPath;
23 |
24 | private boolean isDirty;
25 |
26 | private List paragraphs;
27 |
28 | public Note() {
29 | paragraphs = new ArrayList<>();
30 | isDirty = true;
31 | }
32 |
33 | public List getParagraphs() {
34 | return paragraphs;
35 | }
36 |
37 | public void setParagraphs(List paragraphs) {
38 | if (paragraphs != null) {
39 | this.paragraphs = paragraphs;
40 | }
41 | }
42 |
43 | public String getId() {
44 | return id;
45 | }
46 |
47 | public void setId(String id) {
48 | this.id = id;
49 | }
50 |
51 | public String getTitle() {
52 | return title;
53 | }
54 |
55 | public void setTitle(String title) {
56 | this.title = title;
57 | }
58 |
59 | public long getCreateTime() {
60 | return createTime;
61 | }
62 |
63 | public void setCreateTime(long createTime) {
64 | this.createTime = createTime;
65 | }
66 |
67 | public long getModifyTime() {
68 | return modifyTime;
69 | }
70 |
71 | public void setModifyTime(long modifyTime) {
72 | this.modifyTime = modifyTime;
73 | }
74 |
75 | public String getSummary() {
76 | return summary;
77 | }
78 |
79 | public void setSummary(String summary) {
80 | this.summary = summary;
81 | }
82 |
83 | public String getSummaryImageUrl() {
84 | return summaryImageUrl;
85 | }
86 |
87 | public void setSummaryImageUrl(String summaryImageUrl) {
88 | this.summaryImageUrl = summaryImageUrl;
89 | }
90 |
91 | public String getLocalPath() {
92 | return localPath;
93 | }
94 |
95 | public void setLocalPath(String localPath) {
96 | this.localPath = localPath;
97 | }
98 |
99 | public boolean isDirty() {
100 | return isDirty;
101 | }
102 |
103 | public void setDirty(boolean dirty) {
104 | isDirty = dirty;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/model/NoteBuilder.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.model;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/3/13
5 | */
6 | public class NoteBuilder {
7 | private Note note;
8 |
9 | public NoteBuilder() {
10 | this.note = new Note();
11 | }
12 |
13 | public Note build() {
14 | return note;
15 | }
16 |
17 | public NoteBuilder id(String id) {
18 | note.setId(id);
19 | return this;
20 | }
21 |
22 | public NoteBuilder title(String title) {
23 | note.setTitle(title);
24 | return this;
25 | }
26 |
27 | public NoteBuilder createtime(long time) {
28 | note.setCreateTime(time);
29 | return this;
30 | }
31 |
32 | public NoteBuilder motifytime(long time) {
33 | note.setModifyTime(time);
34 | return this;
35 | }
36 |
37 | public NoteBuilder summary(String summary) {
38 | note.setSummary(summary);
39 | return this;
40 | }
41 |
42 | public NoteBuilder summaryImageUrl(String url) {
43 | note.setSummaryImageUrl(url);
44 | return this;
45 | }
46 |
47 | public NoteBuilder localPath(String path) {
48 | note.setLocalPath(path);
49 | return this;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/model/NoteSnap.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.model;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Note的快照,用于撤销、反撤销
7 | * Created By Chengjunsen on 2019/3/8
8 | */
9 | public class NoteSnap {
10 | private String id;
11 |
12 | private long time;
13 |
14 | private List paragraphs;
15 |
16 | private int selectionStart;
17 |
18 | private int selectionEnd;
19 |
20 | private boolean isContinuousAction;
21 |
22 | public String getId() {
23 | return id;
24 | }
25 |
26 | public void setId(String id) {
27 | this.id = id;
28 | }
29 |
30 | public long getTime() {
31 | return time;
32 | }
33 |
34 | public void setTime(long time) {
35 | this.time = time;
36 | }
37 |
38 | public List getParagraphs() {
39 | return paragraphs;
40 | }
41 |
42 | public void setParagraphs(List paragraphs) {
43 | this.paragraphs = paragraphs;
44 | }
45 |
46 | public int getSelectionStart() {
47 | return selectionStart;
48 | }
49 |
50 | public void setSelectionStart(int selectionStart) {
51 | this.selectionStart = selectionStart;
52 | }
53 |
54 | public int getSelectionEnd() {
55 | return selectionEnd;
56 | }
57 |
58 | public void setSelectionEnd(int selectionEnd) {
59 | this.selectionEnd = selectionEnd;
60 | }
61 |
62 | public void setSelection(int start, int end) {
63 | this.selectionStart = start;
64 | this.selectionEnd = end;
65 | }
66 |
67 | public void setContinuousAction(boolean isContinuousAction) {
68 | this.isContinuousAction = true;
69 | }
70 |
71 | public boolean isContinuousAction() {
72 | return isContinuousAction;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/model/NoteSnapBuilder.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.model;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * Created By Chengjunsen on 2019/3/18
8 | */
9 | public class NoteSnapBuilder {
10 |
11 | private NoteSnap snap;
12 |
13 | public NoteSnapBuilder(Note note) {
14 | snap = new NoteSnap();
15 | snap.setId(note.getId());
16 |
17 | List list = new ArrayList<>();
18 | for (Paragraph item: note.getParagraphs()) {
19 | list.add(item.clone());
20 | }
21 | snap.setParagraphs(list);
22 | }
23 |
24 | public NoteSnapBuilder selection(int start, int end) {
25 | snap.setSelection(start, end);
26 | return this;
27 | }
28 |
29 | public NoteSnap build() {
30 | snap.setTime(System.currentTimeMillis());
31 | return snap;
32 | }
33 |
34 | public NoteSnapBuilder continuous(boolean continuousAction) {
35 | snap.setContinuousAction(true);
36 | return this;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/model/OutLine.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.model;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/2/21
5 | */
6 | public class OutLine {
7 | public enum Type {
8 | Note, Date
9 | }
10 |
11 | private Note note;
12 |
13 | private long time;
14 |
15 | private Type type;
16 |
17 | public OutLine(Note note) {
18 | this.type = Type.Note;
19 | this.note = note;
20 | }
21 |
22 | public OutLine(long time) {
23 | this.type = Type.Date;
24 | this.time = time;
25 | }
26 |
27 | public Note getNote() {
28 | return note;
29 | }
30 |
31 | public void setNote(Note note) {
32 | this.note = note;
33 | }
34 |
35 | public long getTime() {
36 | return time;
37 | }
38 |
39 | public void setTime(long time) {
40 | this.time = time;
41 | }
42 |
43 | public Type getType() {
44 | return type;
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/model/ParagraphBuilder.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.model;
2 |
3 | import java.util.List; /**
4 | * Created By Chengjunsen on 2019/3/13
5 | */
6 | public class ParagraphBuilder {
7 |
8 | private Paragraph paragraph;
9 |
10 | public ParagraphBuilder() {
11 | paragraph = new Paragraph();
12 | paragraph.setDirty(true);
13 | }
14 |
15 | public ParagraphBuilder lineStyle(int lineStyle) {
16 | paragraph.setLineStyle(lineStyle);
17 | if (paragraph.isCheckbox()) {
18 | paragraph.setUnCheckbox(true);
19 | }
20 | return this;
21 | }
22 |
23 | public Paragraph build() {
24 | if (paragraph.isHeadStyle() || paragraph.isParagraphStyle()) {
25 | paragraph.insertPlaceHolder();
26 | } else {
27 | paragraph.removePlaceHolder();
28 | }
29 | return paragraph;
30 | }
31 |
32 | public ParagraphBuilder indentation(int indentation) {
33 | paragraph.setIndentation(indentation);
34 | return this;
35 | }
36 |
37 | public ParagraphBuilder dividingLine(boolean dividingLine) {
38 | paragraph.setDividingLine(dividingLine);
39 | return this;
40 | }
41 |
42 | public ParagraphBuilder image(String url) {
43 | paragraph.setImage(url);
44 | paragraph.setImage(true);
45 | return this;
46 | }
47 |
48 | public ParagraphBuilder image(boolean b) {
49 | paragraph.setImage(b);
50 | return this;
51 | }
52 |
53 | public ParagraphBuilder bullet(boolean b) {
54 | paragraph.setBulletList(b);
55 | return this;
56 | }
57 |
58 | public ParagraphBuilder addWords(String words, List wordStyles) {
59 | paragraph.addWords(words, wordStyles);
60 | return this;
61 | }
62 |
63 | public ParagraphBuilder numList(boolean b) {
64 | paragraph.setNumList(b);
65 | return this;
66 | }
67 |
68 | public ParagraphBuilder uncheckBox(boolean b) {
69 | paragraph.setUnCheckbox(b);
70 | return this;
71 | }
72 |
73 | public ParagraphBuilder checkbox(boolean b) {
74 | paragraph.setCheckbox(b);
75 | return this;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/model/Style.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.model;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/3/1
5 | */
6 | public class Style {
7 | public static final int Bold = 1;
8 |
9 | public static final int Italic = 2;
10 |
11 | public static final int UnderLine = 4;
12 |
13 | public static final int Strikethrough = 8;
14 |
15 | public static final int BackgroudColor = 16;
16 |
17 | public static final int SuperScript = 32;
18 |
19 | public static final int SubScript = 64;
20 |
21 | public static final int CheckBox = 1;
22 |
23 | public static final int UnCheckBox = 2;
24 |
25 | public static final int NumList = 3;
26 |
27 | public static final int BulletList = 4;
28 |
29 | public static final int DividingLine = 5;
30 |
31 | public static final int Image = 6;
32 |
33 | public static boolean isWordStyle(int style, int flag) {
34 | return (style & flag) == flag;
35 | }
36 |
37 | public static int setWordStyle(int style, boolean b, int flag) {
38 | return b ? style | flag : (style | flag) ^ flag;
39 | }
40 |
41 | public static int setLineStyle(int style, boolean b, int flag) {
42 | style = b ? flag : (style == flag ? 0 : style);
43 | return style;
44 | }
45 |
46 | public static boolean isLineStyle(int style, int flag) {
47 | return style == flag;
48 | }
49 |
50 | public static int clearLineStyle(int style) {
51 | return 0;
52 | }
53 |
54 | public static int clearWordStyle(int style) {
55 | return 0;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/mvp/IModel.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.mvp;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/2/20
5 | */
6 | public interface IModel {
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/mvp/IPresenter.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.mvp;
2 |
3 | import android.arch.lifecycle.Lifecycle;
4 | import android.arch.lifecycle.LifecycleObserver;
5 | import android.arch.lifecycle.LifecycleOwner;
6 | import android.arch.lifecycle.OnLifecycleEvent;
7 |
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | /**
11 | * Created By Chengjunsen on 2019/2/20
12 | */
13 | public abstract class IPresenter implements LifecycleObserver {
14 |
15 | @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
16 | protected abstract void onCreate(@NotNull LifecycleOwner owner);
17 |
18 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
19 | protected abstract void onDestroy(@NotNull LifecycleOwner owner);
20 |
21 | @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
22 | protected void onLifecycleChanged(@NotNull LifecycleOwner owner, @NotNull Lifecycle.Event event) {
23 | switch (event) {
24 | case ON_RESUME:
25 | onResume();
26 | break;
27 | case ON_START:
28 | onStart();
29 | break;
30 | case ON_STOP:
31 | onStop();
32 | break;
33 | case ON_PAUSE:
34 | onPause();
35 | break;
36 | default:
37 | break;
38 | }
39 | }
40 |
41 | protected void onResume() {
42 |
43 | }
44 |
45 | protected void onStart() {
46 |
47 | }
48 |
49 | protected void onPause() {
50 |
51 | }
52 |
53 | protected void onStop() {
54 |
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/mvp/IView.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.mvp;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/2/20
5 | */
6 | public interface IView {
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/note_edit/EditNoteFormatDialog.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.note_edit;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.view.Gravity;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import com.jscheng.srich.R;
10 |
11 | public class EditNoteFormatDialog extends Dialog implements View.OnClickListener{
12 |
13 | private EditNotePresenter mPresenter;
14 |
15 | public EditNoteFormatDialog(Context context, EditNotePresenter presenter) {
16 | super(context, R.style.EditNoteBottomDialogTheme);
17 | setContentView(R.layout.edit_note_bottom_dialog);
18 | this.mPresenter = presenter;
19 |
20 | getWindow().setGravity(Gravity.BOTTOM);
21 | getWindow().setWindowAnimations(R.style.EditNoteBottomDialogAnimTheme);
22 | getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
23 | findViewById(R.id.dialog_album).setOnClickListener(this);
24 | findViewById(R.id.dialog_random).setOnClickListener(this);
25 | findViewById(R.id.dialog_network).setOnClickListener(this);
26 | findViewById(R.id.dialog_cancel).setOnClickListener(this);
27 | }
28 |
29 | @Override
30 | public void onClick(View v) {
31 | switch (v.getId()) {
32 | case R.id.dialog_album:
33 | mPresenter.tapAlbum();
34 | this.dismiss();
35 | break;
36 | case R.id.dialog_network:
37 | mPresenter.tapNetworkUrl();
38 | this.dismiss();
39 | break;
40 | case R.id.dialog_random:
41 | mPresenter.tapRandomUrl();
42 | this.dismiss();
43 | case R.id.dialog_cancel:
44 | this.dismiss();
45 | break;
46 | default:
47 | break;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/note_edit/EditNoteInputDialog.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.note_edit;
2 |
3 | import android.app.Activity;
4 | import android.app.Dialog;
5 | import android.content.Context;
6 | import android.support.v7.widget.AppCompatEditText;
7 | import android.support.v7.widget.AppCompatTextView;
8 | import android.text.TextUtils;
9 | import android.util.DisplayMetrics;
10 | import android.view.Gravity;
11 | import android.view.View;
12 | import android.view.Window;
13 | import android.view.WindowManager;
14 | import android.view.inputmethod.InputMethodManager;
15 | import android.widget.Toast;
16 |
17 | import com.jscheng.srich.R;
18 |
19 | /**
20 | * Created By Chengjunsen on 2019/3/19
21 | */
22 | public class EditNoteInputDialog implements View.OnClickListener{
23 | private Dialog dialog;
24 | private Context context;
25 | private AppCompatTextView titleTextView;
26 | private AppCompatEditText contentEditText;
27 | private EditNotePresenter presenter;
28 |
29 | public EditNoteInputDialog(Context context, EditNotePresenter presenter) {
30 | this.context = context;
31 | this.presenter = presenter;
32 | init();
33 | }
34 |
35 | public String getTitle() {
36 | return titleTextView.getText().toString();
37 |
38 | }
39 |
40 | public void setTitle(String title) {
41 | titleTextView.setText(title);
42 | }
43 |
44 | public String getContent() {
45 | return contentEditText.getText().toString();
46 | }
47 |
48 | public void setContent(String content) {
49 | contentEditText.setText(content);
50 | }
51 |
52 | public void dismiss() {
53 | dialog.dismiss();
54 | }
55 |
56 | private void init() {
57 | this.dialog = new Dialog(context);
58 | dialog.setContentView(R.layout.edit_note_input_dialog);
59 | dialog.getWindow().getDecorView().setPadding(0, 0, 0, 0);
60 | Window window = dialog.getWindow();
61 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
62 | WindowManager.LayoutParams layoutParams = window.getAttributes();
63 | dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
64 | window.setGravity(Gravity.CENTER | Gravity.BOTTOM);
65 | layoutParams.width = (int) (displayMetrics.widthPixels * 1);
66 | window.setAttributes(layoutParams);
67 | titleTextView = dialog.findViewById(R.id.titleTextView);
68 | titleTextView.setOnClickListener(this);
69 | contentEditText = dialog.findViewById(R.id.contentEditText);
70 | dialog.findViewById(R.id.cancelTextView).setOnClickListener(this);
71 | dialog.findViewById(R.id.confirmTextView).setOnClickListener(this);
72 | titleTextView.setText("输入");
73 | contentEditText.post(new Runnable() {
74 | @Override
75 | public void run() {
76 | InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
77 | inputMethodManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
78 | }
79 | });
80 | }
81 |
82 | public void show() {
83 | dialog.show();
84 | }
85 |
86 | @Override
87 | public void onClick(View v) {
88 | switch (v.getId()) {
89 | case R.id.confirmTextView:
90 | confirm();
91 | break;
92 | case R.id.cancelTextView:
93 | dismiss();
94 | break;
95 | default:
96 | break;
97 | }
98 | }
99 |
100 | private void confirm() {
101 | String url = getContent();
102 | if (TextUtils.isEmpty(url)) {
103 | Toast.makeText(context, "输入不能为空", Toast.LENGTH_SHORT).show();
104 | return;
105 | }
106 |
107 | if (!url.startsWith("http")){
108 | Toast.makeText(context, "链接不合法", Toast.LENGTH_SHORT).show();
109 | return;
110 | }
111 |
112 | presenter.tapInsertUrl(url);
113 | dismiss();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/note_edit/EditNoteMode.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.note_edit;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/3/11
5 | */
6 |
7 | public enum EditNoteMode {
8 | /**
9 | * 写模式
10 | */
11 | Writing,
12 |
13 | /**
14 | * 读模式
15 | */
16 | Reading,
17 |
18 | /**
19 | * 加载
20 | */
21 | Loading
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/note_edit/FloatEditButton.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.note_edit;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.Drawable;
5 | import android.util.AttributeSet;
6 | import android.view.View;
7 | import android.view.animation.AlphaAnimation;
8 | import android.view.animation.Animation;
9 | import android.view.animation.AnimationSet;
10 | import android.view.animation.TranslateAnimation;
11 |
12 | import com.jscheng.srich.R;
13 |
14 | /**
15 | * Created By Chengjunsen on 2019/2/22
16 | */
17 | public class FloatEditButton extends android.support.v7.widget.AppCompatImageView implements View.OnClickListener{
18 | private static final String NameSpace = "http://schemas.android.com/apk/res/android";
19 | private EditNotePresenter mPresenter;
20 |
21 | public FloatEditButton(Context context) {
22 | this(context, null);
23 | }
24 |
25 | public FloatEditButton(Context context, AttributeSet attrs) {
26 | this(context, attrs, 0);
27 | }
28 |
29 | public FloatEditButton(Context context, AttributeSet attrs, int defStyleAttr) {
30 | super(context, attrs, defStyleAttr);
31 | int srcResource = attrs.getAttributeResourceValue(NameSpace, "src", 0);
32 | init(context, srcResource);
33 | }
34 |
35 | private void init(Context context, int src) {
36 | this.setClickable(true);
37 | super.setOnClickListener(this);
38 | if (src == 0) {
39 | Drawable defaultDrawable = context.getResources().getDrawable(R.mipmap.ic_note_edit_edit, null);
40 | this.setScaleType(ScaleType.CENTER_CROP);
41 | this.setImageDrawable(defaultDrawable);
42 | }
43 | }
44 |
45 | public void setPresenter(EditNotePresenter mPresenter) {
46 | this.mPresenter = mPresenter;
47 | }
48 |
49 | @Override
50 | public void onClick(final View v) {
51 | checkPresenter();
52 | mPresenter.tapEdit();
53 | }
54 |
55 | public void show() {
56 | setVisibility(VISIBLE);
57 | }
58 |
59 | public void hide() {
60 | setVisibility(GONE);
61 | }
62 |
63 | private AnimationSet getHideAniamtion() {
64 | AnimationSet animationSet = new AnimationSet(true);
65 | animationSet.addAnimation(new TranslateAnimation(0, 0, 0, getMeasuredHeight()));
66 | animationSet.addAnimation(new AlphaAnimation(getAlpha(), getAlpha()/2));
67 | animationSet.setDuration(500);
68 | return animationSet;
69 | }
70 |
71 | private AnimationSet getShowAniamtion() {
72 | AnimationSet animationSet = new AnimationSet(true);
73 | animationSet.addAnimation(new TranslateAnimation(0, 0, getMeasuredHeight(), 0));
74 | animationSet.addAnimation(new AlphaAnimation(getAlpha()/2, getAlpha()));
75 | animationSet.setDuration(300);
76 | return animationSet;
77 | }
78 |
79 | private void checkPresenter() {
80 | if (mPresenter == null) {
81 | throw new RuntimeException("you need to call setPresenter() at first");
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/outline/FloatNewButton.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.outline;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.Drawable;
5 | import android.support.annotation.Nullable;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 | import android.view.animation.Animation;
9 | import android.view.animation.RotateAnimation;
10 |
11 | import com.jscheng.srich.R;
12 |
13 | /**
14 | * Created By Chengjunsen on 2019/2/21
15 | */
16 | public class FloatNewButton extends android.support.v7.widget.AppCompatImageView implements View.OnClickListener{
17 |
18 | private static final String NameSpace = "http://schemas.android.com/apk/res/android";
19 |
20 | private OnClickListener mClickListener = null;
21 |
22 | private Animation mAnimation = null;
23 |
24 | public FloatNewButton(Context context) {
25 | this(context, null);
26 | }
27 |
28 | public FloatNewButton(Context context, AttributeSet attrs) {
29 | this(context, attrs, 0);
30 | }
31 |
32 | public FloatNewButton(Context context, AttributeSet attrs, int defStyleAttr) {
33 | super(context, attrs, defStyleAttr);
34 | int srcResource = attrs.getAttributeResourceValue(NameSpace, "src", 0);
35 | init(context, srcResource);
36 | }
37 |
38 | private void init(Context context, int src) {
39 | this.setClickable(true);
40 | super.setOnClickListener(this);
41 | if (src == 0) {
42 | Drawable defaultDrawable =context.getResources().getDrawable(R.mipmap.ic_compose, null);
43 | this.setScaleType(ScaleType.CENTER_CROP);
44 | this.setImageDrawable(defaultDrawable);
45 | }
46 |
47 | this.mAnimation = new RotateAnimation(0, 90,
48 | Animation.RELATIVE_TO_SELF,0.5f,
49 | Animation.RELATIVE_TO_SELF,0.5f);
50 |
51 | this.mAnimation.setDuration(500);
52 | }
53 |
54 | @Override
55 | public void setOnClickListener(@Nullable OnClickListener listener) {
56 | this.mClickListener = listener;
57 | }
58 |
59 | @Override
60 | public void onClick(final View v) {
61 | this.startAnimation(mAnimation);
62 | mAnimation.setAnimationListener(new Animation.AnimationListener() {
63 | @Override
64 | public void onAnimationStart(Animation animation) { }
65 |
66 | @Override
67 | public void onAnimationEnd(Animation animation) {
68 | if (mClickListener != null) {
69 | mClickListener.onClick(v);
70 | }
71 | }
72 |
73 | @Override
74 | public void onAnimationRepeat(Animation animation) { }
75 | });
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/outline/OutLineCenterDialog.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.outline;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.support.annotation.NonNull;
6 | import android.view.Gravity;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | import com.jscheng.srich.R;
11 |
12 | /**
13 | * Created By Chengjunsen on 2019/3/19
14 | */
15 | public class OutLineCenterDialog extends Dialog implements View.OnClickListener{
16 |
17 | private OutLinePresenter mPresenter;
18 | private String noteid;
19 |
20 | public OutLineCenterDialog(@NonNull Context context, OutLinePresenter mPresenter) {
21 | super(context);
22 | setContentView(R.layout.outline_center_dialog);
23 | this.mPresenter = mPresenter;
24 |
25 | getWindow().setGravity(Gravity.CENTER);
26 | getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
27 | findViewById(R.id.delete_view).setOnClickListener(this);
28 | }
29 |
30 | public void show(String noteid) {
31 | this.noteid = noteid;
32 | super.show();
33 | }
34 |
35 | @Override
36 | public void onClick(View v) {
37 | switch (v.getId()) {
38 | case R.id.delete_view:
39 | mPresenter.tapDelete(noteid);
40 | super.dismiss();
41 | break;
42 | default:
43 | break;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/outline/OutLineModel.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.outline;
2 |
3 | import com.jscheng.srich.model.Note;
4 | import com.jscheng.srich.model.OutLine;
5 | import com.jscheng.srich.utils.DateUtil;
6 |
7 | import java.util.ArrayList;
8 | import java.util.Collections;
9 | import java.util.Comparator;
10 | import java.util.List;
11 |
12 | /**
13 | * Created By Chengjunsen on 2019/2/21
14 | */
15 | public class OutLineModel {
16 |
17 | public static List build(List notes) {
18 | List outLines = new ArrayList<>();
19 | Collections.sort(notes, new Comparator() {
20 | @Override
21 | public int compare(Note note1, Note note2) {
22 | return (int)(note2.getModifyTime() - note1.getModifyTime());
23 | }
24 | });
25 |
26 | OutLine newDateOutLine = null;
27 | for (int i = 0; i < notes.size(); i++) {
28 | Note note = notes.get(i);
29 | if (newDateOutLine == null || !DateUtil.isSameDate(newDateOutLine.getTime(), note.getModifyTime())) {
30 | newDateOutLine = buildDateOutLine(note);
31 | outLines.add(newDateOutLine);
32 | }
33 | outLines.add(new OutLine(note));
34 | }
35 | return outLines;
36 | }
37 |
38 | private static OutLine buildDateOutLine(Note note) {
39 | return new OutLine(note.getModifyTime());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/outline/OutLinePresenter.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.outline;
2 |
3 | import android.arch.lifecycle.LifecycleOwner;
4 | import android.content.Context;
5 |
6 | import com.jscheng.srich.model.NoteModel;
7 | import com.jscheng.srich.model.Note;
8 | import com.jscheng.srich.mvp.IPresenter;
9 | import com.jscheng.srich.mvp.IView;
10 | import com.jscheng.srich.route.Router;
11 |
12 | import org.jetbrains.annotations.NotNull;
13 | import java.util.List;
14 |
15 | import io.reactivex.Observable;
16 | import io.reactivex.ObservableEmitter;
17 | import io.reactivex.ObservableOnSubscribe;
18 | import io.reactivex.android.schedulers.AndroidSchedulers;
19 | import io.reactivex.functions.Consumer;
20 | import io.reactivex.schedulers.Schedulers;
21 |
22 | /**
23 | * Created By Chengjunsen on 2019/2/20
24 | */
25 | public class OutLinePresenter extends IPresenter {
26 | private OutLineView mView;
27 |
28 | public interface OutLineView extends IView {
29 | void setData(List ntoes);
30 | void showCenterDialog(String id);
31 | }
32 |
33 | @Override
34 | public void onCreate(@NotNull LifecycleOwner owner) {
35 | this.mView = (OutLineView) owner;
36 | }
37 |
38 | @Override
39 | public void onDestroy(@NotNull LifecycleOwner owner) {
40 | this.mView = null;
41 | }
42 |
43 | @Override
44 | protected void onResume() {
45 | this.reload();
46 | }
47 |
48 | public void reload() {
49 | Observable.create(new ObservableOnSubscribe>() {
50 | @Override
51 | public void subscribe(ObservableEmitter> emitter) throws Exception {
52 | List notes = NoteModel.getNotes((Context)mView);
53 | emitter.onNext(notes);
54 | }
55 | }).subscribeOn(Schedulers.io())
56 | .observeOn(AndroidSchedulers.mainThread())
57 | .subscribe(new Consumer>() {
58 | @Override
59 | public void accept(List notes) throws Exception {
60 | mView.setData(notes);
61 | }
62 | });
63 | }
64 |
65 | public void tapNew() {
66 | Router.with((Context)mView).route("editnote").go();
67 | }
68 |
69 | public void tapNote(String id) {
70 | Router.with((Context)mView).route("editnote").intent("id", id).go();
71 | }
72 |
73 | public void taplongNote(String id) {
74 | mView.showCenterDialog(id);
75 | }
76 |
77 | public void tapDelete(String id) {
78 | NoteModel.deleteNote((Context)mView, id);
79 | reload();
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/outline/OutLinesActivity.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.outline;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import android.support.v4.widget.SwipeRefreshLayout;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.view.View;
10 | import android.widget.RelativeLayout;
11 | import android.widget.TextView;
12 |
13 | import com.jscheng.annotations.Route;
14 | import com.jscheng.srich.BaseActivity;
15 | import com.jscheng.srich.R;
16 | import com.jscheng.srich.model.Note;
17 | import com.jscheng.srich.utils.DateUtil;
18 |
19 | import java.util.List;
20 |
21 | /**
22 | * 预览页
23 | * Created By Chengjunsen on 2019/2/20
24 | */
25 | @Route("outline")
26 | public class OutLinesActivity extends BaseActivity
27 | implements OutLinePresenter.OutLineView, View.OnClickListener, SwipeRefreshLayout.OnRefreshListener{
28 | private final static String TAG = "OutLinesActivity";
29 | private OutLinePresenter mPresenter;
30 | private RecyclerView mRecyclerView;
31 | private LinearLayoutManager mLayoutManager;
32 | private OutLinesAdapter mRecyclerAdapter ;
33 | private RelativeLayout mHeadDateLayout;
34 | private TextView mHeadDateText;
35 | private FloatNewButton mFloatButton;
36 | private SwipeRefreshLayout mSwipeLayout;
37 | private OutLineCenterDialog mCenterDialog;
38 |
39 | @Override
40 | protected void onCreate(@Nullable Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.activity_outline);
43 |
44 | this.mPresenter = new OutLinePresenter();
45 | this.getLifecycle().addObserver(mPresenter);
46 |
47 | this.mHeadDateLayout = findViewById(R.id.outline_head_date);
48 | this.mHeadDateText = mHeadDateLayout.findViewById(R.id.date_text);
49 | this.mFloatButton = findViewById(R.id.float_new_button);
50 | this.mFloatButton.setOnClickListener(this);
51 | this.mSwipeLayout = findViewById(R.id.swipe_layout);
52 | this.mSwipeLayout.setOnRefreshListener(this);
53 |
54 | this.mRecyclerView = findViewById(R.id.outline_recyclerview);
55 | this.mLayoutManager = new LinearLayoutManager(this,
56 | LinearLayoutManager.VERTICAL, false);
57 | this.mRecyclerAdapter = new OutLinesAdapter(mPresenter, mRecyclerView, mLayoutManager);
58 | this.mRecyclerView.setLayoutManager(mLayoutManager);
59 | this.mRecyclerView.setAdapter(mRecyclerAdapter);
60 | this.mRecyclerView.addOnScrollListener(new ScrollChangeListener());
61 | }
62 |
63 | @Override
64 | public void setData(List notes) {
65 | mSwipeLayout.setRefreshing(false);
66 | mRecyclerAdapter.setData(notes);
67 | }
68 |
69 | @Override
70 | public void showCenterDialog(String id) {
71 | if (mCenterDialog == null) {
72 | mCenterDialog = new OutLineCenterDialog(this, mPresenter);
73 | }
74 | mCenterDialog.show(id);
75 | }
76 |
77 | private void tapNewButton() {
78 | mPresenter.tapNew();
79 | }
80 |
81 | @Override
82 | public void onClick(View v) {
83 | switch (v.getId()) {
84 | case R.id.float_new_button:
85 | tapNewButton();
86 | break;
87 | default:
88 | break;
89 | }
90 | }
91 |
92 | @Override
93 | public void onRefresh() {
94 | mPresenter.reload();
95 | }
96 |
97 | private class ScrollChangeListener extends RecyclerView.OnScrollListener {
98 | @Override
99 | public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
100 | super.onScrolled(recyclerView, dx, dy);
101 | updateHeadDateLayout();
102 | }
103 | }
104 |
105 | private void updateHeadDateLayout() {
106 | long time = mRecyclerAdapter.getFirstVisibleDateTime();
107 | if (time > 0) {
108 | mHeadDateLayout.setVisibility(View.VISIBLE);
109 | mHeadDateText.setText(DateUtil.formatDate(time));
110 | } else {
111 | mHeadDateLayout.setVisibility(View.INVISIBLE);
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/revoke/NoteRevocationManager.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.revoke;
2 |
3 | import com.jscheng.srich.model.Note;
4 | import com.jscheng.srich.model.NoteSnap;
5 | import com.jscheng.srich.model.NoteSnapBuilder;
6 |
7 | import java.util.LinkedList;
8 |
9 | /**
10 | * Created By Chengjunsen on 2019/3/18
11 | * 撤销与反撤销
12 | */
13 | public class NoteRevocationManager {
14 |
15 | /**
16 | * 最大撤销步数
17 | */
18 | private final static int MAX_REVOCATION_STEP_COUNT = 10;
19 |
20 | /**
21 | * 最大间隔时间,可当做是同一动作
22 | */
23 | private final static int MAX_INTERVER_MILLTIME = 1000;
24 |
25 | /**
26 | * 撤销栈
27 | */
28 | private LinkedList mRevocationList;
29 |
30 | /**
31 | * 恢复栈
32 | */
33 | private LinkedList mRecoveryList;
34 |
35 | /**
36 | * 可连续的动作
37 | */
38 | private boolean isContinuousAction;
39 |
40 | private NoteSnap currentSnap;
41 |
42 | private boolean isRunning;
43 |
44 | public NoteRevocationManager() {
45 | this.mRecoveryList = new LinkedList<>();
46 | this.mRevocationList = new LinkedList<>();
47 | this.isRunning = false;
48 | this.isContinuousAction = false;
49 | this.currentSnap = null;
50 | }
51 |
52 | /**
53 | * 恢复
54 | */
55 | public NoteSnap recover(Note note, int selectionBegin, int selectionEnd) {
56 | if (mRecoveryList.isEmpty()){
57 | return null;
58 | }
59 |
60 | NoteSnap currentSnap = new NoteSnapBuilder(note)
61 | .continuous(isContinuousAction)
62 | .selection(selectionBegin, selectionEnd)
63 | .build();
64 | mRevocationList.push(currentSnap);
65 | return mRecoveryList.pop();
66 | }
67 |
68 | /**
69 | * 撤销
70 | */
71 | public NoteSnap revoke(Note note, int selectionBegin, int selectionEnd) {
72 | if (mRevocationList.isEmpty()) {
73 | return null;
74 | }
75 | NoteSnap currentSnap = new NoteSnapBuilder(note)
76 | .continuous(isContinuousAction)
77 | .selection(selectionBegin, selectionEnd)
78 | .build();
79 | mRecoveryList.push(currentSnap);
80 | return mRevocationList.pop();
81 | }
82 |
83 | /**
84 | * 是否可以恢复
85 | */
86 | public boolean isCanRecover() {
87 | return mRecoveryList.size() > 0;
88 | }
89 |
90 | /**
91 | * 是否可以撤销
92 | */
93 | public boolean isCanRevoke() {
94 | return mRevocationList.size() > 0;
95 | }
96 |
97 | /**
98 | * 开始动作
99 | */
100 | public void beginAction(Note note, int selectionBegin, int selectionEnd, boolean isContinuous) {
101 | if (isRunning) {
102 | throw new RuntimeException("you should end action before");
103 | }
104 | isRunning = true;
105 | isContinuousAction = isContinuous;
106 |
107 | currentSnap = new NoteSnapBuilder(note)
108 | .continuous(isContinuousAction)
109 | .selection(selectionBegin, selectionEnd)
110 | .build();
111 | }
112 |
113 | /**
114 | * 结束动作
115 | */
116 | public void endAction() {
117 | if (!isRunning) {
118 | throw new RuntimeException("you should begin action before");
119 | }
120 | isRunning = false;
121 | mRecoveryList.clear();
122 |
123 | if (!mRevocationList.isEmpty()) {
124 | NoteSnap lastSnap = mRevocationList.getLast();
125 | if (lastSnap.isContinuousAction() && isContinuousAction &&
126 | Math.abs(currentSnap.getTime() - lastSnap.getTime()) < MAX_INTERVER_MILLTIME) {
127 | return;
128 | }
129 | }
130 |
131 | mRevocationList.push(currentSnap);
132 | checkMaxActions();
133 | }
134 |
135 | private void checkMaxActions() {
136 | while (mRevocationList.size() > MAX_REVOCATION_STEP_COUNT) {
137 | mRevocationList.removeLast();
138 | }
139 | }
140 |
141 | public boolean isRunning() {
142 | return isRunning;
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/route/ActivityInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.route;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 |
8 | /**
9 | * Created By Chengjunsen on 2019/2/21
10 | */
11 | public class ActivityInterceptor implements RouterInterceptor {
12 |
13 | @Override
14 | public RouterResponse process(RouterChain chain) {
15 | RouterRequest request = chain.getRequest();
16 | RouterResponse response = chain.proceed(request);
17 | if (response.isDone() || !isAimInstance(response.getCls())) {
18 | return response;
19 | }
20 | return doRequst(request, response);
21 | }
22 |
23 | private boolean isAimInstance(Class cls) {
24 | if (cls == null) {
25 | return false;
26 | }
27 | boolean isActivity = Activity.class.isAssignableFrom(cls);
28 | return isActivity;
29 | }
30 |
31 | private RouterResponse doRequst(RouterRequest request, RouterResponse response) {
32 | Class activityCls = response.getCls();
33 | Context context = request.getContext();
34 | Bundle bundle = request.getBundle();
35 | Intent intent = new Intent(context, activityCls);
36 |
37 | response.setIntent(intent);
38 | response.setDone(true);
39 |
40 | if (request.isJump()) {
41 | intent.putExtras(bundle);
42 | context.startActivity(intent);
43 | }
44 | return response;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/route/IRouteInfo.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.route;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/3/18
7 | */
8 | public interface IRouteInfo {
9 | void load(Map routes);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/route/LogInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.route;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/2/21
7 | */
8 | public class LogInterceptor implements RouterInterceptor {
9 | private static final String TAG= "RouterLog";
10 | @Override
11 | public RouterResponse process(RouterChain chain) {
12 | RouterRequest request = chain.getRequest();
13 | Log.d(TAG, request.getUri());
14 | RouterResponse response = chain.proceed(request);
15 | return response;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/route/RouteInfoUtil.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.route;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.content.pm.ApplicationInfo;
6 | import android.content.pm.PackageManager;
7 | import android.os.Build;
8 | import android.text.TextUtils;
9 |
10 | import java.io.IOException;
11 | import java.util.ArrayList;
12 | import java.util.Arrays;
13 | import java.util.Enumeration;
14 | import java.util.HashSet;
15 | import java.util.List;
16 | import java.util.Set;
17 | import java.util.concurrent.ArrayBlockingQueue;
18 | import java.util.concurrent.CountDownLatch;
19 | import java.util.concurrent.ThreadPoolExecutor;
20 | import java.util.concurrent.TimeUnit;
21 |
22 | import dalvik.system.DexFile;
23 |
24 | /**
25 | * Created By Chengjunsen on 2019/3/18
26 | */
27 | public class RouteInfoUtil {
28 | /**
29 | * 获得程序所有的apk(instant run会产生很多split apk)
30 | * @param context
31 | * @return
32 | * @throws PackageManager.NameNotFoundException
33 | */
34 | private static List getSourcePaths(Context context) throws PackageManager.NameNotFoundException {
35 | ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
36 | List sourcePaths = new ArrayList<>();
37 | sourcePaths.add(applicationInfo.sourceDir);
38 | //instant run
39 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
40 | if (null != applicationInfo.splitSourceDirs) {
41 | sourcePaths.addAll(Arrays.asList(applicationInfo.splitSourceDirs));
42 | }
43 | }
44 | return sourcePaths;
45 | }
46 |
47 | /**
48 | * 得到路由表的类名
49 | * @param context
50 | * @param packageName
51 | * @return
52 | * @throws PackageManager.NameNotFoundException
53 | * @throws InterruptedException
54 | */
55 | public static Set getFileNameByPackageName(Application context, final String packageName)
56 | throws PackageManager.NameNotFoundException, InterruptedException {
57 | final Set classNames = new HashSet<>();
58 | List paths = getSourcePaths(context);
59 | //使用同步计数器判断均处理完成
60 | final CountDownLatch countDownLatch = new CountDownLatch(paths.size());
61 | ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
62 | paths.size(),
63 | paths.size(),
64 | 30,
65 | TimeUnit.SECONDS, new
66 | ArrayBlockingQueue(64));
67 |
68 | for (final String path : paths) {
69 | threadPoolExecutor.execute(new Runnable() {
70 | @Override
71 | public void run() {
72 | DexFile dexFile = null;
73 | try {
74 | //加载 apk中的dex 并遍历 获得所有包名为 {packageName} 的类
75 | dexFile = new DexFile(path);
76 | Enumeration dexEntries = dexFile.entries();
77 | while (dexEntries.hasMoreElements()) {
78 | String className = dexEntries.nextElement();
79 | if (!TextUtils.isEmpty(className) && className.startsWith(packageName)) {
80 | classNames.add(className);
81 | }
82 | }
83 | } catch (IOException e) {
84 | e.printStackTrace();
85 | } finally {
86 | if (null != dexFile) {
87 | try {
88 | dexFile.close();
89 | } catch (IOException e) {
90 | e.printStackTrace();
91 | }
92 | }
93 | //释放一个
94 | countDownLatch.countDown();
95 | }
96 | }
97 | });
98 | }
99 | //等待执行完成
100 | countDownLatch.await();
101 | return classNames;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/route/Router.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.route;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.content.pm.PackageManager;
6 |
7 | import java.lang.reflect.InvocationTargetException;
8 | import java.util.ArrayList;
9 | import java.util.HashMap;
10 | import java.util.List;
11 | import java.util.Map;
12 | import java.util.Set;
13 |
14 | /**
15 | * 基于APT写的简单路由(可去掉)
16 | * Created By Chengjunsen on 2019/2/21
17 | */
18 | public class Router {
19 | private static final String ROUTE_ROOT_PAKCAGE = "com.jscheng.processor";
20 |
21 | private static HashMap mRouteTable = new HashMap<>();
22 |
23 | private static List mInterceptor = new ArrayList<>();
24 |
25 | public static void addUri(String uri, Class cls) {
26 | mRouteTable.put(uri, cls);
27 | }
28 |
29 | public static void addInterceptor(RouterInterceptor interceptor) {
30 | mInterceptor.add(interceptor);
31 | }
32 |
33 | public static Map getRouteTable() {
34 | return mRouteTable;
35 | }
36 |
37 | public static List getInterceptor() {
38 | return mInterceptor;
39 | }
40 |
41 | public static RouterRequest with(Context context) {
42 | RouterRequest request = new RouterRequest(context);
43 | return request;
44 | }
45 |
46 | public static void init(Application application) {
47 | initInterceptor();
48 | initRoute(application);
49 | }
50 |
51 | private static void initInterceptor() {
52 | Router.addInterceptor(new LogInterceptor());
53 | Router.addInterceptor(new ActivityInterceptor());
54 | Router.addInterceptor(new UriAnalyzerInterceptor());
55 | }
56 |
57 | private static void initRoute(Application application) {
58 | try {
59 | Set routerMap = RouteInfoUtil.getFileNameByPackageName(application, ROUTE_ROOT_PAKCAGE);
60 | for (String className : routerMap) {
61 | ((IRouteInfo) Class.forName(className).getConstructor().newInstance()).load(mRouteTable);
62 | }
63 | } catch (PackageManager.NameNotFoundException |
64 | ClassNotFoundException |
65 | InterruptedException |
66 | InstantiationException |
67 | InvocationTargetException |
68 | NoSuchMethodException |
69 | IllegalAccessException e) {
70 | e.printStackTrace();
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/route/RouterChain.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.route;
2 |
3 | import java.util.List;
4 | import java.util.Stack;
5 |
6 | /**
7 | * Created By Chengjunsen on 2019/2/21
8 | */
9 | public class RouterChain {
10 |
11 | private RouterRequest request;
12 |
13 | private List interceptors;
14 |
15 | private int index;
16 |
17 | public RouterChain(RouterRequest request, List interceptors) {
18 | this.request = request;
19 | this.interceptors = interceptors;
20 | this.index = 0;
21 | }
22 |
23 | public RouterRequest getRequest() {
24 | return request;
25 | }
26 |
27 | public RouterResponse proceed(RouterRequest request) {
28 | this.request = request;
29 | if (index < 0 || index >= interceptors.size()) {
30 | return new RouterResponse();
31 | }
32 | return interceptors.get(index++).process(this);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/route/RouterInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.route;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/2/21
5 | */
6 | public interface RouterInterceptor {
7 |
8 | RouterResponse process(RouterChain chain);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/route/RouterRequest.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.route;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.text.TextUtils;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * Created By Chengjunsen on 2019/2/21
13 | */
14 | public class RouterRequest {
15 | private Context context;
16 | private String uri;
17 | private boolean isJump;
18 | private Bundle bundle;
19 |
20 | public RouterRequest(Context context) {
21 | this.context = context;
22 | this.isJump = false;
23 | this.bundle = new Bundle();
24 | }
25 |
26 | public RouterRequest route(String uri) {
27 | this.uri = uri;
28 | return this;
29 | }
30 |
31 | public RouterRequest intent(Intent intent) {
32 | this.bundle = intent.getExtras();
33 | return this;
34 | }
35 |
36 | public RouterRequest intent(String key, int what) {
37 | if (bundle == null) {
38 | bundle = new Bundle();
39 | }
40 | this.bundle.putInt(key, what);
41 | return this;
42 | }
43 |
44 | public RouterRequest intent(String key, String what) {
45 | if (bundle == null) {
46 | bundle = new Bundle();
47 | }
48 | this.bundle.putString(key, what);
49 | return this;
50 | }
51 |
52 | public RouterRequest intent(String key, List what) {
53 | if (bundle == null) {
54 | bundle = new Bundle();
55 | }
56 | this.bundle.putStringArrayList(key, new ArrayList(what));
57 | return this;
58 | }
59 |
60 |
61 | public RouterRequest intent(Bundle bundle) {
62 | if (bundle == null) {
63 | bundle = new Bundle();
64 | }
65 | this.bundle.putAll(bundle);
66 | return this;
67 | }
68 |
69 | public boolean go() {
70 | check();
71 | this.isJump = true;
72 | return process().isDone();
73 | }
74 |
75 | public Bundle getBundle() {
76 | check();
77 | if (bundle == null) {
78 | bundle = new Bundle();
79 | }
80 | return bundle;
81 | }
82 |
83 | private RouterResponse process() {
84 | List interceptors = Router.getInterceptor();
85 | RouterChain chain = new RouterChain(this, interceptors);
86 | return chain.proceed(this);
87 | }
88 |
89 | private void check() {
90 | if (context == null) {
91 | throw new RuntimeException("context cannot be null");
92 | }
93 | if (TextUtils.isEmpty(uri)) {
94 | throw new RuntimeException("uri cannot be null or isEmpty");
95 | }
96 | Object object = Router.getRouteTable().get(uri);
97 | if ( object == null) {
98 | throw new RuntimeException("you should register uri at first");
99 | }
100 | }
101 |
102 | public String getUri() {
103 | return uri;
104 | }
105 |
106 | public Context getContext() {
107 | return context;
108 | }
109 |
110 | public boolean isJump() {
111 | return isJump;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/route/RouterResponse.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.route;
2 |
3 | import android.content.Intent;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/2/21
7 | */
8 | public class RouterResponse {
9 |
10 | private boolean isDone;
11 |
12 | private String msg;
13 |
14 | private Intent intent;
15 |
16 | private Class cls;
17 |
18 | public boolean isDone() {
19 | return isDone;
20 | }
21 |
22 | public void setDone(boolean done) {
23 | isDone = done;
24 | }
25 |
26 | public String getMsg() {
27 | return msg;
28 | }
29 |
30 | public void setMsg(String msg) {
31 | this.msg = msg;
32 | }
33 |
34 | public Intent getIntent() {
35 | return intent;
36 | }
37 |
38 | public void setIntent(Intent intent) {
39 | this.intent = intent;
40 | }
41 |
42 | public void setCls(Class cls) {
43 | this.cls = cls;
44 | }
45 |
46 | public Class getCls() {
47 | return cls;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/route/UriAnalyzerInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.route;
2 |
3 | /**
4 | * Created By Chengjunsen on 2019/2/25
5 | */
6 | public class UriAnalyzerInterceptor implements RouterInterceptor {
7 | @Override
8 | public RouterResponse process(RouterChain chain) {
9 | RouterRequest request = chain.getRequest();
10 | RouterResponse response = chain.proceed(request);
11 | String uri = request.getUri();
12 | Class cls = Router.getRouteTable().get(uri);
13 | response.setCls(cls);
14 | return response;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/utils/ClipboardUtil.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.utils;
2 |
3 | import android.content.ClipData;
4 | import android.content.ClipboardManager;
5 | import android.content.Context;
6 |
7 | public class ClipboardUtil {
8 |
9 | public static void copy(String content, Context context)
10 | {
11 | ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
12 | // 创建一个剪贴数据集,包含一个普通文本数据条目(需要复制的数据)
13 | ClipData clipData = ClipData.newPlainText(null, content);
14 | // 把数据集设置(复制)到剪贴板
15 | clipboard.setPrimaryClip(clipData);
16 | }
17 |
18 | public static String paste(Context context)
19 | {
20 | // 获取系统剪贴板
21 | ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
22 | // 获取剪贴板的剪贴数据集
23 | ClipData clipData = clipboard.getPrimaryClip();
24 | if (clipData != null && clipData.getItemCount() > 0) {
25 | // 从数据集中获取(粘贴)第一条文本数据
26 | CharSequence text = clipData.getItemAt(0).getText();
27 | return text.toString();
28 | }
29 | return null;
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/utils/DateUtil.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.utils;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.Calendar;
5 | import java.util.Date;
6 |
7 | /**
8 | * Created By Chengjunsen on 2019/2/21
9 | */
10 | public class DateUtil {
11 | public static boolean isSameDate(long time1, long time2) {
12 | Calendar cal1 = Calendar.getInstance();
13 | cal1.setTimeInMillis(time1);
14 | Calendar cal2 = Calendar.getInstance();
15 | cal2.setTimeInMillis(time2);
16 | return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
17 | cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH) &&
18 | cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH);
19 | }
20 |
21 | public static String formatDate(long time) {
22 | SimpleDateFormat fmt = new SimpleDateFormat("yyyy年MM月dd日");
23 | return fmt.format(new Date(time));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/utils/DisplayUtil.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.utils;
2 |
3 | import android.content.Context;
4 | import android.view.WindowManager;
5 |
6 | /**
7 | * Created By Chengjunsen on 2019/2/25
8 | */
9 | public class DisplayUtil {
10 | public static int getScreenHeight(Context context) {
11 | WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
12 | return manager.getDefaultDisplay().getHeight();
13 | }
14 |
15 | public static int getScreenWidth(Context context) {
16 | WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
17 | return manager.getDefaultDisplay().getWidth();
18 | }
19 |
20 | /**
21 | * 将px转换为与之相等的dp
22 | */
23 | public static int px2dp(Context context, float pxValue) {
24 | final float scale = context.getResources().getDisplayMetrics().density;
25 | return (int) (pxValue / scale + 0.5f);
26 | }
27 |
28 | /**
29 | * 将dp转换为与之相等的px
30 | */
31 | public static int dp2px(Context context, float dipValue) {
32 | final float scale = context.getResources().getDisplayMetrics().density;
33 | return (int) (dipValue * scale + 0.5f);
34 | }
35 |
36 | /**
37 | * 将px转换为sp
38 | */
39 | public static int px2sp(Context context, float pxValue) {
40 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
41 | return (int) (pxValue / fontScale + 0.5f);
42 | }
43 |
44 | /**
45 | * 将sp转换为px
46 | */
47 | public static int sp2px(Context context, float spValue) {
48 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
49 | return (int) (spValue * fontScale + 0.5f);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/utils/FontUtil.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.utils;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Typeface;
6 | import android.text.TextUtils;
7 | import android.util.AttributeSet;
8 | import android.util.Log;
9 | import android.view.View;
10 | import android.widget.Button;
11 | import android.widget.TextView;
12 |
13 | import java.lang.ref.SoftReference;
14 | import java.util.Hashtable;
15 |
16 | /**
17 | * Created By Chengjunsen on 2019/2/22
18 | */
19 | public class FontUtil {
20 |
21 | public static void setCustomFont(View view, Context ctx, AttributeSet attrs,
22 | int[] attributeSet, int fontId) {
23 | TypedArray a = ctx.obtainStyledAttributes(attrs, attributeSet);
24 | String customFont = a.getString(fontId);
25 | setCustomFont(view, ctx, customFont);
26 | a.recycle();
27 | }
28 |
29 | public static boolean setCustomFont(View view, Context ctx, String asset) {
30 | if (TextUtils.isEmpty(asset)) {
31 | return false;
32 | }
33 | Typeface tf = null;
34 | try {
35 | tf = getFont(ctx, asset);
36 | if (view instanceof TextView) {
37 | ((TextView) view).setTypeface(tf);
38 | } else {
39 | ((Button) view).setTypeface(tf);
40 | }
41 | } catch (Exception e) {
42 | Log.e("AppUtil", "Could not get typface " + asset);
43 | return false;
44 | }
45 |
46 | return true;
47 | }
48 |
49 | private static final Hashtable> fontCache = new Hashtable>();
50 |
51 | public static Typeface getFont(Context c, String name) {
52 | synchronized (fontCache) {
53 | if (fontCache.get(name) != null) {
54 | SoftReference ref = fontCache.get(name);
55 | if (ref.get() != null) {
56 | return ref.get();
57 | }
58 | }
59 |
60 | Typeface typeface = Typeface.createFromAsset(c.getAssets(), name);
61 | fontCache.put(name, new SoftReference(typeface));
62 |
63 | return typeface;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/utils/KeyboardUtil.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.utils;
2 |
3 | import android.content.Context;
4 | import android.text.InputType;
5 | import android.view.View;
6 | import android.view.inputmethod.InputMethodManager;
7 | import android.widget.EditText;
8 |
9 | import java.lang.reflect.Method;
10 |
11 | /**
12 | * Created By Chengjunsen on 2019/2/26
13 | */
14 | public class KeyboardUtil {
15 | /**
16 | * 显示软键盘
17 | * @param context
18 | * @param view
19 | */
20 | public static void showSoftInput(final Context context, final View view) {
21 | InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
22 | imm.showSoftInput(view, 0);
23 | }
24 |
25 | /**
26 | * 隐藏软键盘
27 | * @param context
28 | * @param view
29 | */
30 | public static void hideSoftInput(Context context, View view) {
31 | InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
32 | imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
33 | }
34 |
35 | /**
36 | * 获取软键盘状态
37 | * @param context
38 | * @return
39 | */
40 | public static boolean isShowSoftInput(Context context, View view) {
41 | InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
42 | //获取状态信息
43 | return imm.isActive(view);//true 打开
44 | }
45 |
46 | public static void configSoftInput(EditText editText, boolean isShow){
47 | Class cls = EditText.class;
48 | Method method;
49 | try {
50 | method = cls.getMethod("setShowSoftInputOnFocus", boolean.class);
51 | method.setAccessible(true);
52 | method.invoke(editText, isShow);
53 | } catch (Exception e) {
54 | }
55 |
56 | try {
57 | method = cls.getMethod("setSoftInputShownOnFocus", boolean.class);
58 | method.setAccessible(true);
59 | method.invoke(editText, isShow);
60 | } catch (Exception e) {
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/utils/MdUtil.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.utils;
2 |
3 | import java.security.MessageDigest;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/3/9
7 | */
8 | public class MdUtil {
9 | public final static String encode(String s) {
10 | char hexDigits[] = { '0', '1', '2', '3', '4',
11 | '5', '6', '7', '8', '9',
12 | 'a', 'b', 'c', 'd', 'e', 'f' };
13 | try {
14 | byte[] btInput = s.getBytes();
15 | //获得MD5摘要算法的 MessageDigest 对象
16 | MessageDigest mdInst = MessageDigest.getInstance("MD5");
17 | //使用指定的字节更新摘要
18 | mdInst.update(btInput);
19 | //获得密文
20 | byte[] md = mdInst.digest();
21 | //把密文转换成十六进制的字符串形式
22 | int j = md.length;
23 | char str[] = new char[j * 2];
24 | int k = 0;
25 | for (int i = 0; i < j; i++) {
26 | byte byte0 = md[i];
27 | str[k++] = hexDigits[byte0 >>> 4 & 0xf];
28 | str[k++] = hexDigits[byte0 & 0xf];
29 | }
30 | return new String(str);
31 | }
32 | catch (Exception e) {
33 | e.printStackTrace();
34 | return null;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/utils/OsUtil.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.utils;
2 |
3 | import android.os.Build;
4 |
5 | /**
6 | * Created By Chengjunsen on 2019/2/22
7 | */
8 | public final class OsUtil {
9 | public static boolean isFlyme() {
10 | String manufacturer = Build.MANUFACTURER;
11 | if ("meizu".equalsIgnoreCase(manufacturer)) {
12 | return true;
13 | }
14 | return false;
15 | }
16 |
17 | public static boolean isEMUI() {
18 | String manufacturer = Build.MANUFACTURER;
19 | if ("huawei".equalsIgnoreCase(manufacturer)) {
20 | return true;
21 | }
22 | return false;
23 | }
24 |
25 | public static boolean isMIUI() {
26 | String manufacturer = Build.MANUFACTURER;
27 | if ("xiaomi".equalsIgnoreCase(manufacturer)) {
28 | return true;
29 | }
30 | return false;
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/utils/StorageUtil.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.utils;
2 |
3 | import android.content.Context;
4 | import android.os.Environment;
5 | import android.text.TextUtils;
6 | import android.util.Log;
7 |
8 | import java.io.BufferedReader;
9 | import java.io.BufferedWriter;
10 | import java.io.FileReader;
11 | import java.io.FileWriter;
12 | import java.io.IOException;
13 |
14 | /**
15 | * Created By Chengjunsen on 2019/3/8
16 | */
17 | public class StorageUtil {
18 | private static final String TAG = "StorageUtil";
19 |
20 | public static String readFile(String path) {
21 | BufferedReader reader = null;
22 | StringBuilder content = new StringBuilder();
23 | try {
24 | String line;
25 | reader = new BufferedReader(new FileReader(path));
26 | while ((line = reader.readLine()) != null) {
27 | content.append(line);
28 | }
29 | } catch (IOException e) {
30 | e.printStackTrace();
31 | }
32 | return content.toString();
33 | }
34 |
35 | public static void overwiteFile(String path, String text) {
36 | if (TextUtils.isEmpty(text)) {
37 | Log.e(TAG, "overwiteFile: path is null");
38 | return;
39 | }
40 | BufferedWriter writer = null;
41 | try {
42 | writer = new BufferedWriter(new FileWriter(path));
43 | writer.write(text);
44 | writer.flush();
45 | writer.close();
46 | } catch (IOException e) {
47 | e.printStackTrace();
48 | }
49 | }
50 |
51 | public static String getDiskCachePath(Context context){
52 | String cachePath;
53 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
54 | || !Environment.isExternalStorageRemovable()) {
55 | cachePath = context.getExternalCacheDir().getPath();
56 | } else {
57 | cachePath = context.getCacheDir().getPath();
58 | }
59 | return cachePath;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jscheng/srich/utils/VersionUtil.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich.utils;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageManager;
5 |
6 | /**
7 | * Created By Chengjunsen on 2019/3/9
8 | */
9 | public class VersionUtil {
10 | /**
11 | * 获取本地apk的版本
12 | * @param mContext
13 | * @return
14 | */
15 | public static int getVersionCode(Context mContext) {
16 | int versionCode = 0;
17 | try {
18 | //获取软件版本号,对应AndroidManifest.xml下android:versionCode
19 | versionCode = mContext.getPackageManager().
20 | getPackageInfo(mContext.getPackageName(), 0).versionCode;
21 | } catch (PackageManager.NameNotFoundException e) {
22 | e.printStackTrace();
23 | }
24 | return versionCode;
25 | }
26 |
27 | /**
28 | * 获取版本号名称
29 | *
30 | * @param context 上下文
31 | * @return
32 | */
33 | public static String getVerName(Context context) {
34 | String verName = "";
35 | try {
36 | verName = context.getPackageManager().
37 | getPackageInfo(context.getPackageName(), 0).versionName;
38 | } catch (PackageManager.NameNotFoundException e) {
39 | e.printStackTrace();
40 | }
41 | return verName;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/edit_note_bottom_in_anim.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/edit_note_bottom_out_anim.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/editor_bar_format_select.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/editor_bar_redo_select.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/editor_bar_undo_select.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/editor_note_bottom_dialog_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/editor_note_dialog_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/editor_note_dialog_input.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/editor_note_dialog_text_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/editor_note_dialog_text_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/editor_view_select.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/outline_toolbar_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
10 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_editnote.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
17 |
25 |
31 |
32 |
33 |
40 |
41 |
42 |
50 |
51 |
63 |
64 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_image_preview.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_outline.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
22 |
23 |
26 |
27 |
36 |
37 |
38 |
47 |
48 |
49 |
50 |
51 |
52 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/edit_note_bottom_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
20 |
21 |
25 |
26 |
34 |
35 |
39 |
40 |
48 |
49 |
53 |
54 |
62 |
63 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/edit_note_input_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
29 |
30 |
34 |
35 |
39 |
40 |
51 |
52 |
56 |
57 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/edit_note_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
18 |
19 |
31 |
32 |
44 |
45 |
57 |
58 |
69 |
70 |
81 |
82 |
93 |
94 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/outline_center_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/outline_item_view_date.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
17 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/outline_item_view_note.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
20 |
21 |
29 |
30 |
37 |
38 |
50 |
51 |
62 |
63 |
64 |
72 |
73 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/outline_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
19 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/outline_toolbar2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
17 |
18 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_compose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_compose.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_attach.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_attach.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_back.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_backward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_backward.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_check.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_check.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_edit.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_error.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_format_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_format_off.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_forward.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_loading.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_more.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_redo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_redo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_redo_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_redo_disabled.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_tick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_tick.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_uncheck.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_uncheck.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_undo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_note_edit_undo_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xhdpi/ic_note_edit_undo_disabled.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_note_edit_format_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xxhdpi/ic_note_edit_format_on.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | #2196F3
18 | 4dp
19 | 0
20 | 100
21 | false
22 | false
23 | 4000
24 | 5000
25 | 500
26 | 3
27 | -90
28 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #01A92D
4 | #01A92D
5 | #01A92D
6 | #5A5A5A
7 | #FFF
8 | #01A92D
9 | #414141
10 | #f5f595
11 | #01A92D
12 | #5A5A5A
13 | #000
14 | #5A5A5A
15 | #5A5A5A
16 | #5A5A5A
17 | #F8F8F8
18 | #FFFFFF
19 | #EBEBEB
20 | #979797
21 | #000
22 | #000
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 80dp
4 | 50dp
5 | 30dp
6 | 150dp
7 | 50dp
8 | 10dp
9 | 5dp
10 | 15dp
11 | 20dp
12 | 50dp
13 | 20dp
14 | 50dp
15 | 30dp
16 | 15sp
17 | 3dp
18 | 30dp
19 | 80dp
20 | 20sp
21 | 12sp
22 | 16sp
23 | 13sp
24 | 3dp
25 | 50dp
26 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SRich
3 | 所有笔记
4 | 删除
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
21 |
22 |
32 |
33 |
37 |
38 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/test/java/com/jscheng/srich/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.srich;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | maven { url 'https://maven.google.com' }
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.1.2'
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 | }
22 | }
23 |
24 | task clean(type: Delete) {
25 | delete rootProject.buildDir
26 | }
27 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChyengJason/SRich/f748baa5cb8403cd39e5697e483b4a20e13ba082/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Feb 20 17:52:01 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/processor/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/processor/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java-library'
2 |
3 | dependencies {
4 | implementation fileTree(dir: 'libs', include: ['*.jar'])
5 | implementation 'com.google.auto.service:auto-service:1.0-rc2'
6 | implementation 'com.squareup:javapoet:1.9.0'
7 | compile project(path: ':annotation')
8 | }
9 |
10 | sourceCompatibility = "1.8"
11 | targetCompatibility = "1.8"
12 |
--------------------------------------------------------------------------------
/processor/src/main/java/com/jscheng/processor/AnnotatedClass.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.processor;
2 |
3 | import com.squareup.javapoet.ClassName;
4 | import com.squareup.javapoet.JavaFile;
5 | import com.squareup.javapoet.MethodSpec;
6 | import com.squareup.javapoet.ParameterSpec;
7 | import com.squareup.javapoet.ParameterizedTypeName;
8 | import com.squareup.javapoet.TypeName;
9 | import com.squareup.javapoet.TypeSpec;
10 |
11 | import java.util.ArrayList;
12 | import java.util.Map;
13 |
14 | import javax.lang.model.element.Modifier;
15 | import javax.lang.model.element.TypeElement;
16 | import javax.lang.model.util.Elements;
17 |
18 | /**
19 | * Created By Chengjunsen on 2019/3/18
20 | */
21 | public class AnnotatedClass {
22 | private static final String CLASS_PAKCAGE = "com.jscheng.processor";
23 | private static final String CLASS_ROOT_NAME = "Router$$";
24 | private static final String LOAD_METHOD = "load";
25 | private static final ClassName IROUTE_INFO = ClassName.get("com.jscheng.srich.route", "IRouteInfo");
26 |
27 | private TypeElement mTypeElement;
28 | private ArrayList mRouteClasses;
29 | private Elements mElements;
30 |
31 | AnnotatedClass(TypeElement typeElement, Elements elements) {
32 | mTypeElement = typeElement;
33 | mElements = elements;
34 | mRouteClasses = new ArrayList<>();
35 | }
36 |
37 | void addField(RouteClass field) {
38 | mRouteClasses.add(field);
39 | }
40 |
41 | JavaFile generateFile() {
42 | // 生成参数 Map> routes>
43 | ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(
44 | ClassName.get(Map.class),
45 | ClassName.get(String.class),
46 | ClassName.get(Class.class)
47 | );
48 |
49 | ParameterSpec parameter = ParameterSpec.builder(parameterizedTypeName, "routes").build();
50 |
51 | MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(LOAD_METHOD)
52 | .addModifiers(Modifier.PUBLIC)
53 | .addAnnotation(Override.class)
54 | .addParameter(parameter)
55 | .returns(TypeName.VOID);
56 |
57 | for (RouteClass item: mRouteClasses) {
58 | // uri class
59 | methodBuilder.addStatement("routes.put($S, $T.class)", item.getUrl(), item.getTypeClass());
60 | }
61 |
62 | TypeSpec injectClass = TypeSpec.classBuilder(CLASS_ROOT_NAME + mTypeElement.getSimpleName())
63 | .addModifiers(Modifier.PUBLIC)
64 | .addSuperinterface(IROUTE_INFO)
65 | .addMethod(methodBuilder.build())
66 | .build();
67 |
68 | return JavaFile.builder(CLASS_PAKCAGE, injectClass).build();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/processor/src/main/java/com/jscheng/processor/RouteClass.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.processor;
2 |
3 | import com.jscheng.annotations.Route;
4 |
5 | import javax.lang.model.element.Element;
6 | import javax.lang.model.element.ElementKind;
7 | import javax.lang.model.element.Name;
8 | import javax.lang.model.element.TypeElement;
9 | import javax.lang.model.type.TypeMirror;
10 |
11 | /**
12 | * Created By Chengjunsen on 2019/3/18
13 | */
14 | public class RouteClass {
15 | private TypeElement mTypeElement;
16 | private String mUrl;
17 |
18 | RouteClass(TypeElement element) throws IllegalArgumentException {
19 |
20 | if (element.getKind() != ElementKind.CLASS) {
21 | throw new IllegalArgumentException(String.format("Only classes can be annotated with @%s",
22 | Route.class.getSimpleName()));
23 | }
24 |
25 | mTypeElement = element;
26 |
27 | Route route = mTypeElement.getAnnotation(Route.class);
28 | mUrl = route.value();
29 |
30 | if (mUrl.isEmpty()) {
31 | throw new IllegalArgumentException(String.format("value() in %s for class %s is not valid !",
32 | Route.class.getSimpleName(),
33 | mTypeElement.getSimpleName()));
34 | }
35 | }
36 |
37 |
38 | TypeElement getTypeClass() {
39 | return mTypeElement;
40 | }
41 |
42 | String getUrl() {
43 | return mUrl;
44 | }
45 |
46 | TypeMirror getFieldType() {
47 | return mTypeElement.asType();
48 | }
49 |
50 | public Name getClassName() {
51 | return mTypeElement.getSimpleName();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/processor/src/main/java/com/jscheng/processor/RouterProcessor.java:
--------------------------------------------------------------------------------
1 | package com.jscheng.processor;
2 |
3 | import com.google.auto.service.AutoService;
4 | import com.jscheng.annotations.Route;
5 | import com.squareup.javapoet.JavaFile;
6 |
7 | import java.io.IOException;
8 | import java.util.LinkedHashSet;
9 | import java.util.Map;
10 | import java.util.Set;
11 | import java.util.TreeMap;
12 |
13 | import javax.annotation.processing.AbstractProcessor;
14 | import javax.annotation.processing.Filer;
15 | import javax.annotation.processing.Messager;
16 | import javax.annotation.processing.ProcessingEnvironment;
17 | import javax.annotation.processing.Processor;
18 | import javax.annotation.processing.RoundEnvironment;
19 | import javax.lang.model.SourceVersion;
20 | import javax.lang.model.element.Element;
21 | import javax.lang.model.element.PackageElement;
22 | import javax.lang.model.element.TypeElement;
23 | import javax.lang.model.util.Elements;
24 | import javax.tools.Diagnostic;
25 |
26 | /**
27 | * Created By Chengjunsen on 2019/3/18
28 | */
29 | @AutoService(Processor.class)
30 | public class RouterProcessor extends AbstractProcessor {
31 |
32 | private Filer mFiler;
33 |
34 | private Elements mElements;
35 |
36 | private Messager mMessager;
37 |
38 | private Map mAnnotatedClassMap;
39 |
40 | @Override
41 | public synchronized void init(ProcessingEnvironment processingEnvironment) {
42 | super.init(processingEnvironment);
43 | mElements = processingEnvironment.getElementUtils();
44 | mMessager = processingEnvironment.getMessager();
45 | mFiler = processingEnvironment.getFiler();
46 | mAnnotatedClassMap = new TreeMap<>();
47 | }
48 |
49 | @Override
50 | public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
51 | mAnnotatedClassMap.clear();
52 |
53 | processRoute(roundEnvironment);
54 |
55 | for (AnnotatedClass annotatedClass : mAnnotatedClassMap.values()) {
56 | try {
57 | JavaFile javaFile = annotatedClass.generateFile();
58 | info(javaFile.toString());
59 | javaFile.writeTo(mFiler);
60 | } catch (IOException e) {
61 | error("Generate file failed, reason: %s", e.getMessage());
62 | }
63 | }
64 | return true;
65 | }
66 |
67 | private void processRoute(RoundEnvironment roundEnv) throws IllegalArgumentException {
68 | for (Element element : roundEnv.getElementsAnnotatedWith(Route.class)) {
69 | TypeElement typeElement = (TypeElement) element;
70 | PackageElement packageElement = (PackageElement) element.getEnclosingElement();
71 |
72 | String fullName = typeElement.getQualifiedName().toString();
73 |
74 | AnnotatedClass annotatedClass = mAnnotatedClassMap.get(fullName);
75 |
76 | if (annotatedClass == null) {
77 | annotatedClass = new AnnotatedClass(typeElement, mElements);
78 | mAnnotatedClassMap.put(fullName, annotatedClass);
79 | }
80 |
81 | RouteClass routeClass = new RouteClass(typeElement);
82 | info(routeClass.getClassName() + " " + routeClass.getUrl());
83 | annotatedClass.addField(routeClass);
84 | }
85 | }
86 |
87 | @Override
88 | public Set getSupportedAnnotationTypes() {
89 | Set set = new LinkedHashSet<>();
90 | set.add(Route.class.getCanonicalName());
91 | return set;
92 | }
93 |
94 | @Override
95 | public SourceVersion getSupportedSourceVersion() {
96 | return SourceVersion.latestSupported();
97 | }
98 |
99 | private void error(String msg, Object... args) {
100 | mMessager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args));
101 | }
102 |
103 | private void info(String msg, Object... args) {
104 | mMessager.printMessage(Diagnostic.Kind.NOTE, String.format(msg, args));
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | 之前在看大部分的Android 富文本编辑几乎都是利用webview实现,所以,便有了做一个Android原生的富文本编辑器的主意。
2 |
3 | ##### Blog
4 |
5 | [Android 写一个属于自己的富文本编辑器](https://blog.csdn.net/qq_15893929/article/details/88670198)
6 |
7 | ##### 样例
8 | 照惯例先秀一下图:
9 |
10 |
11 |    
12 |
13 |    
14 |
15 |
16 | 该富文本编辑器样式仿照印象笔记的Android版,绘制层实现基于Android的span样式。
17 |
18 | ##### 目前已经实现的功能:
19 | 1. 粗体、斜体、下划线、删除线、上下标、背景色字体样式;
20 | 2. 分割线、缩进、有序列表、无序列表、复选框行样式;
21 | 3. 支持插入本地图片;
22 | 4. 支持插入网络图片;
23 | 4. 支持图片预览;
24 | 5. 支持撤销和反撤销;
25 | 6. 支持本地持久化、支持增删改;
26 | 7. 支持编辑模式和预览模式
27 |
28 | ##### 具体的实现:
29 | 主要的实现在于编辑页面,直接是继承自EditText加以改造的(偷懒),但是如果想实现一个商业级别的编辑器,建议使用StaticLayout和自定义View,但是需要考虑的东西比较多,例如输入法和排版布局、选区管理绘制、各类点击事件。
30 |
31 | 
32 | * NoteEditText 继承自 EditText,NoteEditorManager管理基本逻辑;
33 |
34 | * NoteEditorRender负责绘制,NoteLineSpanRender是行样式、NoteWordSpanRender是字体样式;
35 |
36 | * NoteRevocationManager负责撤销与反撤销;
37 |
38 | * NoteImageLoader 是图片加载库,之前想用Glide库,但是Glide不支持直接在UI线程获取缓冲区的Bitmap,所以简单写了一个基于OkHttp的图片加载,内部参照(抄)了Glide的思想,例如ImageView在宽高为0时如何加载图片、图片过大时候怎么处理。Glide太强大了,代码也好复杂;
39 | 后续还是要继续替换成Glide,可以通过自定义设置Glide缓冲池,这样外部就可以直接拿到缓冲区数据;
40 |
41 | * converter 是简单地将富文本对象转成文本数据,或将文本数转成富文本对象的模块;
42 |
43 | * dao 数据库层;
44 |
45 | * route 是在利用APT和借鉴OkHttp责任链模式仿写的一个跳转路由的功能;
46 | 只是自己学习所写的一个小工具,完全可以去掉。
47 |
48 |
49 | ##### 后续计划:
50 |
51 | 这个版本更多的是将自己所学的一些知识的运用,只做了小一段时间,所以存留很多了bug和缺陷,后续会继续找时间修补。
52 |
53 | 想增加的内容:
54 | 1. 增加导入导出html
55 | 2. 完善图片池
56 | 3. 增加桌面小部件
57 | 4. 增加保存为图片
58 | 5. 支持超链接、引用更多样式
59 |
60 |
61 | ##### 附上
62 | 1. BackgroundColorSpan 背景色
63 | 2. ForegroundColorSpan 文本颜色(前景色)
64 | 3. RasterizerSpan 光栅效果
65 | 4. StrikethroughSpan 删除线
66 | 5. SuggestionSpan 相当于占位符
67 | 6. UnderlineSpan 下划线
68 | 7. AbsoluteSizeSpan 绝对大小(文本字体)
69 | 8. DynamicDrawableSpan 设置图片,基于文本基线或底部对齐。
70 | 9. ImageSpan 图片
71 | 10. RelativeSizeSpan 相对大小(文本字体)
72 | 11. ReplacementSpan 父类,一般不用
73 | 12. URLSpan 文本超链接
74 | 13. StyleSpan 字体样式
75 | 14. SubscriptSpan 下标
76 | 15. SuperscriptSpan 上标
77 | 16. TextAppearanceSpan 文本外貌(包括字体、大小、样式和颜色)
78 | 17. TypefaceSpan 文本字体
79 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':annotation', ':processor'
2 |
--------------------------------------------------------------------------------