From 1e953b81329e18287508de77dcc3b767d8abab2f Mon Sep 17 00:00:00 2001 From: Hellings Date: Fri, 16 Oct 2026 13:05:27 -0400 Subject: [PATCH] ok --- .gitignore | 5 +- .../caches/9.7.1/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../9.7.1/javaCompile/classAnalysis.bin | Bin 1290656 -> 1297726 bytes .../caches/9.7.1/javaCompile/javaCompile.lock | Bin 17 -> 17 bytes .gradle-user/caches/journal-1/file-access.bin | Bin 24297 -> 24297 bytes .gradle-user/caches/journal-1/journal-1.lock | Bin 17 -> 17 bytes .gradle-user/caches/modules-2/modules-2.lock | Bin 17 -> 17 bytes .gradle-user/daemon/9.7.1/registry.bin.lock | Bin 17 -> 17 bytes README.md | 2 +- apkpatch/MoanLauncher.toml | 38 + apkpatch/MoanNote.toml | 33 +- extractors/index.ts | 223 ++- jfmt.sh | 3 +- .../ms/moannotes/nativebridge/InkPanel.class | Bin 0 -> 7281 bytes .../moannotes/nativebridge/LayerPanel$1.class | Bin 1448 -> 0 bytes .../moannotes/nativebridge/LayerPanel$2.class | Bin 1715 -> 0 bytes .../moannotes/nativebridge/LayerPanel.class | Bin 10950 -> 12483 bytes .../nativebridge/NativeFeatureHooks.class | Bin 4627 -> 8076 bytes .../moannotes/nativebridge/PanelDocker.class | Bin 0 -> 5915 bytes .../moannotes/nativebridge/ToolsSheet.class | Bin 0 -> 8048 bytes .../ms/moannotes/nativebridge/InkPanel.java | 141 ++ .../ms/moannotes/nativebridge/LayerPanel.java | 364 ++-- .../nativebridge/NativeFeatureHooks.java | 182 +- .../moannotes/nativebridge/PanelDocker.java | 122 ++ .../ms/moannotes/nativebridge/ToolsSheet.java | 185 ++ package.json | 3 +- .../res/layout/home_card_app_item.xml | 50 +- patches/moanlauncher/res/layout/item_apps.xml | 50 +- .../rip/crit/ms/launcher/XiaomiProfile.smali | 63 + .../background/template_college_ruled.png | Bin 11038 -> 12242 bytes .../drawable-nodpi/template_college_ruled.png | Bin 11038 -> 12242 bytes patches/moannotes/res/drawable/icp_check.xml | 23 + patches/moannotes/res/drawable/icp_close.xml | 24 + patches/moannotes/res/drawable/icp_export.xml | 3 - .../moannotes/res/drawable/icp_eye_closed.xml | 48 + .../moannotes/res/drawable/icp_eye_open.xml | 22 + patches/moannotes/res/drawable/icp_ink.xml | 23 + .../moannotes/res/drawable/icp_kra_export.xml | 33 +- patches/moannotes/res/drawable/icp_layers.xml | 40 +- .../moannotes/res/drawable/icp_palette.xml | 3 - patches/moannotes/res/drawable/icp_pen.xml | 3 - .../moannotes/res/drawable/icp_snap_line.xml | 31 +- patches/moannotes/res/drawable/icp_tools.xml | 22 + .../res/values/moannotes_patch_ids.xml | 4 +- .../res/values/moannotes_strings.xml | 4 + .../ms/moannotes/BackgroundCatalogPatch.smali | 114 +- .../crit/ms/moannotes/FeatureRegistry.smali | 179 +- .../moannotes/SnapToLineTouchListener.smali | 176 +- qmlfmt.sh | 3 - scripts/reinstall-moannotes.ts | 100 +- scripts/sign-moannotes.ts | 77 +- .../ntx/note/asynctask/BitmapWorkerTask.java | 91 +- .../java/ntx/painter/ImagePickerActivity.java | 523 ++--- .../main/java/ntx/painter/MainActivity.java | 1327 ++++++------- .../src/main/java/ntx/painter/NtxView.java | 1742 +++++++++-------- .../java/ntx/painter/ReselectSpinner.java | 57 +- .../nRefresh/src/ntx/demo/MainActivity.java | 969 +++++---- .../src/ntx/demo/NtxImageViewClass.java | 211 +- .../rip/crit/ms/unote/XiaomiEinkRenderer.java | 6 - 59 files changed, 4278 insertions(+), 3044 deletions(-) create mode 100644 moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/InkPanel.class delete mode 100644 moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/LayerPanel$1.class delete mode 100644 moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/LayerPanel$2.class create mode 100644 moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/PanelDocker.class create mode 100644 moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/ToolsSheet.class create mode 100644 moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/InkPanel.java create mode 100644 moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/PanelDocker.java create mode 100644 moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/ToolsSheet.java create mode 100644 patches/moannotes/res/drawable/icp_check.xml create mode 100644 patches/moannotes/res/drawable/icp_close.xml delete mode 100644 patches/moannotes/res/drawable/icp_export.xml create mode 100644 patches/moannotes/res/drawable/icp_eye_closed.xml create mode 100644 patches/moannotes/res/drawable/icp_eye_open.xml create mode 100644 patches/moannotes/res/drawable/icp_ink.xml delete mode 100644 patches/moannotes/res/drawable/icp_palette.xml delete mode 100644 patches/moannotes/res/drawable/icp_pen.xml create mode 100644 patches/moannotes/res/drawable/icp_tools.xml create mode 100644 patches/moannotes/res/values/moannotes_strings.xml delete mode 100755 qmlfmt.sh diff --git a/.gitignore b/.gitignore index 3e69143..a3a024f 100644 --- a/.gitignore +++ b/.gitignore @@ -64,4 +64,7 @@ skills-lock.json notable/models/ -translator/__pycache__/ \ No newline at end of file +translator/__pycache__/ + +.gradle-user/ +.tessl/ \ No newline at end of file diff --git a/.gradle-user/caches/9.7.1/fileHashes/fileHashes.lock b/.gradle-user/caches/9.7.1/fileHashes/fileHashes.lock index b80ab5901b65a761e31398b8c4d7fdc10372747f..218e0c2995749bdafba2d3218411175858efa88c 100644 GIT binary patch literal 17 VcmZSn84`TjY2gJ81~6cX2LLyR1gii5 literal 17 VcmZSn84`TjY2gJ81~6da1^_oR1Xln6 diff --git a/.gradle-user/caches/9.7.1/javaCompile/classAnalysis.bin b/.gradle-user/caches/9.7.1/javaCompile/classAnalysis.bin index a84d6e57099e96c471149ca6da5c2db1655c3841..1ad8e7f4c6ed74dd5af30ce3794db4d99ca72aaa 100644 GIT binary patch delta 2016 zcmd6nZETZO6vua;wxjLZ=iaAxbL(E5g`sK8-dq?VK$JzN!vWp8OyZ_1ltIVT zfvHGnU>^XN?6fOp4A^wA8H&p;ib38SVhA797(WnY5nV=HCVoIBD&E^C(xNsony5{F z$&=f2a?bsq|GE3VT79B#jc*$ola6GE!!d8R8Y|G z2uaKl(rgK!mF!}Xb`oKF>9g%wb0ccxDGM7~q%~$q*$9>zl~%UcseQT~RkGf*T6-48 z+J#59+b^lHW7TYYQv0fePWdaZXgNAgBCEVIFMTiC$y$B-CD}MMqtlJO{f(M&A^@-V z>nrdL-v{p3j(x8lez==`-LJ3INedC|>X(=MhX(Z=1INfo;+WXbp!wD=)Xkfdhi&+@ z`)XrK;kRo3nQ2z~gj}OR*0Ak{ksWo($8u1BHSd(?2rdZy;M#g{sTw)4lGTLdVFhp5 zG@%)7Emu$9DP%WCw~P~0)F0v2Ew-WvcGl;v)UwG-T0)=5}?|saehC(p8l_W#m zFl|By)b9)1h>U`fZ*8^P)d}qbG!KoyK!7gLm$fxJ>f2l`P?3pJp&&?8;c_2Mfiu@7 z1@iCKD9~MIw1DqpVuzOl{EsiavKh_|)10J8-ou{fnq3=U@=cnVF5T6epMSWZXff~Z zsAzR9#~~0UAAqW7i8ZD{fSz?ES~C~g_tX2~5wm20TMdLHmUMW%o;GpII=Hs?j|H=_ z^pvR5&T$JOQA$i(LK`JU5qNM9A#hKSB*D}Hx(uExMK~bA+a&QBZ;; z9p(bW0+ap3uI#CAYuMm%H{^LX=9P4~TNxIUK;(L^F`5{<3k zED@+$n}5?e_>H`?{Mq%3tZ30s*0yUb!nW<`9x^;@9gJtUTKj) zeFaW8YOO$0B?D|uBUG5b^huDlk%1>+?({MbzRdQevzfLpoz1K< zgVA&QIb)WGjN6Z!vM|ar$-SJe%q7^m-8`5ju8=Ws`?Mo0nzGxkKV+$5Vq7^r%TF-0 zz5fXd5VHa?+xGq^?007~c5KgW;INdRzHdH<$9A~|9G9tFlB(~Rw^I0)7GHz59;NHF|m2Za%xjc53?co+*eDlXt_k9p3&J?6QF!;Gk(&-~v<->jM`cw>}Ub^#P(Nwsc&ah^uhq z$POGRI5#)sd^B8}fsXR!ob#VQ$%PCD-f-Y`m!paKdhN+vL=_9Kf_K431~XfNT{E}n z_sa{-$z&pEWOC3Hpa57vtS~?raKk7C843xnXxFL(Yf@4uC$D3?rK+y>_12Zx5VQoF z<3Zml0Jat0abQnMTVNc(`Wx)4kZ}|M>ri3fK#B`)2Ia_^!Yb$?101UdIL|5Rado1q zJ+2e$j0)+@sBo5>WhMuwzqaHe)VRRYxmHKZu?PcL1#s!~pj^uW?xX-Wj&eU1NZ{g}79pPWb7OK+y*w!P8v@LAax^V#tG-|?51 U__MuC{x5WMJyW*1R4P?|07^5o>i_@% delta 1222 zcmbW0J4?f07=>G__y@H49qwKb9MnbX;H9mCvs#{QGbS8 zM+@EklAK5D3t5~V&N=TbBuCg zT$ewYxIdhbhBhBh$*phu(U7@QX8*FZ<%)c`Dh-R$C9&MkfzS`$7V^>auFH=Z`O{@j z$c6rfQn)2u6?a5G#V3dKLF=O-b+D#Amh8u`t8Q0B!-kn->zIR&-c#S9h~Afm_($>& zMCP%dIrNdE>9b@mJl1@O4^N^zdaZ>*{MPZ=k520+KQnV;?Y@&!?Y|VSMe+;j8!^7e z_1U^O@4J(^SL(YJ@$Eh5$|3jsssFvV)=lbbJx4G2EPC#js5fIVQhJEQeNs_>7GHv| ave)XBx>=VtJ`(reeDCiccJnfs%;+~Jzp0x5 diff --git a/.gradle-user/caches/journal-1/journal-1.lock b/.gradle-user/caches/journal-1/journal-1.lock index f0fe687a53704eec83ccc9e89e21bd547f7fb3d2..a0fb51e214b6d2b238d858d06114fae97be71b46 100644 GIT binary patch literal 17 VcmZSXUw!x9CYFi67{EZ#0{}ty1(N^( literal 17 VcmZSXUw!x9CYFi67{Gw#0{}vW1}*>q diff --git a/.gradle-user/caches/modules-2/modules-2.lock b/.gradle-user/caches/modules-2/modules-2.lock index 06f77e790bd4cde9fc4fe8ff216b89884567d240..27f5c0114690b3b28ae17d52041a481fec76458a 100644 GIT binary patch literal 17 VcmZSnzvJ7dw%XJF3}C=m002vf1~C8t literal 17 VcmZSnzvJ7dw%XJF3}C=00{}}j1>FDu diff --git a/.gradle-user/daemon/9.7.1/registry.bin.lock b/.gradle-user/daemon/9.7.1/registry.bin.lock index 9301334b490a7cc334421f65c1ce84f1432f9976..744da064eb5bcc6a546cedef151c18794babd2bc 100644 GIT binary patch literal 17 UcmZS1JHGz3$SdX=1_&qt05+NgqyPW_ literal 17 UcmZS1JHGz3$SdX=1_)3905(PhPyhe` diff --git a/README.md b/README.md index 591cc68..aa2bee8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # OpenMiInk -`OpenMiInk` is a Xiaomi-focused reverse engineering of major mfr's E-Ink controller interfaces under Android. +`OpenMiInk` is a Xiaomi-focused reverse engineering including other REs of major mfr's E-Ink controller interfaces under Android. diff --git a/apkpatch/MoanLauncher.toml b/apkpatch/MoanLauncher.toml index f281044..998fa30 100644 --- a/apkpatch/MoanLauncher.toml +++ b/apkpatch/MoanLauncher.toml @@ -19,7 +19,45 @@ targetFile = "com/note/fragment/ApplicationFragment.smali" search = " new-instance v2, Landroidx/recyclerview/widget/GridLayoutManager;\n\n invoke-virtual {p0}, Lcom/note/fragment/ApplicationFragment;->getActivity()Landroidx/fragment/app/FragmentActivity;\n\n move-result-object v3\n\n const/4 v4, 0x4\n\n invoke-direct {v2, v3, v4}, Landroidx/recyclerview/widget/GridLayoutManager;->(Landroid/content/Context;I)V\n\n .line 185\n .local v2, \"layoutManager\":Landroidx/recyclerview/widget/GridLayoutManager;" replace = " new-instance v2, Landroidx/recyclerview/widget/LinearLayoutManager;\n\n invoke-virtual {p0}, Lcom/note/fragment/ApplicationFragment;->getActivity()Landroidx/fragment/app/FragmentActivity;\n\n move-result-object v3\n\n invoke-direct {v2, v3}, Landroidx/recyclerview/widget/LinearLayoutManager;->(Landroid/content/Context;)V\n\n .line 185\n .local v2, \"layoutManager\":Landroidx/recyclerview/widget/LinearLayoutManager;" +# All external launches funnel through XiaomiProfile.safeStart (null-proof, +# ActivityNotFound-proof): a toast instead of a crash when a target such as +# WPS, Notes, or Settings is missing, disabled, or shadowed by a rename. +[[smaliPatches]] +targetFile = "com/note/utils/AppUtils.smali" +search = " invoke-virtual {p0, v0}, Landroid/content/Context;->startActivity(Landroid/content/Intent;)V" +replace = " invoke-static {p0, v0}, Lrip/crit/ms/launcher/XiaomiProfile;->safeStart(Landroid/content/Context;Landroid/content/Intent;)V" + +[[smaliPatches]] +targetFile = "com/note/launcher/MainActivity$2.smali" +search = " invoke-virtual {v1, v0}, Lcom/note/launcher/MainActivity;->startActivity(Landroid/content/Intent;)V" +replace = " invoke-static {v1, v0}, Lrip/crit/ms/launcher/XiaomiProfile;->safeStart(Landroid/content/Context;Landroid/content/Intent;)V" + +[[smaliPatches]] +targetFile = "com/note/utils/DocMsgManager.smali" +search = " invoke-virtual {v1, v0}, Landroid/content/Context;->startActivity(Landroid/content/Intent;)V" +replace = " invoke-static {v1, v0}, Lrip/crit/ms/launcher/XiaomiProfile;->safeStart(Landroid/content/Context;Landroid/content/Intent;)V" + +[[smaliPatches]] +targetFile = "com/note/utils/DocMsgManager.smali" +search = " invoke-virtual {v2, v0}, Landroid/content/Context;->startActivity(Landroid/content/Intent;)V" +replace = " invoke-static {v2, v0}, Lrip/crit/ms/launcher/XiaomiProfile;->safeStart(Landroid/content/Context;Landroid/content/Intent;)V" + +[[smaliPatches]] +targetFile = "com/note/launcher/MainActivity$1.smali" +search = " const-string v1, \"com.moan.notes\"\n\n invoke-virtual {v0, v1}, Landroid/content/pm/PackageManager;->getLaunchIntentForPackage(Ljava/lang/String;)Landroid/content/Intent;\n\n move-result-object v0\n\n .line 151\n .local v0, \"intent\":Landroid/content/Intent;\n iget-object v1, p0, Lcom/note/launcher/MainActivity$1;->this$0:Lcom/note/launcher/MainActivity;\n\n invoke-virtual {v1, v0}, Lcom/note/launcher/MainActivity;->startActivity(Landroid/content/Intent;)V" +replace = " invoke-static {v0}, Lrip/crit/ms/launcher/XiaomiProfile;->resolveNotesIntent(Landroid/content/pm/PackageManager;)Landroid/content/Intent;\n\n move-result-object v0\n\n .line 151\n .local v0, \"intent\":Landroid/content/Intent;\n if-eqz v0, :notes_missing\n\n iget-object v1, p0, Lcom/note/launcher/MainActivity$1;->this$0:Lcom/note/launcher/MainActivity;\n\n invoke-virtual {v1, v0}, Lcom/note/launcher/MainActivity;->startActivity(Landroid/content/Intent;)V\n\n goto :notes_launched\n\n :notes_missing\n iget-object v1, p0, Lcom/note/launcher/MainActivity$1;->this$0:Lcom/note/launcher/MainActivity;\n\n const-string v0, \"Notes app is not installed\"\n\n const/4 v2, 0x0\n\n invoke-static {v1, v0, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;\n\n move-result-object v0\n\n invoke-virtual {v0}, Landroid/widget/Toast;->show()V\n\n :notes_launched" + [[smaliPatches]] targetFile = "com/note/launcher/MainActivity.smali" search = " invoke-virtual {p0, v0}, Lcom/note/launcher/MainActivity;->setContentView(I)V\n\n .line 87" replace = " invoke-virtual {p0, v0}, Lcom/note/launcher/MainActivity;->setContentView(I)V\n\n invoke-static {p0}, Lrip/crit/ms/launcher/XiaomiProfile;->capture(Landroid/content/Context;)V\n\n .line 87" + +[[smaliPatches]] +targetFile = "com/note/weight/NoteWeight.smali" +search = " invoke-static {}, Lcom/note/moannotelib/notes/summaries/NoteSummaries;->sGetInstance()Lcom/note/moannotelib/notes/summaries/NoteSummaries;\n\n move-result-object v0\n\n invoke-virtual {v0}, Lcom/note/moannotelib/notes/summaries/NoteSummaries;->updateSummaries()I" +replace = " invoke-static {}, Lcom/note/moannotelib/notes/summaries/NoteSummaries;->sGetInstance()Lcom/note/moannotelib/notes/summaries/NoteSummaries;\n\n move-result-object v0\n\n if-eqz v0, :empty_summaries\n\n invoke-virtual {v0}, Lcom/note/moannotelib/notes/summaries/NoteSummaries;->updateSummaries()I" + +[[smaliPatches]] +targetFile = "com/note/weight/NoteWeight.smali" +search = " .line 210\n return-void\n.end method" +replace = " .line 210\n return-void\n\n :empty_summaries\n return-void\n.end method" diff --git a/apkpatch/MoanNote.toml b/apkpatch/MoanNote.toml index 2c5a510..3bbc2d6 100644 --- a/apkpatch/MoanNote.toml +++ b/apkpatch/MoanNote.toml @@ -13,20 +13,9 @@ target = "en" [generatedSmali] sourceDir = "moannotes-native/build/smali/rip/crit/ms/moannotes/nativebridge" -[[resources.xmlPatches]] -targetFile = "res/layout/note_edit.xml" -search = "android:src=\"@drawable/ic_noteview_stylus_ballpen\"" -replace = "android:src=\"@drawable/icp_pen\" android:contentDescription=\"Pen\"" - -[[resources.xmlPatches]] -targetFile = "res/layout/note_edit.xml" -search = "android:src=\"@drawable/ic_noteview_background\"" -replace = "android:src=\"@drawable/icp_palette\" android:contentDescription=\"Paper and monochrome ink\"" - -[[resources.xmlPatches]] -targetFile = "res/layout/note_edit.xml" -search = "android:src=\"@drawable/ic_noteview_more\"" -replace = "android:src=\"@drawable/icp_export\" android:contentDescription=\"Document actions and export\"" +# NOTE: the stock toolbar icons and menus are intentionally left untouched. +# All patch UI lives outside it: one external FAB on the window decor opens +# the ToolsSheet bottom docker (layers / ink / snap / KRA export). [[smaliPatches]] targetFile = "com/moan/notes/LaunchActivity.smali" @@ -59,7 +48,23 @@ targetFile = "h/f/b/m5/j.smali" search = " iget p2, p2, Lh/f/b/m5/l;->c:I\n\n invoke-virtual {p1, p2}, Landroid/widget/ImageView;->setImageResource(I)V" replace = " iget-object v1, p2, Lh/f/b/m5/l;->d:Ljava/lang/String;\n\n iget p2, p2, Lh/f/b/m5/l;->c:I\n\n iget-object v0, p0, Lh/f/b/m5/j;->h:Landroid/content/Context;\n\n invoke-static {v0, v1, p2}, Lrip/crit/ms/moannotes/BackgroundCatalogPatch;->previewResource(Landroid/content/Context;Ljava/lang/String;I)I\n\n move-result p2\n\n invoke-virtual {p1, p2}, Landroid/widget/ImageView;->setImageResource(I)V" +# The stock page loader clamps a negative mEditLayerID but never an +# over-large one, so any layer-count shrink (delete) crashes note open with +# IndexOutOfBounds in u5/b/j. Upper-clamp here: corrupted notes open again. +[[smaliPatches]] +targetFile = "h/f/b/u5/b/j.smali" +search = " invoke-static {v2, v1}, Lcom/moan/notes/hand/data/config/LayerConfigData;->sSaveLayerConfigData(Landroid/content/Context;Lcom/moan/notes/hand/data/config/LayerConfigData;)I\n\n :cond_1e\n iget-object v1, v0, Lh/f/b/u5/a/b;->a:Ljava/util/ArrayList;" +replace = " invoke-static {v2, v1}, Lcom/moan/notes/hand/data/config/LayerConfigData;->sSaveLayerConfigData(Landroid/content/Context;Lcom/moan/notes/hand/data/config/LayerConfigData;)I\n\n :cond_1e\n iget-object v1, v0, Lh/f/b/u5/a/b;->a:Ljava/util/ArrayList;\n\n invoke-virtual {v1}, Ljava/util/ArrayList;->size()I\n\n move-result v1\n\n iget-object v2, v0, Lh/f/b/u5/a/b;->i:Lcom/moan/notes/hand/data/config/LayerConfigData;\n\n iget v2, v2, Lcom/moan/notes/hand/data/config/LayerConfigData;->mEditLayerID:I\n\n if-ge v2, v1, :layer_clamp_ok\n\n add-int/lit8 v1, v1, -0x1\n\n if-gez v1, :layer_clamp_store\n\n const/4 v1, 0x0\n\n :layer_clamp_store\n iget-object v2, v0, Lh/f/b/u5/a/b;->i:Lcom/moan/notes/hand/data/config/LayerConfigData;\n\n iput v1, v2, Lcom/moan/notes/hand/data/config/LayerConfigData;->mEditLayerID:I\n\n move v2, v1\n\n :layer_clamp_ok\n iget-object v1, v0, Lh/f/b/u5/a/b;->a:Ljava/util/ArrayList;" + [[smaliPatches]] targetFile = "com/moan/notes/NoteActivity.smali" search = " invoke-virtual {p1, v0}, Lcom/moan/notes/hand/view/HandwriteView;->setObserver(Lcom/moan/notes/hand/view/HWViewStatus$IStatusReporter;)V\n\n :cond_0\n return-void" replace = " invoke-virtual {p1, v0}, Lcom/moan/notes/hand/view/HandwriteView;->setObserver(Lcom/moan/notes/hand/view/HWViewStatus$IStatusReporter;)V\n\n invoke-static {p0}, Lrip/crit/ms/moannotes/FeatureRegistry;->installNote(Landroid/app/Activity;)V\n\n :cond_0\n return-void" + +# Rotation goes through onConfigurationChanged -> setContentView, which swaps +# the HandwriteView without rerunning init. Reinstall there so the snap +# listener follows the new view (FAB lives on the decor and survives). +[[smaliPatches]] +targetFile = "com/moan/notes/NoteActivity.smali" +search = " invoke-virtual {p0}, Lcom/moan/notes/NoteActivity;->D()V\n\n invoke-virtual {p0}, Lcom/moan/notes/NoteActivity;->R()V" +replace = " invoke-virtual {p0}, Lcom/moan/notes/NoteActivity;->D()V\n\n invoke-virtual {p0}, Lcom/moan/notes/NoteActivity;->R()V\n\n invoke-static {p0}, Lrip/crit/ms/moannotes/FeatureRegistry;->installNote(Landroid/app/Activity;)V" diff --git a/extractors/index.ts b/extractors/index.ts index 64971b0..879c2a4 100644 --- a/extractors/index.ts +++ b/extractors/index.ts @@ -45,6 +45,79 @@ export interface PatchConfig { } const cjk = /[\u3400-\u9fff]/; +const preferredTranslations: Readonly> = { + " 搜索...": "Search", + 按修改时间: "Modified", + 按名称: "Name", + "背景:": "Background", + 全部: "All", + 书架: "Library", + 关闭: "Close", + 取消: "Cancel", + "加载中...": "Loading", + 去设置WLAN: "Wi-Fi settings", + 同步: "Sync", + 删除: "Delete", + 更新: "Update", + 清空: "Clear", + 测试: "Test", + 标题: "Title", + 搜索历史: "Search history", + "搜索...": "Search", + 暂无数据: "No items", + 暂无小部件: "No widgets", + 暂无文件: "No files", + 暂无搜索结果: "No results", + 没有网络: "No network", + 新增首页小部件: "Add home widget", + 编辑首页小部件: "Edit home widget", + 添加小部件: "Add widget", + 点击添加小部件: "Add widget", + 点击新建笔记: "New note", + 笔记: "Notes", + 笔记名称: "Note name", + 笔记更新: "Notes update", + 修改名称: "Rename", + 重命名: "Rename", + 知道了: "Done", + 所有笔记加密密码相同: "Use one password for all notes", + 关闭密码: "Disable password", + 继续: "Continue", + "请输入4~8位数字": "Enter 4 to 8 digits", + "共 8 项": "8 items", + "录音文件名称录...": "Recording name", + "请按名称或主题关键词搜索书籍,笔记": "Search books and notes", + 支持搜索多看的在线书籍: "Search online books", + 请输入书名或作者名称关键词: "Search by title or author", + 阅读数据: "Reading data", + 首页: "Home", + 优化配置: "Optimize", + 应用: "Apps", + 卸载: "Uninstall", + 请使用手机扫描查看: "Scan with your phone", + 阅读分享: "Share reading", + 打开多看: "Open Duokan", + "将PDF文档放至Books或者Download文件夹内\n就能在此显示哦~": + "Put PDF files in Books or Download to see them here", + 最近浏览: "Recent", + 本地PDF: "Local PDFs", + 多看: "Duokan", + 今日阅读: "Reading today", + 全网排行: "Rankings", + 全网阅读数据: "Reading statistics", + 分钟: "minutes", + 名: "rank", + "点击登录系统账号,开始统计": "Sign in to track reading", + 目标完成: "Goal reached", + 近七天阅读: "Last 7 days", + 查看最近文档: "Recent documents", + "作者:": "Author", + 加入书架: "Add to library", + 已在书架: "In library", + 文件夹: "Folder", + "移动到…./文件夹1/文件夹2/文件夹3/文件夹4/文件夹5/文件夹6/文件夹7/文件夹8/文件夹9/文件夹10": + "Move to folder", +}; const signaturePermissions = new Set([ "android.permission.CHANGE_CONFIGURATION", "android.permission.DELETE_PACKAGES", @@ -189,10 +262,22 @@ function renameManifestPackage( const match = xml.match(/\bpackage="([^"]+)"/); if (!match) throw new Error("Manifest package attribute was not found"); const original = match[1]!; - const renamed = original.startsWith(`${prefix}.`) - ? original - : `${prefix}.${original}`; + // A prefix identical to (or already containing) the original package + // means "keep the stock package": the tablet breaks when the installer + // namespace changes, so never double-prefix (com.moan.notes must stay + // com.moan.notes). + const renamed = + original === prefix || original.startsWith(`${prefix}.`) + ? original + : `${prefix}.${original}`; xml = xml.replace(`package="${original}"`, `package="${renamed}"`); + // Provider authorities belong to the installation namespace, not the + // signing namespace. A renamed package must always receive a renamed + // authority or Android rejects it while the OEM package is still present. + xml = xml.replaceAll( + `android:authorities="${original}`, + `android:authorities="${renamed}`, + ); if (ordinarySignature) { xml = xml.replace(/\s+android:sharedUserId="[^"]+"/, ""); xml = xml.replace( @@ -200,10 +285,6 @@ function renameManifestPackage( (element, permission: string) => signaturePermissions.has(permission) ? "" : element, ); - xml = xml.replaceAll( - `android:authorities="${original}`, - `android:authorities="${renamed}`, - ); console.log(" -> Removed shared system UID and signature-only permissions"); } fs.writeFileSync(manifestPath, xml); @@ -239,10 +320,49 @@ function createTranslatedStringOverlay( value: match[3]!, }); } - if (!entries.length) return; + if (entries.length) { + const translations = translateTexts( + entries.map((entry) => entry.value), + config, + projectRoot, + ); + const strings = entries.map( + (entry, index) => + ` ${escapeXmlText(translations[index]!)}`, + ); + const target = config.target ?? "en"; + const destination = path.join( + workDir, + "res", + `values-${target}`, + "strings.xml", + ); + fs.mkdirSync(path.dirname(destination), { recursive: true }); + fs.writeFileSync( + destination, + `\n\n${strings.join("\n")}\n\n`, + ); + console.log( + ` -> Added ${strings.length} ${target} translations through ${config.backend ?? "argos"}`, + ); + } + createTranslatedLayoutOverlays(workDir, config, projectRoot); +} + +function translateTexts( + texts: string[], + config: TranslationConfig, + projectRoot: string, +): string[] { + if (!texts.length) return []; + const translations = texts.map((text) => preferredTranslations[text]); + const unresolved = texts + .map((text, index) => ({ text, index })) + .filter(({ index }) => translations[index] === undefined); + if (!unresolved.length) return translations as string[]; const apiKey = config.apiKeyEnv ? process.env[config.apiKeyEnv] : undefined; const request = JSON.stringify({ - texts: entries.map((entry) => entry.value), + texts: unresolved.map(({ text }) => text), backend: config.backend ?? "argos", source: config.source ?? "zh", target: config.target ?? "en", @@ -278,28 +398,73 @@ function createTranslatedStringOverlay( const response = JSON.parse(result.stdout) as { translations: string[] }; if ( !Array.isArray(response.translations) || - response.translations.length !== entries.length + response.translations.length !== unresolved.length ) throw new Error("Translation bridge returned an invalid result count"); - const strings = entries.map( - (entry, index) => - ` ${escapeXmlText(response.translations[index]!)}`, - ); + for (const [index, translation] of response.translations.entries()) + translations[unresolved[index]!.index] = translation; + return translations as string[]; +} + +function createTranslatedLayoutOverlays( + workDir: string, + config: TranslationConfig, + projectRoot: string, +): void { + const resourceRoot = path.join(workDir, "res"); const target = config.target ?? "en"; - const destination = path.join( - workDir, - "res", - `values-${target}`, - "strings.xml", - ); - fs.mkdirSync(path.dirname(destination), { recursive: true }); - fs.writeFileSync( - destination, - `\n\n${strings.join("\n")}\n\n`, - ); - console.log( - ` -> Added ${strings.length} ${target} translations through ${config.backend ?? "argos"}`, + const attributePattern = + /\bandroid:(text|hint|contentDescription)="([^"]*)"/g; + const records: Array<{ + destination: string; + content: string; + values: string[]; + }> = []; + for (const directory of fs + .readdirSync(resourceRoot) + .filter((name) => /^layout(?:-.+)?$/.test(name))) { + const sourceDirectory = path.join(resourceRoot, directory); + for (const file of fs + .readdirSync(sourceDirectory) + .filter((name) => name.endsWith(".xml"))) { + const source = path.join(sourceDirectory, file); + const content = fs.readFileSync(source, "utf8"); + const values = Array.from(content.matchAll(attributePattern)) + .map((match) => match[2]!) + .filter((value) => cjk.test(value)); + if (!values.length) continue; + const qualifier = directory.slice("layout".length); + records.push({ + destination: path.join( + resourceRoot, + `layout-${target}${qualifier}`, + file, + ), + content, + values, + }); + } + } + if (!records.length) return; + const translations = translateTexts( + records.flatMap((record) => record.values), + config, + projectRoot, ); + let translationIndex = 0; + for (const record of records) { + const translated = record.content.replace( + attributePattern, + (match, attribute: string, value: string) => { + if (!cjk.test(value)) return match; + const text = translations[translationIndex++]!; + return `android:${attribute}="${escapeXmlAttribute(text)}"`; + }, + ); + fs.mkdirSync(path.dirname(record.destination), { recursive: true }); + fs.writeFileSync(record.destination, translated); + } + console.log(` -> Added ${translationIndex} ${target} layout translations`); } function escapeXmlText(value: string): string { @@ -309,6 +474,10 @@ function escapeXmlText(value: string): string { .replace(/'/g, "\\'"); } +function escapeXmlAttribute(value: string): string { + return escapeXmlText(value).replaceAll('"', """); +} + function applySmaliPatches( workDir: string, patches: SmaliPatch[], diff --git a/jfmt.sh b/jfmt.sh index b7e92ad..f305ad0 100755 --- a/jfmt.sh +++ b/jfmt.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash -git ls-files -co --exclude-standard | grep '\.java$' | xargs java \ + +git ls-files -co --exclude-standard | grep '\.java$' | sed 's/ /\\ /g' | xargs java \ --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ -jar google-java-format-1.37.0-all-deps.jar --replace diff --git a/moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/InkPanel.class b/moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/InkPanel.class new file mode 100644 index 0000000000000000000000000000000000000000..f1802ff984093de05127d105a9dcd18e9f8612f3 GIT binary patch literal 7281 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 GBlN!wZb8HV literal 0 HcmV?d00001 diff --git a/moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/LayerPanel$1.class b/moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/LayerPanel$1.class deleted file mode 100644 index 24696d7de113adafbf0f08b51b80f5ea4e0d3cb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1448 zcmb7EZBG+H5Pr5TE!0Dxw15J>R)vBbRq?G#qK(ET}aykfB)pPsZtbBTB9Z zsu_qd>~Pp%nC{)|G~cDl*6d~)w{Rzg+YF1B4ObId6PuxhyT~)7l+T^0E{nZLj*?V6 zZI{jZNtZ65G2$^^=TwP_WA4vn$k0axYZFt6b{N%ci(#;)xwEsv{VrcwB}o(LjeyjY z*oJC5Er!WnHfK;xEk=!#^3V9BYpNYM;=OB%Gt_J61=C8Tc_$rDg%o2&=$2}v@QC5z zWqFGR!@?6R(Y1W#w|$1yUXrJJ`2=5`!8ys`c}>~lVOTQvvMQ2BCfbWIh37OaN5gWO z1%~lI&RhSg2=xaQYHAK@UV8fwAI_Lg2WX`}k5=k(`a>k>9mG|#8X{>Ku}G6G%pGF5 zFn@?lVfql+!dHxaq4OB7(KkuffWUS7T9KT^B&KLb4T~FHtZhOv=(*|77%dzS+yS8- zAUpL9Hw{=k0!tGp&_ouk2(Zv`>H?BG?%{qEB!>qS&RFL#gIV$~L?|pv$8(tP#$6(% zF=A&E-j49cI)}&jtcy>*@NWq3uMi0=M6qow(waaKkMWeG42A5&GrUNeA57+8;5$VTi!UV=OltVzr={K0HP^k%-BLuO|H%ZR z@q<6WA7z}qmY$@A;3aoEyEpU9%d<1T|NQ&~;4#)xh%wl{^z4c+wOtSFy33VvwFqp* zwQPuOUsh|vF7Z9#zu`(alZz=N7zRG@2DcrqYWC*#2T{=}BpDXCs`{?1+PlU|+pAUX zY2j~4u{*h`idxjm!Xaf*Ey+L&C42^}!JSE7A!kzWF=R`J7~J#h)z*2QA#LL&~Z$I3goB{ z$3sPwiv2B&8Bn-Wt8%Ql(#gLSA!HX0WTQ~Rk2z`6rA%){@m`+coWDJ9@ z+Hi?zRXgk!hVdxRPP4qpV&WtX18kJiWp~G~h!@h_uuRm|bEci>xUti_Ap-r%4K%J&TgK__L1JLY^e#Xvb#Ay)--au7EzQh0y%`LU_8>`X zh>;aAlAAfeQ112t&gaGskj-669AG&29V6f9ZwzDfO_B|;i!1cCLU{z^@Yv!H+%Tx|5H(E}z$aY5=MedB7*Do^O!k<;?a*5m zvzQCDv#==wlSvlySU>`cBsXI+`gcg5rUUM^W4a&4^pdoUHx{{XCiNwBoo)VXyJjud n5x6-q;0Zhkp&sH9tqCk)87m|W5ke21;#ty6%H~s`C5ENHpn0^V diff --git a/moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/LayerPanel.class b/moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/LayerPanel.class index bd4c7d94a328560027f772df16a18d09f4439b83..b83bbacfd89c8d5e878295cce29889d4cca12608 100644 GIT binary patch literal 12483 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 literal 10950 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 literal 4627 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 diff --git a/moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/PanelDocker.class b/moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/PanelDocker.class new file mode 100644 index 0000000000000000000000000000000000000000..8042f4cbc53fd3ec2471ad67e603e772bac70ded GIT binary patch literal 5915 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 literal 0 HcmV?d00001 diff --git a/moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/ToolsSheet.class b/moannotes-native/bin/main/rip/crit/ms/moannotes/nativebridge/ToolsSheet.class new file mode 100644 index 0000000000000000000000000000000000000000..624149b04894f1d812ad5f90b04af862c93539ed GIT binary patch literal 8048 zcmb7I3w&GUb^jk*(v{_##F24`LLP=BV9R!t5E8&90mpe@>?FnxArC^hmTzpuvaYz2 z^MIDZpsXz|(2ljRtSzNo(@~%VisL0)yRi;7)^;oHx^<6kEo<8Dk#6m}CrkD}_sWv| z2ut||-%sD;eCIpofBxru-y7#neF?xixz|92pv6sP6G=CfPmJUeBaW5HIC(pl$XNN* zA-msA4Gh|e9gdUE^$po}-atT5bI>|uCDK-AFtNS=pqdl(n{~N+?4((SAe9?Q<#Gn*2`-y*E>+ZWyRDOFq?5_hD2WdJ9?5&s z>u{Ngg;*q*owM`X-4rL#)=q}HHFWC!OK`b?dclf|I9rdEvC|u!1q?#s2z1_q1(V(!KD)m3INWk2-aYJ7}pDe9-#Gi zn~5733NR=`=h4j#-6eQ;3|VfUefOxHN!s+Dfg1($Cd3!*(BY&VxAHuLN=0rk(V=RY z1(Dupp#5=wbhC*qxLHtL80|~lLq`P~Hmmeo1Pv3?3(A+HY?%+X9?P{xauIZ*TOqfV zK}CB}fEs1Hi5}d_1JRaxXhB=a0iFf5x2*_^V%HWrIy-3x@4$WZ;1rcQN3216!)QM5 zWFqKQlQNL2JJL?huFoq|Xzwl)yVbCDJf{bL;>^yGXR?ZQow3)%9crg2XOxiUSu2b? z3FK5VyFaN?H6-pbVWFRxN)e1rr6WzkRGH`c@-Cs&rXcIZfC*c-s^M0i_Put_8FiDC z+=)Ro;t)YWBdQI1anQst(&QpeFE=^8Q`-2Q4HqRCGA0}_0t_e9kxnIt1KF$v;{;^dxE1VRBJD6Jbz1`!p3w zYR(Y7wA2R(2DMZwGaSYTsb;4(9wodDqz;AH=w0>8?vqY4bW)_&g!VSJ44+vGc>tG7Ljr--^P)&o|3)=FhcUaG`{ z27Y}y9hBvF1D_Bqoo1)$)DpolJgsr}sY$80XxX;T&OHj?WB9a*&uBhU9x1WJM8b(8 zfX|vZj?a-VoXkc)T?kA(UzEtkomKSR z3nqdHMerhCGH{|So0TKd#LM_1Pr!1?FO-!rBFddFotmhCt0LSva>&I;$ zsfU6#+DXUV>1BYrsj{@2G;<8UZQ^(EB`rl5qwRUEwER+}tUfAt3JH=#VHK(+(n>Lp zlplv>R?w?L1dECjW{L0QbI`jyuc%v(*g|yooj%%x<#{;JkwZ4V17wX_r8h0GV#~=8!a{{cvD8JJ{r5sx0B>H?$I@WYvS+lJz6J~ z>nmSXS6&wt_GsJ_##=g-l%iYg*Ypv_4|o(^%W9zB@A;vCLDyQJ%e&67o#U7JP>0=7 zs#M=^4VA@7&jPLwG_c5*m^vls~D=hWCcy_Pv=u`7)KWX$+Ida$!N7+C-E>ebqM z+rd}SyNBt1(qI#qVukmqE7}5sU~hG>`;W9A+si31br(rpi^G3m8(;5fJR^{uQ$4I zZnVG93@MjrbS^C_Ror4)daXjI)R{65Um>L3)ZkEltK|-+GJ*E{fy(oiWd+)MK;#4t zd8`r~Odri^#H&u7M1Gam;g{UiUfZvw1@YqcGw{^ps#wp#n*wj2fs8f6;{=A7Ce<0% zNdJJfY$fUJQ0XUcFwGZ&?KG=uNhS>vH>C06ZsuN{j~&0jab_YRStHjP!b{o3TxPpm%P&NDnU(8= z7qz*5k<#HmF7)u;apB^0dH`s$3grnAyUab;l#aMYkGS5H8|5bdHm*Thq{$0hu%AaU7(W{vC!b@aWw+Y-A!i^Lk(=cfL$l!( zrCm~)LnO^aw+Amyv<`3=$31FFmvl1{MoL|Rua?tvVWlrz(@xsXE$e1J8qKzqnQJ;j zPFlF*Xx1*De({6;mhZf_95n^W9acKs$NbnfCnQ^Hz#J*kW6Dw-X2*7R6#8y6Ww~6* zz8$5$J*G5yeY;D2`%OuBeRq0&VU;&vN*lslo2)^&ATMRg4c@#Vb})x^W_TUzNL#s_ zoil_ab@{Y_oyt-4@|5eEA$OO(85GIQtBJV>1DNkeJ`EW{oHkGFQ6QqS@_KsR$_SZO z%Nj!6mrsMwOdv4is9^CsPbH{*#9$SfwUYN*(t*~rY?-{F@YRkzRs5CUSNILaPa<-H zjS5ZxCu(OMX7R7-Prz)<;jb#CEcS45Tk8N<~r@fwo@m2pG=dgE(U+O@5LSRi`c7+U$cX$$J&h(jhh}^cE%*xG_`Xhj zdO0WaI>*q=CW>zu^_mduV#(tk~km2ny^h;uw4@9kyU&JyB6Kj%CU9WCGFTP8?Z+@ zuva$W4%y7UPTVeC*e88_Z`z0b(vQ2u9!m)2lB`mbKJa7Iic z<`O?*e#Vgcw=w+taSfs9f3Ov=SvXS&{Z}l=rG}}p|9cGo6AO5O6blgT=M>84V}Tc` zs)t*+#3S5y_-lqOr#zmN{`Y5_Yh%+h`dpv?p|!byKoEM zjXvCmJ$Mhje?N`)01fsaB|OBx@1=zk{9kszL-qtsWm zctR?O*b46akOYX^O77)w<*UTV2G|m!yMx?!pM*V(HVrY)bIbA(zpW$ucMdB(WSy1Jd4_JdB%#nvg$8!Tu=aLNKw6iEg4(cJXB|l;b#RXhCMD1*M-UDtyQrqKheA*mk@)YSe2I{ z{QT4!iA8wu1*cKSFx9c@W=$~FeuAm4l}njnVv$%idAm5vjG4>_tueD!=4&3}H|9?> zIgPtSCMTqFq#qzN10Sa?K0$W=B!4|kZheO6`ZR7NF1twdefTWy;+xnoK1cjNOI&}R zIDQU~;(0uQ7l`67aQ2HFIYDlE38(n_^eS6#;1zs>q4gF{dbWMYw{X#4XNvwhQ}oxF zg1;hsXzFUV!YR*0G%mxUg>`}mHzh^;U+d{`FC68iT(zP{H}g9tU; zC6|*&tEtV^as^wa7Y~b+DZ2{zEa2Lw%(H|~O++-n{UT%q&7+VCu#T8ogRJllA?qlqS=$wJdZ-~?5} zx3{%ulf>{{@^>ZERA6l&-clDBmvyvhdr9*vxIOTG3i$wC{|IT~gRBxiM9O@W<-=oy z_2b^gaqonQ!byT? z!MY+OTiLfwZk1lI)h9dUcCWQd_R1YzYoFXDme=YRTMl@wK{+VHlBSF}f-)izaY9x2 cxMW#1@Nt4koL|Y|n2d(1_y}-B?m_JT0C~g?IRF3v literal 0 HcmV?d00001 diff --git a/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/InkPanel.java b/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/InkPanel.java new file mode 100644 index 0000000..df8a9b4 --- /dev/null +++ b/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/InkPanel.java @@ -0,0 +1,141 @@ +package rip.crit.ms.moannotes.nativebridge; + +import android.app.Activity; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; +import android.view.Gravity; +import android.view.View; +import android.widget.GridLayout; +import android.widget.LinearLayout; +import android.widget.TextView; +import java.lang.reflect.Field; + +/** + * Ink docker: monochrome pen selector (white plus named greys through black) with a persistent + * default. The choice is written straight into {@code HandwriteView.mColorARGB} via reflection, so + * new strokes use it immediately, and re-applied on every editor install so reopening the note + * keeps the ink. + */ +public final class InkPanel { + private static final String KEY = "ink_argb"; + private static final int[] INKS = { + 0xFFFFFFFF, 0xFFD9D9D9, 0xFFB3B3B3, 0xFF808080, 0xFF595959, 0xFF333333, 0xFF000000, + }; + private static final String[] NAMES = { + "White", "Silver", "Grey", "Mid grey", "Dark grey", "Charcoal", "Black", + }; + + private InkPanel() {} + + public static void show(Activity activity) { + Object canvas = NativeFeatureHooks.findCanvas(activity); + int current = saved(activity); + if (canvas != null) { + Integer live = readColor(canvas); + if (live != null) current = live; + } + final int selected = current; + + LinearLayout body = new LinearLayout(activity); + body.setOrientation(LinearLayout.VERTICAL); + + TextView hint = new TextView(activity); + hint.setText("Monochrome ink"); + hint.setTextColor(0xFF616161); + hint.setTextSize(13); + body.addView(hint); + + GridLayout grid = new GridLayout(activity); + grid.setColumnCount(4); + grid.setAlignmentMode(GridLayout.ALIGN_BOUNDS); + int gap = PanelDocker.dp(activity, 10); + grid.setPadding(0, gap, 0, gap); + for (int i = 0; i < INKS.length; i++) { + final int color = INKS[i]; + View swatch = new View(activity); + int size = PanelDocker.dp(activity, 44); + GridLayout.LayoutParams params = new GridLayout.LayoutParams(); + params.width = size; + params.height = size; + params.setMargins(gap, gap, gap, gap); + params.setGravity(Gravity.CENTER); + swatch.setLayoutParams(params); + swatch.setContentDescription(NAMES[i] + " ink"); + swatch.setBackground(ring(color, color == selected)); + swatch.setOnClickListener( + v -> { + apply(activity, NativeFeatureHooks.findCanvas(activity), color); + show(activity); + }); + grid.addView(swatch); + } + body.addView(grid); + + TextView name = new TextView(activity); + name.setText("Current: " + nameOf(selected)); + name.setTextColor(Color.BLACK); + name.setTextSize(15); + body.addView(name); + + PanelDocker.show(activity, "Ink", body); + } + + /** Re-applies the saved ink after the editor (re)creates its canvas. */ + static void applySaved(Activity activity, Object canvas) { + if (canvas == null) return; + Integer live = readColor(canvas); + int saved = saved(activity); + if (live != null && live == saved) return; + writeColor(canvas, saved); + } + + private static void apply(Activity activity, Object canvas, int color) { + saved(activity, color); + if (canvas != null) writeColor(canvas, color); + } + + private static GradientDrawable ring(int fill, boolean selected) { + GradientDrawable drawable = new GradientDrawable(); + drawable.setShape(GradientDrawable.OVAL); + drawable.setColor(fill); + drawable.setStroke(selected ? 6 : 2, selected ? 0xFF1A1A1A : 0xFF9E9E9E); + return drawable; + } + + private static Integer readColor(Object canvas) { + try { + Field field = canvas.getClass().getDeclaredField("mColorARGB"); + field.setAccessible(true); + return (Integer) field.get(canvas); + } catch (Exception ignored) { + return null; + } + } + + private static void writeColor(Object canvas, int color) { + try { + Field field = canvas.getClass().getDeclaredField("mColorARGB"); + field.setAccessible(true); + field.setInt(canvas, color); + } catch (Exception ignored) { + } + } + + private static int saved(Activity activity) { + return prefs(activity).getInt(KEY, 0xFF000000); + } + + private static void saved(Activity activity, int color) { + prefs(activity).edit().putInt(KEY, color).apply(); + } + + private static SharedPreferences prefs(Activity activity) { + return activity.getSharedPreferences(NativeFeatureHooks.PREFS, 0); + } + + private static String nameOf(int color) { + for (int i = 0; i < INKS.length; i++) if (INKS[i] == color) return NAMES[i]; + return String.format("#%06X", color & 0xFFFFFF); + } +} diff --git a/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/LayerPanel.java b/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/LayerPanel.java index 6985317..b32cd7a 100644 --- a/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/LayerPanel.java +++ b/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/LayerPanel.java @@ -2,154 +2,279 @@ package rip.crit.ms.moannotes.nativebridge; import android.app.Activity; import android.graphics.Color; -import android.graphics.drawable.GradientDrawable; import android.view.Gravity; import android.view.View; -import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.LinearLayout; -import android.widget.PopupWindow; -import android.widget.ScrollView; -import android.widget.SeekBar; -import android.widget.Spinner; import android.widget.TextView; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; +/** + * Layers docker, wired to the engine's real layer model. + * + *

How the stock renderer works (verified in smali): the live page object ({@code Lh/f/b/u5/a/b}) + * owns the layer runtimes in list {@code a} and the draw target in {@code c}; {@code dispatchDraw} + * composites every entry of {@code a} unconditionally. There is no per-layer visibility channel, so + * a hide/show toggle cannot exist — it is intentionally absent here. What the engine honors, and + * what this panel does: + * + *

    + *
  • Active layer: {@code mEditLayerID} plus the live pointer {@code c = a.get(id)}, saved and + * refreshed. New strokes attach to that layer. + *
  • Add / delete: config op on the live object, saved, then the current page is reloaded + * through the stock queue ({@code saveCurPage} first so no stroke is lost), which rebuilds + * the runtime list from disk. + *
+ */ public final class LayerPanel { private LayerPanel() {} public static void show(Activity activity) { try { Object canvas = NativeFeatureHooks.findCanvas(activity); - if (canvas == null) return; - Object page = publicField(canvas, "mPageConfigData"); - int pageId = ((Integer) publicField(page, "mEditPage")).intValue(); + if (canvas == null) { + warn("show: no canvas"); + return; + } Class layerClass = Class.forName("com.moan.notes.hand.data.config.LayerConfigData"); - Object config = - layerClass - .getMethod( - "sLoadLayerConfig", - android.content.Context.class, - page.getClass(), - int.class, - layerClass) - .invoke(null, activity, page, pageId, null); - if (config == null) return; - popup(activity, canvas, config, layerClass); + // Operate on the renderer's LIVE config (mCurPage.i). Mutating a + // separately loaded copy only reached disk, so edits never showed. + Object config = liveConfig(canvas); + if (config == null) config = loadFromDisk(activity, canvas, layerClass); + if (config == null) { + warn("show: no layer config"); + return; + } + PanelDocker.show(activity, "Layers", body(activity, canvas, config, layerClass)); + } catch (Exception error) { + warn("show: " + error); + } + } + + private static void warn(String message) { + try { + android.util.Log.w("MiStringLayers", message); } catch (Exception ignored) { } } - private static void popup(Activity activity, Object canvas, Object config, Class layerClass) + /** Returns the live {@code LayerConfigData} the renderer draws from, if loaded. */ + private static Object liveConfig(Object canvas) { + try { + Object curPage = declaredField(canvas, "mCurPage"); + if (curPage == null) return null; + return declaredField(curPage, "i"); + } catch (Exception ignored) { + return null; + } + } + + private static Object loadFromDisk(Activity activity, Object canvas, Class layerClass) throws Exception { - LinearLayout list = new LinearLayout(activity); - list.setOrientation(LinearLayout.VERTICAL); - list.setPadding(dp(activity, 16), dp(activity, 16), dp(activity, 16), dp(activity, 16)); - list.setBackgroundColor(Color.WHITE); - TextView title = new TextView(activity); - title.setText("Layers"); - title.setTextColor(Color.BLACK); - title.setTextSize(20); - list.addView(title); + Object page = field(canvas, "mPageConfigData"); + if (page == null) return null; + int pageId = ((Integer) field(page, "mEditPage")).intValue(); + return layerClass + .getMethod( + "sLoadLayerConfig", + android.content.Context.class, + page.getClass(), + int.class, + layerClass) + .invoke(null, activity, page, pageId, null); + } + + private static LinearLayout body( + Activity activity, Object canvas, Object config, Class layerClass) throws Exception { @SuppressWarnings("unchecked") - ArrayList visible = (ArrayList) publicField(config, "mEditLayerVisible"); - String parent = (String) publicField(config, "mParentPath"); - for (int index = visible.size() - 1; index >= 0; index--) { + ArrayList folders = (ArrayList) field(config, "mLayerDataFolders"); + int count = folders.size(); + int active = ((Integer) field(config, "mEditLayerID")).intValue(); + + LinearLayout body = new LinearLayout(activity); + body.setOrientation(LinearLayout.VERTICAL); + + TextView hint = new TextView(activity); + hint.setText("Tap a row to draw on that layer"); + hint.setTextColor(0xFF616161); + hint.setTextSize(13); + body.addView(hint); + + for (int index = count - 1; index >= 0; index--) { final int layer = index; + boolean isActive = layer == active; + LinearLayout row = new LinearLayout(activity); row.setGravity(Gravity.CENTER_VERTICAL); - Button eye = button(activity, visible.get(index) != 0 ? "Hide" : "Show"); - eye.setOnClickListener( - v -> { - try { - visible.set(layer, visible.get(layer) == 0 ? 1 : 0); - save(activity, config, layerClass); - refresh(canvas); - show(activity); - } catch (Exception ignored) { - } - }); + int vPad = PanelDocker.dp(activity, 10); + row.setPadding(0, vPad, 0, vPad); + + // Active-layer indicator dot. + View dot = new View(activity); + int dotSize = PanelDocker.dp(activity, 10); + LinearLayout.LayoutParams dotParams = new LinearLayout.LayoutParams(dotSize, dotSize); + dotParams.rightMargin = PanelDocker.dp(activity, 10); + dot.setLayoutParams(dotParams); + android.graphics.drawable.GradientDrawable dotBg = + new android.graphics.drawable.GradientDrawable(); + dotBg.setShape(android.graphics.drawable.GradientDrawable.OVAL); + dotBg.setColor(isActive ? 0xFF1A1A1A : 0x00000000); + dotBg.setStroke(PanelDocker.dp(activity, 1), 0xFF9E9E9E); + dot.setBackground(dotBg); + row.addView(dot); + TextView name = new TextView(activity); - name.setText("Layer " + (index + 1)); + name.setText("Layer " + (layer + 1) + (isActive ? " · active" : "")); name.setTextColor(Color.BLACK); name.setTextSize(16); - name.setPadding(dp(activity, 12), 0, dp(activity, 12), 0); - Button remove = button(activity, "Remove"); - remove.setEnabled(visible.size() > 1); - remove.setOnClickListener( + row.addView(name, new LinearLayout.LayoutParams(0, -2, 1)); + + row.setOnClickListener( v -> { try { - layerClass - .getMethod( - "sDeleteLayerFiles", android.content.Context.class, layerClass, int.class) - .invoke(null, activity, config, layer); - refresh(canvas); + selectActive(activity, canvas, config, layerClass, layer); show(activity); - } catch (Exception ignored) { + } catch (Exception error) { + warn("row/add/delete: " + error); } }); - row.addView(eye); - row.addView(name, new LinearLayout.LayoutParams(0, -2, 1)); - row.addView(remove); - list.addView(row); - SeekBar opacity = new SeekBar(activity); - opacity.setMax(255); - opacity.setProgress(LayerSettings.opacity(activity, parent, layer)); - opacity.setContentDescription("Layer opacity"); - opacity.setOnSeekBarChangeListener( - new SeekBar.OnSeekBarChangeListener() { - public void onProgressChanged(SeekBar bar, int value, boolean user) { - if (user) LayerSettings.opacity(activity, parent, layer, value); - } - - public void onStartTrackingTouch(SeekBar bar) {} - - public void onStopTrackingTouch(SeekBar bar) {} - }); - list.addView(opacity); - Spinner blend = new Spinner(activity); - String[] modes = {"normal", "multiply", "screen", "overlay", "darken", "lighten"}; - blend.setAdapter( - new ArrayAdapter<>(activity, android.R.layout.simple_spinner_dropdown_item, modes)); - String current = LayerSettings.blend(activity, parent, layer); - for (int mode = 0; mode < modes.length; mode++) - if (modes[mode].equals(current)) blend.setSelection(mode); - blend.setOnItemSelectedListener( - new android.widget.AdapterView.OnItemSelectedListener() { - public void onItemSelected( - android.widget.AdapterView p, View v, int position, long itemId) { - LayerSettings.blend(activity, parent, layer, modes[position]); - } - - public void onNothingSelected(android.widget.AdapterView p) {} - }); - list.addView(blend); + body.addView(row); + if (index > 0) body.addView(PanelDocker.hairline(activity)); } - Button add = button(activity, "Add layer"); + + body.addView(PanelDocker.hairline(activity)); + LinearLayout actions = new LinearLayout(activity); + actions.setOrientation(LinearLayout.VERTICAL); + int top = PanelDocker.dp(activity, 8); + actions.setPadding(0, top, 0, 0); + + Button add = actionButton(activity, "Add layer"); add.setOnClickListener( v -> { try { - int count = ((ArrayList) publicField(config, "mLayerDataFolders")).size(); + Object live = liveConfig(NativeFeatureHooks.findCanvas(activity)); + Object target = live != null ? live : config; + int size = ((ArrayList) field(target, "mLayerDataFolders")).size(); + savePage(canvas); layerClass .getMethod("sAddLayerFiles", android.content.Context.class, layerClass, int.class) - .invoke(null, activity, config, count); - refresh(canvas); + .invoke(null, activity, target, size); + save(activity, target, layerClass); + reloadPage(canvas); show(activity); } catch (Exception ignored) { } }); - list.addView(add); - ScrollView scroll = new ScrollView(activity); - scroll.addView(list); - PopupWindow window = new PopupWindow(scroll, dp(activity, 300), -2, true); - window.setBackgroundDrawable(surface()); - window.setElevation(dp(activity, 8)); - window.showAtLocation( - activity.getWindow().getDecorView(), - Gravity.RIGHT | Gravity.TOP, - dp(activity, 12), - dp(activity, 64)); + actions.addView(add); + + Button remove = actionButton(activity, "Delete active layer"); + remove.setEnabled(count > 1 && active >= 0 && active < count); + remove.setOnClickListener( + v -> { + try { + Object live = liveConfig(NativeFeatureHooks.findCanvas(activity)); + Object target = live != null ? live : config; + savePage(canvas); + Object next = + layerClass + .getMethod( + "sDeleteLayerFiles", android.content.Context.class, layerClass, int.class) + .invoke(null, activity, target, active); + // sDeleteLayerFiles shrinks the lists but leaves mEditLayerID + // stale; an over-large id crashes note open in the page loader. + // Honor its returned index, clamped into range. + int size = ((ArrayList) field(target, "mLayerDataFolders")).size(); + int fixed = ((Integer) next).intValue(); + if (fixed < 0) fixed = 0; + if (size > 0 && fixed > size - 1) fixed = size - 1; + layerClass.getField("mEditLayerID").set(target, fixed); + save(activity, target, layerClass); + reloadPage(canvas); + show(activity); + } catch (Exception ignored) { + } + }); + actions.addView(remove); + + Button export = actionButton(activity, "Export KRA"); + export.setOnClickListener( + v -> { + PanelDocker.dismiss(); + Object target = NativeFeatureHooks.findCanvas(activity); + if (target != null) KraExporter.exportCurrentPage(activity, target); + }); + actions.addView(export); + body.addView(actions); + return body; + } + + /** + * Switches the draw target: config id plus the live runtime pointer ({@code b.c = b.a.get(id)}), + * which is what new strokes actually follow. Setting the id alone left strokes landing on the + * previous layer. + */ + private static void selectActive( + Activity activity, Object canvas, Object config, Class layerClass, int layer) + throws Exception { + Object page = livePage(canvas); + if (page != null) { + @SuppressWarnings("unchecked") + ArrayList runtimes = (ArrayList) field(page, "a"); + if (layer >= 0 && layer < runtimes.size()) { + page.getClass().getField("c").set(page, runtimes.get(layer)); + } + } + layerClass.getField("mEditLayerID").set(config, layer); + save(activity, config, layerClass); + refresh(canvas); + } + + /** + * Reloads the current page through the stock queue (same sequence as page turn) so the runtime + * layer list is rebuilt from disk after add/delete. + */ + private static void reloadPage(Object canvas) throws Exception { + Object queue = field(canvas, "queueManager"); + int pageIdx = ((Integer) canvas.getClass().getMethod("getPageIdx").invoke(canvas)).intValue(); + canvas + .getClass() + .getMethod("addLoadPageTask", queue.getClass(), int.class) + .invoke(canvas, queue, pageIdx); + canvas + .getClass() + .getMethod("addReportTask", queue.getClass(), int.class, int.class) + .invoke(canvas, queue, 0, 1); + Method kick = null; + for (Method method : queue.getClass().getMethods()) { + if (method.getName().equals("d") && method.getParameterTypes().length == 2) kick = method; + } + if (kick != null) kick.invoke(queue, false, false); + refresh(canvas); + } + + private static Object livePage(Object canvas) { + try { + return declaredField(canvas, "mCurPage"); + } catch (Exception ignored) { + return null; + } + } + + private static void savePage(Object canvas) { + try { + canvas.getClass().getMethod("saveCurPage").invoke(canvas); + } catch (Exception ignored) { + } + } + + private static Button actionButton(Activity activity, String label) { + Button button = new Button(activity); + button.setText(label); + button.setTextColor(Color.BLACK); + button.setAllCaps(false); + return button; } private static void save(Activity activity, Object config, Class layerClass) throws Exception { @@ -162,27 +287,14 @@ public final class LayerPanel { canvas.getClass().getMethod("refreshUILayer").invoke(canvas); } - private static Object publicField(Object o, String name) throws Exception { - Field f = o.getClass().getField(name); - return f.get(o); + private static Object field(Object object, String name) throws Exception { + Field field = object.getClass().getField(name); + return field.get(object); } - private static Button button(Activity a, String label) { - Button b = new Button(a); - b.setText(label); - b.setTextColor(Color.BLACK); - b.setAllCaps(false); - return b; - } - - private static int dp(Activity a, int value) { - return Math.round(value * a.getResources().getDisplayMetrics().density); - } - - private static GradientDrawable surface() { - GradientDrawable g = new GradientDrawable(); - g.setColor(Color.WHITE); - g.setCornerRadius(24); - return g; + private static Object declaredField(Object object, String name) throws Exception { + Field field = object.getClass().getDeclaredField(name); + field.setAccessible(true); + return field.get(object); } } diff --git a/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/NativeFeatureHooks.java b/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/NativeFeatureHooks.java index f53ef13..566d04a 100644 --- a/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/NativeFeatureHooks.java +++ b/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/NativeFeatureHooks.java @@ -1,72 +1,158 @@ package rip.crit.ms.moannotes.nativebridge; import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; +import android.view.Gravity; import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.ImageButton; -import android.widget.LinearLayout; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +/** + * Single integration point for the note-editor patches. + * + *

The stock toolbar is never touched: no buttons are added, no icons are replaced. All patch UI + * lives in one external floating action button anchored to the window decor (bottom-end, clear of + * the page bar), which opens the {@link ToolsSheet} bottom docker. Native editor UI (toolbar, + * menus, background picker) keeps working exactly as shipped. + * + *

The smali-side snap listener is touched through reflection only, so this module still compiles + * without the injected patch classes on the classpath. + */ public final class NativeFeatureHooks { + static final String PREFS = "moannotes_patch"; + static final String SNAP_KEY = "snap_to_line_enabled"; + private static final String LISTENER_CLASS = "rip.crit.ms.moannotes.SnapToLineTouchListener"; + private NativeFeatureHooks() {} public static void install(Activity activity) { - LinearLayout bar = findBar(activity); - if (bar == null || activity.findViewById(id(activity, "moannotes_kra_export_button")) != null) - return; - add( - activity, - bar, - "moannotes_layers_button", - "icp_layers", - "Layers", - v -> LayerPanel.show(activity)); - add( - activity, - bar, - "moannotes_kra_export_button", - "icp_kra_export", - "Export KRA", - v -> { - Object canvas = findCanvas(activity); - if (canvas != null) KraExporter.exportCurrentPage(activity, canvas); - }); + View handwrite = findHandwriteView(activity); + if (handwrite == null) return; + ensureSnapListener(activity, handwrite); + ensureFab(activity); + InkPanel.applySaved(activity, findCanvas(activity)); } - static Object findCanvas(Activity activity) { + // ---- external floating entry point (decor only, never the toolbar) ------ + + private static void ensureFab(Activity activity) { + int fabId = PanelDocker.id(activity, "moannotes_tools_fab"); + View root = activity.getWindow().getDecorView(); + if (!(root instanceof ViewGroup)) return; + if (fabId != 0 && activity.findViewById(fabId) != null) return; + ImageButton fab = new ImageButton(activity); + if (fabId != 0) fab.setId(fabId); + fab.setContentDescription("Patch tools"); + int icon = PanelDocker.id(activity, "icp_tools"); + if (icon != 0) fab.setImageResource(icon); + fab.setScaleType(ImageButton.ScaleType.FIT_CENTER); + fab.setBackground(fabFace()); + int size = PanelDocker.dp(activity, 56); + int pad = PanelDocker.dp(activity, 14); + fab.setPadding(pad, pad, pad, pad); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(size, size); + params.gravity = Gravity.BOTTOM | Gravity.END; + params.rightMargin = PanelDocker.dp(activity, 16); + params.bottomMargin = PanelDocker.dp(activity, 88); + fab.setLayoutParams(params); + if (android.os.Build.VERSION.SDK_INT >= 21) fab.setElevation(PanelDocker.dp(activity, 6)); + fab.setOnClickListener(v -> ToolsSheet.show(activity)); + ((ViewGroup) root).addView(fab); + } + + private static GradientDrawable fabFace() { + GradientDrawable drawable = new GradientDrawable(); + drawable.setShape(GradientDrawable.OVAL); + drawable.setColor(Color.WHITE); + drawable.setStroke(2, 0xFFE0E0E0); + return drawable; + } + + // ---- snap-to-line (reflection: class lives in injected smali) ------------ + + private static void ensureSnapListener(Activity activity, View handwrite) { + if (taggedListener(activity, handwrite) != null) return; try { - return activity.getClass().getField("g").get(activity); + Class type = Class.forName(LISTENER_CLASS); + Constructor init = type.getConstructor(Context.class, boolean.class); + Object listener = init.newInstance(activity, prefs(activity).getBoolean(SNAP_KEY, false)); + handwrite.setOnTouchListener((View.OnTouchListener) listener); + int tagId = snapTagId(activity); + if (tagId != 0) handwrite.setTag(tagId, listener); } catch (Exception ignored) { - return null; } } - private static LinearLayout findBar(Activity activity) { - View view = activity.findViewById(id(activity, "edit_button_bar")); - return view instanceof LinearLayout ? (LinearLayout) view : null; + static Object snapListener(Activity activity, View handwrite) { + Object listener = taggedListener(activity, handwrite); + if (listener != null) return listener; + ensureSnapListener(activity, handwrite); + return taggedListener(activity, handwrite); } - private static void add( - Activity a, - LinearLayout bar, - String idName, - String drawable, - String description, - View.OnClickListener action) { - ImageButton button = new ImageButton(a); - button.setId(id(a, idName)); - button.setContentDescription(description); - button.setBackgroundColor(Color.TRANSPARENT); - button.setImageResource(id(a, drawable)); - int size = Math.round(48 * a.getResources().getDisplayMetrics().density); - button.setLayoutParams(new LinearLayout.LayoutParams(size, -1)); - button.setOnClickListener(action); - bar.addView(button); + private static Object taggedListener(Activity activity, View handwrite) { + int tagId = snapTagId(activity); + if (tagId == 0) return null; + try { + Object tag = handwrite.getTag(tagId); + if (tag != null && tag.getClass().getName().equals(LISTENER_CLASS)) return tag; + } catch (Exception ignored) { + } + return null; } - static int id(Activity activity, String name) { - return activity - .getResources() - .getIdentifier( - name, name.startsWith("icp_") ? "drawable" : "id", activity.getPackageName()); + private static int snapTagId(Activity activity) { + return PanelDocker.id(activity, "moannotes_tools_fab"); + } + + static boolean isSnapEnabled(Object listener) { + try { + Method isEnabled = listener.getClass().getMethod("isEnabled"); + return Boolean.TRUE.equals(isEnabled.invoke(listener)); + } catch (Exception ignored) { + return false; + } + } + + static void setSnapEnabled(Activity activity, Object listener, boolean enabled) { + try { + Method setEnabled = listener.getClass().getMethod("setEnabled", boolean.class); + setEnabled.invoke(listener, enabled); + } catch (Exception ignored) { + return; + } + prefs(activity).edit().putBoolean(SNAP_KEY, enabled).apply(); + } + + static boolean snapEnabled(Activity activity) { + return prefs(activity).getBoolean(SNAP_KEY, false); + } + + // ---- shared lookups ------------------------------------------------------ + + static Object findCanvas(Activity activity) { + // NoteActivity.g is the public HandwriteView field. + try { + Field field = activity.getClass().getField("g"); + Object canvas = field.get(activity); + if (canvas != null) return canvas; + } catch (Exception ignored) { + } + return findHandwriteView(activity); + } + + static View findHandwriteView(Activity activity) { + return activity.findViewById(PanelDocker.id(activity, "note_handwrite_view")); + } + + static SharedPreferences prefs(Activity activity) { + return activity.getSharedPreferences(PREFS, 0); } } diff --git a/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/PanelDocker.java b/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/PanelDocker.java new file mode 100644 index 0000000..6185eb5 --- /dev/null +++ b/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/PanelDocker.java @@ -0,0 +1,122 @@ +package rip.crit.ms.moannotes.nativebridge; + +import android.app.Activity; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; +import android.view.Gravity; +import android.view.View; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.ScrollView; +import android.widget.TextView; + +/** + * Shared chrome for all patch floaters (layers + ink). One visible panel at a time, same rounded + * surface everywhere so the patches read as one UI. + */ +public final class PanelDocker { + private static PopupWindow current; + + private PanelDocker() {} + + /** Shows {@code content} in the shared rounded floater, replacing any open panel. */ + public static void show(Activity activity, String title, View content) { + dismiss(); + LinearLayout card = new LinearLayout(activity); + card.setOrientation(LinearLayout.VERTICAL); + card.setBackground(surface()); + int pad = dp(activity, 16); + card.setPadding(pad, dp(activity, 12), pad, pad); + if (android.os.Build.VERSION.SDK_INT >= 21) card.setElevation(dp(activity, 8)); + + LinearLayout header = new LinearLayout(activity); + header.setGravity(Gravity.CENTER_VERTICAL); + TextView label = new TextView(activity); + label.setText(title); + label.setTextColor(Color.BLACK); + label.setTextSize(18); + LinearLayout.LayoutParams labelParams = + new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1); + header.addView(label, labelParams); + ImageButton close = new ImageButton(activity); + close.setContentDescription("Close panel"); + close.setBackgroundColor(Color.TRANSPARENT); + close.setImageResource(id(activity, "icp_close")); + int touch = dp(activity, 44); + close.setLayoutParams(new LinearLayout.LayoutParams(touch, touch)); + close.setOnClickListener(v -> dismiss()); + header.addView(close); + card.addView(header); + + View divider = new View(activity); + divider.setBackgroundColor(0xFFE0E0E0); + LinearLayout.LayoutParams dividerParams = + new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, dp(activity, 1)); + dividerParams.topMargin = dp(activity, 8); + dividerParams.bottomMargin = dp(activity, 8); + card.addView(divider, dividerParams); + + ScrollView scroll = new ScrollView(activity); + scroll.addView(content); + card.addView(scroll); + + PopupWindow window = new PopupWindow(card, dp(activity, 300), dp(activity, 420), true); + window.setBackgroundDrawable(surface()); + window.setOnDismissListener(() -> current = null); + current = window; + window.showAtLocation( + activity.getWindow().getDecorView(), + Gravity.END | Gravity.TOP, + dp(activity, 12), + dp(activity, 72)); + } + + public static void dismiss() { + if (current != null) { + try { + current.dismiss(); + } catch (Exception ignored) { + } + current = null; + } + } + + /** Top-rounded face for the bottom tools sheet; same white surface as cards. */ + static android.graphics.drawable.GradientDrawable sheetFace() { + android.graphics.drawable.GradientDrawable drawable = + new android.graphics.drawable.GradientDrawable(); + drawable.setColor(Color.WHITE); + drawable.setCornerRadii(new float[] {48, 48, 48, 48, 0, 0, 0, 0}); + drawable.setStroke(2, 0xFFE0E0E0); + return drawable; + } + + static GradientDrawable surface() { + GradientDrawable drawable = new GradientDrawable(); + drawable.setColor(Color.WHITE); + drawable.setCornerRadius(48); + drawable.setStroke(2, 0xFFE0E0E0); + return drawable; + } + + static int dp(Activity activity, int value) { + return Math.round(value * activity.getResources().getDisplayMetrics().density); + } + + static int id(Activity activity, String name) { + return activity + .getResources() + .getIdentifier( + name, name.startsWith("icp_") ? "drawable" : "id", activity.getPackageName()); + } + + /** Full-width hairline used between rows (launcher lists and panel rows). */ + public static View hairline(Activity activity) { + View line = new View(activity); + line.setBackgroundColor(0xFFE0E0E0); + line.setLayoutParams( + new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, dp(activity, 1))); + return line; + } +} diff --git a/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/ToolsSheet.java b/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/ToolsSheet.java new file mode 100644 index 0000000..68620c9 --- /dev/null +++ b/moannotes-native/src/main/java/rip/crit/ms/moannotes/nativebridge/ToolsSheet.java @@ -0,0 +1,185 @@ +package rip.crit.ms.moannotes.nativebridge; + +import android.app.Activity; +import android.graphics.Color; +import android.view.Gravity; +import android.view.View; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.TextView; + +/** + * Bottom docker listing every patch action in one place: Layers, Ink, snap toggle, Export KRA. + * Opened from the external tools FAB; the stock toolbar and menus are left alone. Row icons are + * official ByteDance IconPark ports (see res/drawable/icp_*.xml provenance headers). + */ +public final class ToolsSheet { + private static PopupWindow current; + + private ToolsSheet() {} + + public static void show(Activity activity) { + dismiss(); + LinearLayout sheet = new LinearLayout(activity); + sheet.setOrientation(LinearLayout.VERTICAL); + sheet.setBackground(PanelDocker.sheetFace()); + int hPad = PanelDocker.dp(activity, 8); + sheet.setPadding(hPad, PanelDocker.dp(activity, 8), hPad, PanelDocker.dp(activity, 16)); + + LinearLayout header = new LinearLayout(activity); + header.setGravity(Gravity.CENTER_VERTICAL); + header.setPadding(PanelDocker.dp(activity, 12), 0, PanelDocker.dp(activity, 4), 0); + TextView label = new TextView(activity); + label.setText("Tools"); + label.setTextColor(Color.BLACK); + label.setTextSize(18); + header.addView(label, new LinearLayout.LayoutParams(0, -2, 1)); + ImageButton close = new ImageButton(activity); + close.setContentDescription("Close tools"); + close.setBackgroundColor(Color.TRANSPARENT); + int closeIcon = PanelDocker.id(activity, "icp_close"); + if (closeIcon != 0) close.setImageResource(closeIcon); + int touch = PanelDocker.dp(activity, 44); + close.setLayoutParams(new LinearLayout.LayoutParams(touch, touch)); + close.setOnClickListener(v -> dismiss()); + header.addView(close); + sheet.addView(header); + sheet.addView(PanelDocker.hairline(activity)); + + sheet.addView( + row( + activity, + "icp_layers", + "Layers", + "Show, reorder and export layers", + v -> { + dismiss(); + LayerPanel.show(activity); + })); + sheet.addView(PanelDocker.hairline(activity)); + sheet.addView( + row( + activity, + "icp_ink", + "Ink", + "Monochrome pen color", + v -> { + dismiss(); + InkPanel.show(activity); + })); + sheet.addView(PanelDocker.hairline(activity)); + sheet.addView(snapRow(activity)); + sheet.addView(PanelDocker.hairline(activity)); + sheet.addView( + row( + activity, + "icp_kra_export", + "Export KRA", + "Krita paint layers", + v -> { + dismiss(); + Object target = NativeFeatureHooks.findCanvas(activity); + if (target != null) KraExporter.exportCurrentPage(activity, target); + })); + + PopupWindow window = + new PopupWindow( + sheet, + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT, + true); + window.setBackgroundDrawable(PanelDocker.sheetFace()); + window.setOnDismissListener(() -> current = null); + current = window; + window.showAtLocation(activity.getWindow().getDecorView(), Gravity.BOTTOM, 0, 0); + } + + static void dismiss() { + if (current != null) { + try { + current.dismiss(); + } catch (Exception ignored) { + } + current = null; + } + } + + private static LinearLayout snapRow(Activity activity) { + View handwrite = NativeFeatureHooks.findHandwriteView(activity); + Object listener = + handwrite == null ? null : NativeFeatureHooks.snapListener(activity, handwrite); + boolean enabled = + listener == null + ? NativeFeatureHooks.snapEnabled(activity) + : NativeFeatureHooks.isSnapEnabled(listener); + LinearLayout row = + baseRow( + activity, + "icp_snap_line", + "Snap to line", + enabled ? "On: strokes stick to ruled baselines" : "Off"); + ImageView state = new ImageView(activity); + int check = PanelDocker.id(activity, "icp_check"); + if (enabled && check != 0) { + state.setImageResource(check); + state.setContentDescription("Snap on"); + } else { + state.setContentDescription("Snap off"); + } + int trailing = PanelDocker.dp(activity, 28); + state.setLayoutParams(new LinearLayout.LayoutParams(trailing, trailing)); + state.setAlpha(enabled ? 1.0f : 0.35f); + row.addView(state); + row.setOnClickListener( + v -> { + View target = NativeFeatureHooks.findHandwriteView(activity); + if (target == null) return; + Object active = NativeFeatureHooks.snapListener(activity, target); + if (active == null) return; + boolean next = !NativeFeatureHooks.isSnapEnabled(active); + NativeFeatureHooks.setSnapEnabled(activity, active, next); + show(activity); + }); + return row; + } + + private static LinearLayout row( + Activity activity, String icon, String title, String subtitle, View.OnClickListener action) { + LinearLayout row = baseRow(activity, icon, title, subtitle); + row.setOnClickListener(action); + return row; + } + + private static LinearLayout baseRow( + Activity activity, String icon, String title, String subtitle) { + LinearLayout row = new LinearLayout(activity); + row.setGravity(Gravity.CENTER_VERTICAL); + int vPad = PanelDocker.dp(activity, 10); + row.setPadding(PanelDocker.dp(activity, 12), vPad, PanelDocker.dp(activity, 12), vPad); + + ImageView glyph = new ImageView(activity); + int iconId = PanelDocker.id(activity, icon); + if (iconId != 0) glyph.setImageResource(iconId); + int glyphSize = PanelDocker.dp(activity, 28); + LinearLayout.LayoutParams glyphParams = new LinearLayout.LayoutParams(glyphSize, glyphSize); + glyphParams.rightMargin = PanelDocker.dp(activity, 16); + row.addView(glyph, glyphParams); + + LinearLayout texts = new LinearLayout(activity); + texts.setOrientation(LinearLayout.VERTICAL); + TextView name = new TextView(activity); + name.setText(title); + name.setTextColor(Color.BLACK); + name.setTextSize(16); + texts.addView(name); + TextView hint = new TextView(activity); + hint.setText(subtitle); + hint.setTextColor(0xFF616161); + hint.setTextSize(13); + texts.addView(hint); + row.addView(texts, new LinearLayout.LayoutParams(0, -2, 1)); + return row; + } +} diff --git a/package.json b/package.json index 7631031..2c900c7 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "scripts": { "postinstall": "patch-package", "pp": "git submodule update --init --recursive && mkdir -p java/ && cp -r apk_unpack/java/* java/ && cp -r apk_unpack/cli . && patch-package apk_unpack", - "format": "./jsfmt.sh && ./jfmt.sh && ./qmlfmt.sh && ./cppfmt.sh && ./rustfmt.sh", + "format": "./jsfmt.sh && ./jfmt.sh && ./cppfmt.sh && ./rustfmt.sh", "patch-apks": "bun run index.ts", "extract-eink": "bun run extractors/eink.ts", "kr": "bun run scripts/krita-patch.ts", @@ -37,6 +37,7 @@ "sign:moanlauncher": "bun run scripts/sign-moannotes.ts MoanLauncher", "patch:moanlauncher": "bun run patch-apks MoanLauncher && bun run sign:moanlauncher", "reinstall:moannotes": "bun run scripts/reinstall-moannotes.ts", + "reinstall:moanlauncher": "bun run scripts/reinstall-moannotes.ts --app MoanLauncher", "adbme": "bun run scripts/adbme.ts", "translator:test": "python3 -m unittest discover -s translator -p 'test_*.py'" }, diff --git a/patches/moanlauncher/res/layout/home_card_app_item.xml b/patches/moanlauncher/res/layout/home_card_app_item.xml index 6708b54..5e91a82 100644 --- a/patches/moanlauncher/res/layout/home_card_app_item.xml +++ b/patches/moanlauncher/res/layout/home_card_app_item.xml @@ -1,25 +1,39 @@ - - + + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + diff --git a/patches/moanlauncher/res/layout/item_apps.xml b/patches/moanlauncher/res/layout/item_apps.xml index daa4b1a..7e29afe 100644 --- a/patches/moanlauncher/res/layout/item_apps.xml +++ b/patches/moanlauncher/res/layout/item_apps.xml @@ -1,25 +1,39 @@ - - + + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + diff --git a/patches/moanlauncher/smali/rip/crit/ms/launcher/XiaomiProfile.smali b/patches/moanlauncher/smali/rip/crit/ms/launcher/XiaomiProfile.smali index e3dd859..8c54f27 100644 --- a/patches/moanlauncher/smali/rip/crit/ms/launcher/XiaomiProfile.smali +++ b/patches/moanlauncher/smali/rip/crit/ms/launcher/XiaomiProfile.smali @@ -34,3 +34,66 @@ invoke-interface {v0}, Landroid/content/SharedPreferences$Editor;->apply()V return-void .end method + +.method public static resolveNotesIntent(Landroid/content/pm/PackageManager;)Landroid/content/Intent; + .locals 2 + + # Stock Notes first so the original package keeps working, then the + # current patch build, then older renamed builds. Null means "no Notes + # installed", never crash. + const-string v0, "com.moan.notes" + + invoke-virtual {p0, v0}, Landroid/content/pm/PackageManager;->getLaunchIntentForPackage(Ljava/lang/String;)Landroid/content/Intent; + + move-result-object v0 + + if-nez v0, :done + + const-string v1, "rip.crit.ms.com.moan.notes" + + invoke-virtual {p0, v1}, Landroid/content/pm/PackageManager;->getLaunchIntentForPackage(Ljava/lang/String;)Landroid/content/Intent; + + move-result-object v0 + + if-nez v0, :done + + const-string v1, "rip.crit.ms.notes.com.moan.notes" + + invoke-virtual {p0, v1}, Landroid/content/pm/PackageManager;->getLaunchIntentForPackage(Ljava/lang/String;)Landroid/content/Intent; + + move-result-object v0 + + :done + return-object v0 +.end method + +.method public static safeStart(Landroid/content/Context;Landroid/content/Intent;)V + .locals 2 + + # Every external launch in the launcher funnels through here: null + # intents and unresolvable targets become a toast instead of a crash. + if-eqz p1, :toast + + :try_start + invoke-virtual {p0, p1}, Landroid/content/Context;->startActivity(Landroid/content/Intent;)V + :try_end + .catchall {:try_start .. :try_end} :caught + + return-void + + :caught + move-exception v0 + + :toast + const-string v0, "Cannot open this item" + + const/4 v1, 0x0 + + invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast; + + move-result-object v0 + + invoke-virtual {v0}, Landroid/widget/Toast;->show()V + + return-void +.end method diff --git a/patches/moannotes/assets/background/template_college_ruled.png b/patches/moannotes/assets/background/template_college_ruled.png index 58e895ad87f7993b84b5664fe8c6a9cca8b66029..2c52adba905a3fe9b863b4675fd62b8680179438 100644 GIT binary patch literal 12242 zcmeAS@N?(olHy`uVBq!ia0y~yV69#w&1&G@kY z^ZVD#Kt{v<$uEEugNh3~kW^5#0&zGmG6G4D4n831;8X$<5?TaOJwZSgNH(;LY8ef& z(F8M^U&tsf3d_EK<$Y2k2`ol_>wrs>BQs}SHy8M2#yw9!&$w9p+9baTp+{%Vyq4yum1d7;19txG zyhl4~qy5woQ5>e1$KQ82hujpJGqbXmd13zJ2d9CR!kzS&Kyq~CVzfLM_T|B&H}76c zpZJN?6zViKUZ1bi-g=x>e^0w^jZAR`%j}BCi zj?)kG;;?h`ZeSJg9JwiU?#!NjjTg2Ten9R?kIvkTPFxN1;&60Q0od>vot7Q#4l<~W zPAULX!RR`L(ej{SbW#CS9*!>D87&Vcj7};56T#?8w$bu{L1=VR0hkI#mv)Yp2M(i? n3ZU|EbglDfc|g_T@JPMT%buAVPPTv+ZZUYe`njxgN@xNAZF(pW literal 11038 zcmeAS@N?(olHy`uVBq!ia0y~yV69RbK*nfS3`<-_^PclU;|%HOfN z6nXbOKLf*u{_hsHKqvgO=ZI%u5Ngq9VBkC?&d|W}kek7w=>sc+g5wV+h6xHaj0_$E z`xqEhhOx4T@^btB|9pS>rTu@~y-W-ZmH$6{`1}3;ob&(x&3`G%z;Nbwdpj_E{>YEO zupuuMtkJ)JS$JBBwauIS_S4V5@G&qfTibX1C8zhVpS%9q+V9)r_nv_v;a1gWGuuSZ z{qL{;y?p%GQE>)^{L=mY{t=g6me}QQ-+dRXuQHE$B&EgC@E!^U&S?6kZ*?&m-lO3? zoLeTN>3cMNkEZX@_8_%-VWZ{AXn8VPo{W|!qvgra9MBpqzemgO(eit={2nd8N6YWg z@o&PDQ=|3eXni?aUyjz7qxI!zeK}fRj@FlhZ5_*K`((6zGTJ^FZJ&&`Pl#!sfR^8_ zug~57|Lc12^1JA}?=K4j*NXwy<#hu?aD=bJ`w_phvGUHWd)ltR<(mwiu6{1-oD!M< Dyw-Sm diff --git a/patches/moannotes/res/drawable-nodpi/template_college_ruled.png b/patches/moannotes/res/drawable-nodpi/template_college_ruled.png index 58e895ad87f7993b84b5664fe8c6a9cca8b66029..2c52adba905a3fe9b863b4675fd62b8680179438 100644 GIT binary patch literal 12242 zcmeAS@N?(olHy`uVBq!ia0y~yV69#w&1&G@kY z^ZVD#Kt{v<$uEEugNh3~kW^5#0&zGmG6G4D4n831;8X$<5?TaOJwZSgNH(;LY8ef& z(F8M^U&tsf3d_EK<$Y2k2`ol_>wrs>BQs}SHy8M2#yw9!&$w9p+9baTp+{%Vyq4yum1d7;19txG zyhl4~qy5woQ5>e1$KQ82hujpJGqbXmd13zJ2d9CR!kzS&Kyq~CVzfLM_T|B&H}76c zpZJN?6zViKUZ1bi-g=x>e^0w^jZAR`%j}BCi zj?)kG;;?h`ZeSJg9JwiU?#!NjjTg2Ten9R?kIvkTPFxN1;&60Q0od>vot7Q#4l<~W zPAULX!RR`L(ej{SbW#CS9*!>D87&Vcj7};56T#?8w$bu{L1=VR0hkI#mv)Yp2M(i? n3ZU|EbglDfc|g_T@JPMT%buAVPPTv+ZZUYe`njxgN@xNAZF(pW literal 11038 zcmeAS@N?(olHy`uVBq!ia0y~yV69RbK*nfS3`<-_^PclU;|%HOfN z6nXbOKLf*u{_hsHKqvgO=ZI%u5Ngq9VBkC?&d|W}kek7w=>sc+g5wV+h6xHaj0_$E z`xqEhhOx4T@^btB|9pS>rTu@~y-W-ZmH$6{`1}3;ob&(x&3`G%z;Nbwdpj_E{>YEO zupuuMtkJ)JS$JBBwauIS_S4V5@G&qfTibX1C8zhVpS%9q+V9)r_nv_v;a1gWGuuSZ z{qL{;y?p%GQE>)^{L=mY{t=g6me}QQ-+dRXuQHE$B&EgC@E!^U&S?6kZ*?&m-lO3? zoLeTN>3cMNkEZX@_8_%-VWZ{AXn8VPo{W|!qvgra9MBpqzemgO(eit={2nd8N6YWg z@o&PDQ=|3eXni?aUyjz7qxI!zeK}fRj@FlhZ5_*K`((6zGTJ^FZJ&&`Pl#!sfR^8_ zug~57|Lc12^1JA}?=K4j*NXwy<#hu?aD=bJ`w_phvGUHWd)ltR<(mwiu6{1-oD!M< Dyw-Sm diff --git a/patches/moannotes/res/drawable/icp_check.xml b/patches/moannotes/res/drawable/icp_check.xml new file mode 100644 index 0000000..879b034 --- /dev/null +++ b/patches/moannotes/res/drawable/icp_check.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/patches/moannotes/res/drawable/icp_close.xml b/patches/moannotes/res/drawable/icp_close.xml new file mode 100644 index 0000000..64ebb5f --- /dev/null +++ b/patches/moannotes/res/drawable/icp_close.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/patches/moannotes/res/drawable/icp_export.xml b/patches/moannotes/res/drawable/icp_export.xml deleted file mode 100644 index a1e1a9b..0000000 --- a/patches/moannotes/res/drawable/icp_export.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/patches/moannotes/res/drawable/icp_eye_closed.xml b/patches/moannotes/res/drawable/icp_eye_closed.xml new file mode 100644 index 0000000..73203a9 --- /dev/null +++ b/patches/moannotes/res/drawable/icp_eye_closed.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/patches/moannotes/res/drawable/icp_eye_open.xml b/patches/moannotes/res/drawable/icp_eye_open.xml new file mode 100644 index 0000000..1417f32 --- /dev/null +++ b/patches/moannotes/res/drawable/icp_eye_open.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/patches/moannotes/res/drawable/icp_ink.xml b/patches/moannotes/res/drawable/icp_ink.xml new file mode 100644 index 0000000..1ffeaec --- /dev/null +++ b/patches/moannotes/res/drawable/icp_ink.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/patches/moannotes/res/drawable/icp_kra_export.xml b/patches/moannotes/res/drawable/icp_kra_export.xml index 01b1b7f..cdc92d5 100644 --- a/patches/moannotes/res/drawable/icp_kra_export.xml +++ b/patches/moannotes/res/drawable/icp_kra_export.xml @@ -1 +1,32 @@ - + + + + + + diff --git a/patches/moannotes/res/drawable/icp_layers.xml b/patches/moannotes/res/drawable/icp_layers.xml index 14e18b8..dada71f 100644 --- a/patches/moannotes/res/drawable/icp_layers.xml +++ b/patches/moannotes/res/drawable/icp_layers.xml @@ -1 +1,39 @@ - + + + + + + + diff --git a/patches/moannotes/res/drawable/icp_palette.xml b/patches/moannotes/res/drawable/icp_palette.xml deleted file mode 100644 index a536aac..0000000 --- a/patches/moannotes/res/drawable/icp_palette.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/patches/moannotes/res/drawable/icp_pen.xml b/patches/moannotes/res/drawable/icp_pen.xml deleted file mode 100644 index e3465a6..0000000 --- a/patches/moannotes/res/drawable/icp_pen.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/patches/moannotes/res/drawable/icp_snap_line.xml b/patches/moannotes/res/drawable/icp_snap_line.xml index d25fc81..d923c90 100644 --- a/patches/moannotes/res/drawable/icp_snap_line.xml +++ b/patches/moannotes/res/drawable/icp_snap_line.xml @@ -1,7 +1,32 @@ + - + android:viewportWidth="48" + android:viewportHeight="48"> + + + diff --git a/patches/moannotes/res/drawable/icp_tools.xml b/patches/moannotes/res/drawable/icp_tools.xml new file mode 100644 index 0000000..1b8e3e2 --- /dev/null +++ b/patches/moannotes/res/drawable/icp_tools.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/patches/moannotes/res/values/moannotes_patch_ids.xml b/patches/moannotes/res/values/moannotes_patch_ids.xml index ccd18e5..385523b 100644 --- a/patches/moannotes/res/values/moannotes_patch_ids.xml +++ b/patches/moannotes/res/values/moannotes_patch_ids.xml @@ -1,5 +1,3 @@ - - - + diff --git a/patches/moannotes/res/values/moannotes_strings.xml b/patches/moannotes/res/values/moannotes_strings.xml new file mode 100644 index 0000000..efa2223 --- /dev/null +++ b/patches/moannotes/res/values/moannotes_strings.xml @@ -0,0 +1,4 @@ + + + College ruled + diff --git a/patches/moannotes/smali/rip/crit/ms/moannotes/BackgroundCatalogPatch.smali b/patches/moannotes/smali/rip/crit/ms/moannotes/BackgroundCatalogPatch.smali index af6ec2e..c9f9d7a 100644 --- a/patches/moannotes/smali/rip/crit/ms/moannotes/BackgroundCatalogPatch.smali +++ b/patches/moannotes/smali/rip/crit/ms/moannotes/BackgroundCatalogPatch.smali @@ -2,31 +2,105 @@ .super Ljava/lang/Object; .method public static appendCollegeRuled(Ljava/util/ArrayList;)V - .locals 4 + .locals 6 + + if-eqz p0, :return + + # Guard against double registration: the catalog builder can run more + # than once per process, and duplicates used to pile up rows. + invoke-virtual {p0}, Ljava/util/ArrayList;->size()I + + move-result v0 + + const/4 v1, 0x0 + + :loop + if-ge v1, v0, :add + + invoke-virtual {p0, v1}, Ljava/util/ArrayList;->get(I)Ljava/lang/Object; + + move-result-object v2 + + check-cast v2, Lh/f/b/m5/l; + + iget-object v2, v2, Lh/f/b/m5/l;->d:Ljava/lang/String; + + const-string v3, "College ruled" + + invoke-virtual {v3, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z + + move-result v4 + + if-eqz v4, :next + + return-void + + :next + add-int/lit8 v1, v1, 0x1 + + goto :loop + + :add + # Resolve REAL resource ids. Two other adapters (background apply list + # and page-thumb picker) call getString(e) / setImageResource(c) + # unconditionally, so a 0 in either field crashes with + # Resources$NotFoundException the moment the row binds. Skip the entry + # if either id is missing instead of crashing. + sget-object v0, Lcom/moan/notes/NoteApp;->e:Landroid/content/Context; + + if-eqz v0, :return + + invoke-virtual {v0}, Landroid/content/Context;->getResources()Landroid/content/res/Resources; + + move-result-object v1 + + invoke-virtual {v0}, Landroid/content/Context;->getPackageName()Ljava/lang/String; + + move-result-object v0 + + const-string v2, "template_college_ruled" + + const-string v3, "drawable" + + invoke-virtual {v1, v2, v3, v0}, Landroid/content/res/Resources;->getIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I + + move-result v2 + + if-eqz v2, :return + + const-string v3, "college_ruled" + + const-string v4, "string" + + invoke-virtual {v1, v3, v4, v0}, Landroid/content/res/Resources;->getIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I + + move-result v1 + + if-eqz v1, :return new-instance v0, Lh/f/b/m5/l; invoke-direct {v0}, Lh/f/b/m5/l;->()V - const-string v1, "background" + const-string v3, "background" - invoke-static {v1}, Lh/a/a/a/a;->m(Ljava/lang/String;)Ljava/lang/StringBuilder; + invoke-static {v3}, Lh/a/a/a/a;->m(Ljava/lang/String;)Ljava/lang/StringBuilder; - move-result-object v1 + move-result-object v3 - sget-object v2, Ljava/io/File;->separator:Ljava/lang/String; + sget-object v4, Ljava/io/File;->separator:Ljava/lang/String; - const-string v3, "template_college_ruled.png" + const-string v5, "template_college_ruled.png" - invoke-static {v1, v2, v3}, Lh/a/a/a/a;->j(Ljava/lang/StringBuilder;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + invoke-static {v3, v4, v5}, Lh/a/a/a/a;->j(Ljava/lang/StringBuilder;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - move-result-object v1 + move-result-object v3 - iput-object v1, v0, Lh/f/b/m5/l;->b:Ljava/lang/String; + iput-object v3, v0, Lh/f/b/m5/l;->b:Ljava/lang/String; - const/4 v1, 0x0 + iput v2, v0, Lh/f/b/m5/l;->c:I - iput v1, v0, Lh/f/b/m5/l;->c:I + iput v1, v0, Lh/f/b/m5/l;->e:I const-string v1, "College ruled" @@ -34,11 +108,12 @@ invoke-virtual {p0, v0}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z + :return return-void .end method .method public static previewResource(Landroid/content/Context;Ljava/lang/String;I)I - .locals 4 + .locals 5 const-string v0, "College ruled" @@ -48,6 +123,7 @@ if-eqz v0, :cond_0 + :try_start invoke-virtual {p0}, Landroid/content/Context;->getResources()Landroid/content/res/Resources; move-result-object v0 @@ -62,8 +138,20 @@ invoke-virtual {v0, v1, v2, v3}, Landroid/content/res/Resources;->getIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I - move-result p2 + move-result v0 + :try_end + .catchall {:try_start .. :try_end} :fallback + # Never hand a 0 back: setImageResource(0) throws and used to crash the + # background grid the moment College ruled scrolled into view. Fall back + # to the stock preview (or id) the caller already had. + if-eqz v0, :fallback + + move p2, v0 + + goto :cond_0 + + :fallback :cond_0 return p2 .end method diff --git a/patches/moannotes/smali/rip/crit/ms/moannotes/FeatureRegistry.smali b/patches/moannotes/smali/rip/crit/ms/moannotes/FeatureRegistry.smali index 43629f9..8cda926 100644 --- a/patches/moannotes/smali/rip/crit/ms/moannotes/FeatureRegistry.smali +++ b/patches/moannotes/smali/rip/crit/ms/moannotes/FeatureRegistry.smali @@ -18,7 +18,7 @@ const-string v1, "feature_manifest_version" - const/4 v2, 0x1 + const/4 v2, 0x2 invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences$Editor;->putInt(Ljava/lang/String;I)Landroid/content/SharedPreferences$Editor; @@ -29,182 +29,13 @@ return-void .end method -.method private static dp(Landroid/content/Context;I)I - .locals 2 - - invoke-virtual {p0}, Landroid/content/Context;->getResources()Landroid/content/res/Resources; - - move-result-object p0 - - invoke-virtual {p0}, Landroid/content/res/Resources;->getDisplayMetrics()Landroid/util/DisplayMetrics; - - move-result-object p0 - - iget p0, p0, Landroid/util/DisplayMetrics;->density:F - - int-to-float p1, p1 - - mul-float/2addr p0, p1 - - invoke-static {p0}, Ljava/lang/Math;->round(F)I - - move-result p0 - - return p0 -.end method - .method public static installNote(Landroid/app/Activity;)V - .locals 12 - - invoke-virtual {p0}, Landroid/app/Activity;->getResources()Landroid/content/res/Resources; - - move-result-object v0 - - const-string v1, "moannotes_snap_line_button" - - const-string v2, "id" - - invoke-virtual {p0}, Landroid/app/Activity;->getPackageName()Ljava/lang/String; - - move-result-object v3 - - invoke-virtual {v0, v1, v2, v3}, Landroid/content/res/Resources;->getIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I - - move-result v1 - - invoke-virtual {p0, v1}, Landroid/app/Activity;->findViewById(I)Landroid/view/View; - - move-result-object v1 - - if-eqz v1, :cond_0 - - return-void - - :cond_0 - const-string v1, "note_handwrite_view" - - const-string v2, "id" - - invoke-virtual {p0}, Landroid/app/Activity;->getPackageName()Ljava/lang/String; - - move-result-object v3 - - invoke-virtual {v0, v1, v2, v3}, Landroid/content/res/Resources;->getIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I - - move-result v1 - - invoke-virtual {p0, v1}, Landroid/app/Activity;->findViewById(I)Landroid/view/View; - - move-result-object v1 - - if-eqz v1, :cond_2 - - const-string v2, "moannotes_patch" - - const/4 v3, 0x0 - - invoke-virtual {p0, v2, v3}, Landroid/app/Activity;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences; - - move-result-object v2 - - const-string v4, "snap_to_line_enabled" - - invoke-interface {v2, v4, v3}, Landroid/content/SharedPreferences;->getBoolean(Ljava/lang/String;Z)Z - - move-result v4 - - new-instance v5, Lrip/crit/ms/moannotes/SnapToLineTouchListener; - - invoke-direct {v5, p0, v4}, Lrip/crit/ms/moannotes/SnapToLineTouchListener;->(Landroid/content/Context;Z)V - - invoke-virtual {v1, v5}, Landroid/view/View;->setOnTouchListener(Landroid/view/View$OnTouchListener;)V - - const-string v1, "edit_button_bar" - - const-string v6, "id" - - invoke-virtual {p0}, Landroid/app/Activity;->getPackageName()Ljava/lang/String; - - move-result-object v7 - - invoke-virtual {v0, v1, v6, v7}, Landroid/content/res/Resources;->getIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I - - move-result v1 - - invoke-virtual {p0, v1}, Landroid/app/Activity;->findViewById(I)Landroid/view/View; - - move-result-object v1 - - instance-of v6, v1, Landroid/widget/LinearLayout; - - if-eqz v6, :cond_2 - - check-cast v1, Landroid/widget/LinearLayout; - - new-instance v6, Landroid/widget/ImageButton; - - invoke-direct {v6, p0}, Landroid/widget/ImageButton;->(Landroid/content/Context;)V - - invoke-virtual {v6, v3}, Landroid/widget/ImageButton;->setBackgroundColor(I)V - - const/16 v7, 0x30 - - invoke-static {p0, v7}, Lrip/crit/ms/moannotes/FeatureRegistry;->dp(Landroid/content/Context;I)I - - move-result v7 - - new-instance v8, Landroid/widget/LinearLayout$LayoutParams; - - const/4 v9, -0x1 - - invoke-direct {v8, v7, v9}, Landroid/widget/LinearLayout$LayoutParams;->(II)V - - invoke-virtual {v6, v8}, Landroid/widget/ImageButton;->setLayoutParams(Landroid/view/ViewGroup$LayoutParams;)V - - const-string v7, "icp_snap_line" - - const-string v8, "drawable" - - invoke-virtual {p0}, Landroid/app/Activity;->getPackageName()Ljava/lang/String; - - move-result-object v9 - - invoke-virtual {v0, v7, v8, v9}, Landroid/content/res/Resources;->getIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I - - move-result v0 - - invoke-virtual {v6, v0}, Landroid/widget/ImageButton;->setImageResource(I)V - - const-string v0, "Handwriting snap to line" - - invoke-virtual {v6, v0}, Landroid/widget/ImageButton;->setContentDescription(Ljava/lang/CharSequence;)V - - invoke-virtual {v6, v1}, Landroid/widget/ImageButton;->setId(I)V - - if-eqz v4, :cond_1 - - const/high16 v0, 0x3f800000 # 1.0f - - goto :goto_0 - - :cond_1 - const v0, 0x3ee66666 # 0.45f - - :goto_0 - invoke-virtual {v6, v0}, Landroid/widget/ImageButton;->setAlpha(F)V - - new-instance v0, Lrip/crit/ms/moannotes/SnapToLineToggle; - - invoke-direct {v0, v5, v2, v6}, Lrip/crit/ms/moannotes/SnapToLineToggle;->(Lrip/crit/ms/moannotes/SnapToLineTouchListener;Landroid/content/SharedPreferences;Landroid/widget/ImageButton;)V - - invoke-virtual {v6, v0}, Landroid/widget/ImageButton;->setOnClickListener(Landroid/view/View$OnClickListener;)V - - const/4 v0, 0x7 - - invoke-virtual {v1, v6, v0}, Landroid/widget/LinearLayout;->addView(Landroid/view/View;I)V + .locals 1 + # Single integration point: the Java bridge appends the layers / ink / + # snap buttons (guarded against duplicates) and owns the snap listener. + # Nothing here touches stock toolbar icons. invoke-static {p0}, Lrip/crit/ms/moannotes/nativebridge/NativeFeatureHooks;->install(Landroid/app/Activity;)V - :cond_2 return-void .end method diff --git a/patches/moannotes/smali/rip/crit/ms/moannotes/SnapToLineTouchListener.smali b/patches/moannotes/smali/rip/crit/ms/moannotes/SnapToLineTouchListener.smali index 4f98a8c..8bb7346 100644 --- a/patches/moannotes/smali/rip/crit/ms/moannotes/SnapToLineTouchListener.smali +++ b/patches/moannotes/smali/rip/crit/ms/moannotes/SnapToLineTouchListener.smali @@ -26,7 +26,9 @@ iget p1, p1, Landroid/util/DisplayMetrics;->density:F - const/high16 v0, 0x42600000 # 56.0f + # 64dp matches the regenerated college-ruled sheet (120px lines at the + # native 1404px page width, ~1.875x e-ink density bucket). + const/high16 v0, 0x42800000 # 64.0f mul-float/2addr p1, v0 @@ -56,97 +58,161 @@ .end method .method public onTouch(Landroid/view/View;Landroid/view/MotionEvent;)Z - .locals 6 + .locals 8 - iget-boolean p1, p0, Lrip/crit/ms/moannotes/SnapToLineTouchListener;->enabled:Z + iget-boolean v0, p0, Lrip/crit/ms/moannotes/SnapToLineTouchListener;->enabled:Z - if-eqz p1, :cond_5 + if-nez v0, :cond_enabled - const/4 p1, 0x0 + goto :pass - invoke-virtual {p2, p1}, Landroid/view/MotionEvent;->getToolType(I)I + :cond_enabled + # Scan every pointer for a stylus (tool type 2). The old code only looked + # at index 0, so a stylus arriving as a second pointer never snapped. + invoke-virtual {p2}, Landroid/view/MotionEvent;->getPointerCount()I move-result v0 - const/4 v1, 0x2 + const/4 v1, 0x0 - if-ne v0, v1, :cond_5 + const/4 v2, -0x1 - invoke-virtual {p2}, Landroid/view/MotionEvent;->getActionMasked()I + :scan + if-ge v1, v0, :scanned - move-result v0 - - if-ne v0, p1, :cond_0 - - invoke-virtual {p2}, Landroid/view/MotionEvent;->getY()F - - move-result v1 - - iget v2, p0, Lrip/crit/ms/moannotes/SnapToLineTouchListener;->spacing:F - - div-float v3, v1, v2 - - invoke-static {v3}, Ljava/lang/Math;->round(F)I + invoke-virtual {p2, v1}, Landroid/view/MotionEvent;->getToolType(I)I move-result v3 - int-to-float v3, v3 + const/4 v4, 0x2 - mul-float/2addr v3, v2 + if-ne v3, v4, :scan_next - sub-float/2addr v3, v1 + move v2, v1 - iput v3, p0, Lrip/crit/ms/moannotes/SnapToLineTouchListener;->offset:F + goto :scanned - :cond_0 - const/4 v1, 0x1 + :scan_next + add-int/lit8 v1, v1, 0x1 - if-eq v0, p1, :cond_1 + goto :scan - if-eq v0, v1, :cond_1 + :scanned + const/4 v0, -0x1 - const/4 p1, 0x2 + if-eq v2, v0, :pass - if-eq v0, p1, :cond_1 + invoke-virtual {p2}, Landroid/view/MotionEvent;->getActionMasked()I - const/4 p1, 0x3 + move-result v3 - if-ne v0, p1, :cond_5 + invoke-virtual {p2, v2}, Landroid/view/MotionEvent;->getX(I)F - :cond_1 - invoke-virtual {p2}, Landroid/view/MotionEvent;->getX()F + move-result v4 - move-result p1 + invoke-virtual {p2, v2}, Landroid/view/MotionEvent;->getY(I)F - invoke-virtual {p2}, Landroid/view/MotionEvent;->getY()F + move-result v5 - move-result v2 + # (Re)compute the frozen stroke offset whenever a pointer goes down, so a + # stylus joining late still snaps. The frozen offset preserves the + # internal letter shape for the whole stroke. + if-nez v3, :not_down - iget v3, p0, Lrip/crit/ms/moannotes/SnapToLineTouchListener;->offset:F + goto :recompute - add-float/2addr v2, v3 + :not_down + const/4 v0, 0x5 - invoke-virtual {p2, p1, v2}, Landroid/view/MotionEvent;->setLocation(FF)V + if-ne v3, v0, :apply_check - if-ne v0, v1, :cond_3 + :recompute + iget v0, p0, Lrip/crit/ms/moannotes/SnapToLineTouchListener;->spacing:F - :cond_2 - const/4 p1, 0x0 + div-float v1, v5, v0 - iput p1, p0, Lrip/crit/ms/moannotes/SnapToLineTouchListener;->offset:F + invoke-static {v1}, Ljava/lang/Math;->round(F)I - goto :goto_0 + move-result v1 - :cond_3 - const/4 p1, 0x3 + int-to-float v1, v1 - if-ne v0, p1, :cond_5 + mul-float/2addr v1, v0 - goto :cond_2 + sub-float/2addr v1, v5 - :cond_5 - :goto_0 - const/4 p1, 0x0 + iput v1, p0, Lrip/crit/ms/moannotes/SnapToLineTouchListener;->offset:F - return p1 + :apply_check + const/4 v0, 0x2 + + if-ne v3, v0, :check_down + + goto :apply + + :check_down + if-nez v3, :check_up + + goto :apply + + :check_up + const/4 v0, 0x1 + + if-ne v3, v0, :check_cancel + + goto :apply + + :check_cancel + const/4 v0, 0x3 + + if-ne v3, v0, :check_pdown + + goto :apply + + :check_pdown + const/4 v0, 0x5 + + if-ne v3, v0, :check_pup + + goto :apply + + :check_pup + const/4 v0, 0x6 + + if-ne v3, v0, :pass + + :apply + iget v0, p0, Lrip/crit/ms/moannotes/SnapToLineTouchListener;->offset:F + + add-float/2addr v5, v0 + + invoke-virtual {p2, v4, v5}, Landroid/view/MotionEvent;->setLocation(FF)V + + const/4 v0, 0x1 + + if-ne v3, v0, :check_cancel2 + + goto :clear + + :check_cancel2 + const/4 v0, 0x3 + + if-ne v3, v0, :check_pup2 + + goto :clear + + :check_pup2 + const/4 v0, 0x6 + + if-ne v3, v0, :pass + + :clear + const/4 v0, 0x0 + + iput v0, p0, Lrip/crit/ms/moannotes/SnapToLineTouchListener;->offset:F + + :pass + const/4 v0, 0x0 + + return v0 .end method diff --git a/qmlfmt.sh b/qmlfmt.sh deleted file mode 100755 index b455803..0000000 --- a/qmlfmt.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -git ls-files -z --cached --others --exclude-standard | grep -zE '\.(qml|js)$' | xargs -0 qmlformat -i -echo "qml/done" \ No newline at end of file diff --git a/scripts/reinstall-moannotes.ts b/scripts/reinstall-moannotes.ts index 4ecd9da..7a3d7fe 100644 --- a/scripts/reinstall-moannotes.ts +++ b/scripts/reinstall-moannotes.ts @@ -5,6 +5,35 @@ import path from "node:path"; const root = path.resolve(import.meta.dir, ".."); const args = process.argv.slice(2); +const app = value("--app") ?? "MoanNote"; +const profiles: Record< + string, + { + patchScript: string; + legacyPackage: string; + patchedPackage: string; + removeLegacy: boolean; + legacySystemImage: boolean; + } +> = { + MoanNote: { + patchScript: "patch:moannotes", + legacyPackage: "com.moan.notes", + patchedPackage: "rip.crit.ms.com.moan.notes", + removeLegacy: false, + legacySystemImage: true, + }, + MoanLauncher: { + patchScript: "patch:moanlauncher", + legacyPackage: "com.note.launcher", + patchedPackage: "rip.crit.ms.com.note.launcher", + removeLegacy: false, + legacySystemImage: true, + }, +}; +const profile = profiles[app]; +if (!profile) throw new Error(`Unsupported app ${app}`); + function value(flag: string): string | undefined { const index = args.indexOf(flag); return index >= 0 ? args[index + 1] : undefined; @@ -20,21 +49,82 @@ function run(command: string, commandArgs: string[]): void { throw new Error(`${command} ${commandArgs.join(" ")} failed`); } +function uninstallIfPresent(serial: string, packageName: string): void { + const result = spawnSync("adb", ["-s", serial, "uninstall", packageName], { + cwd: root, + encoding: "utf8", + }); + if (result.error) throw result.error; + if (result.status === 0) { + if (result.stdout) process.stdout.write(result.stdout); + return; + } + const output = `${result.stdout ?? ""}${result.stderr ?? ""}`; + if (/not installed/i.test(output)) { + console.log(`[info] ${packageName} was not installed`); + return; + } + // OEM apps in /system cannot be uninstalled from the system partition. For a + // clean, per-user replacement, Package Manager can remove that system app + // for user 0. This also clears its user data. + if (/DELETE_FAILED_INTERNAL_ERROR|unknown system package/i.test(output)) { + const perUserResult = spawnSync( + "adb", + ["-s", serial, "shell", "pm", "uninstall", "--user", "0", packageName], + { cwd: root, encoding: "utf8" }, + ); + if (perUserResult.error) throw perUserResult.error; + if (perUserResult.status === 0) { + if (perUserResult.stdout) process.stdout.write(perUserResult.stdout); + console.log(`[info] Removed system app ${packageName} for user 0`); + return; + } + const perUserOutput = `${perUserResult.stdout ?? ""}${perUserResult.stderr ?? ""}`; + if (/not installed/i.test(perUserOutput)) { + console.log(`[info] ${packageName} was not installed for user 0`); + return; + } + if (perUserOutput) process.stderr.write(perUserOutput); + } + if (output) process.stderr.write(output); + throw new Error(`Unable to remove ${packageName}`); +} + +function requireConnectedDevice(serial: string): void { + const result = spawnSync("adb", ["-s", serial, "get-state"], { + cwd: root, + encoding: "utf8", + }); + if (result.error) throw result.error; + if (result.status !== 0 || result.stdout.trim() !== "device") + throw new Error(`ADB device ${serial} is not connected and authorized`); +} + const serial = value("--serial"); if (!serial) throw new Error("Specify the Xiaomi target with --serial "); if (!args.includes("--confirm-reinstall")) throw new Error("Refusing to replace an app without --confirm-reinstall"); -run("bun", ["run", "patch:moannotes"]); +requireConnectedDevice(serial); +run("bun", ["run", profile.patchScript]); const apk = - value("--apk") ?? path.join(root, "outputs", "MoanNote_patched-signed.apk"); + value("--apk") ?? path.join(root, "outputs", `${app}_patched-signed.apk`); if (!existsSync(apk)) { throw new Error( - `Signed APK is missing at ${path.join("outputs", "MoanNote_patched-signed.apk")}`, + `Signed APK is missing at ${path.join("outputs", `${app}_patched-signed.apk`)}`, ); } -run("adb", ["-s", serial, "install", "--no-incremental", "-r", apk]); +// Android can reject an uninstall of an already-patched package with +// DELETE_FAILED_INTERNAL_ERROR, even though `adb install -r` updates the same +// package normally. Keep the patched app in place and only remove the original +// package when its unchanged provider authority would conflict with the patch. +if (profile.removeLegacy) { + uninstallIfPresent(serial, profile.legacyPackage); +} else { + console.log(`[info] Keeping protected OEM package ${profile.legacyPackage}`); +} +run("adb", ["-s", serial, "install", "-r", "--no-incremental", apk]); console.log( - `[ok] Reinstalled ${path.basename(apk)} without uninstalling MoanNotes data.`, + `[ok] Installed ${path.basename(apk)}${profile.legacySystemImage ? ` alongside protected OEM system image ${profile.legacyPackage}` : profile.removeLegacy ? ` after replacing ${profile.legacyPackage}` : " as a side-by-side update"}.`, ); diff --git a/scripts/sign-moannotes.ts b/scripts/sign-moannotes.ts index dd16b56..e5db474 100644 --- a/scripts/sign-moannotes.ts +++ b/scripts/sign-moannotes.ts @@ -5,7 +5,8 @@ import { spawnSync } from "node:child_process"; const root = path.resolve(import.meta.dir, ".."); const app = process.argv[2] ?? "MoanNote"; -if (!/^[A-Za-z0-9_-]+$/.test(app)) throw new Error("App name must be a simple file name"); +if (!/^[A-Za-z0-9_-]+$/.test(app)) + throw new Error("App name must be a simple file name"); const unsignedApk = path.join(root, "outputs", `${app}_patched.apk`); const signedApk = path.join(root, "outputs", `${app}_patched-signed.apk`); const signingDir = path.join(root, "build", "signing"); @@ -17,7 +18,9 @@ function run(command: string, args: string[]): void { const result = spawnSync(command, args, { cwd: root, stdio: "inherit" }); if (result.error) throw result.error; if (result.status !== 0) - throw new Error(`${path.basename(command)} failed with status ${result.status}`); + throw new Error( + `${path.basename(command)} failed with status ${result.status}`, + ); } function readCredentials(): Record { @@ -32,7 +35,10 @@ function readCredentials(): Record { } function androidTool(name: "apksigner" | "zipalign"): string { - const sdk = process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? "/var/home/hellings/android-sdk"; + const sdk = + process.env.ANDROID_HOME ?? + process.env.ANDROID_SDK_ROOT ?? + "/var/home/hellings/android-sdk"; for (const version of ["36.0.0", "35.0.0", "34.0.0"]) { const tool = path.join(sdk, "build-tools", version, name); if (fs.existsSync(tool)) return tool; @@ -44,27 +50,72 @@ function ensureDevKey(): Record { fs.mkdirSync(signingDir, { recursive: true, mode: 0o700 }); if (!fs.existsSync(keystore)) { const password = crypto.randomBytes(32).toString("base64url"); - const values = { storePassword: password, keyPassword: password, alias: keyName }; + const values = { + storePassword: password, + keyPassword: password, + alias: keyName, + }; run("keytool", [ - "-genkeypair", "-keystore", keystore, "-storetype", "PKCS12", "-storepass", password, - "-alias", values.alias, "-keypass", password, "-keyalg", "RSA", "-keysize", "2048", - "-validity", "3650", "-dname", `CN=${app} Development,O=rip.crit.ms,C=US`, + "-genkeypair", + "-keystore", + keystore, + "-storetype", + "PKCS12", + "-storepass", + password, + "-alias", + values.alias, + "-keypass", + password, + "-keyalg", + "RSA", + "-keysize", + "2048", + "-validity", + "3650", + "-dname", + `CN=${app} Development,O=rip.crit.ms,C=US`, ]); - fs.writeFileSync(credentials, Object.entries(values).map(([key, value]) => `${key}=${value}`).join("\n") + "\n", { mode: 0o600 }); + fs.writeFileSync( + credentials, + Object.entries(values) + .map(([key, value]) => `${key}=${value}`) + .join("\n") + "\n", + { mode: 0o600 }, + ); console.log(`[ok] Created local ${app} development key at ${keystore}`); } - if (!fs.existsSync(credentials)) throw new Error(`Development key credentials are missing at ${credentials}`); + if (!fs.existsSync(credentials)) + throw new Error( + `Development key credentials are missing at ${credentials}`, + ); return readCredentials(); } -if (!fs.existsSync(unsignedApk)) throw new Error(`Patched APK is missing at ${unsignedApk}`); +if (!fs.existsSync(unsignedApk)) + throw new Error(`Patched APK is missing at ${unsignedApk}`); const values = ensureDevKey(); const aligned = path.join(root, "build", `${app}_patched-aligned.apk`); fs.mkdirSync(path.dirname(aligned), { recursive: true }); run(androidTool("zipalign"), ["-f", "-p", "4", unsignedApk, aligned]); run(androidTool("apksigner"), [ - "sign", "--ks", keystore, "--ks-key-alias", values.alias!, "--ks-pass", `pass:${values.storePassword!}`, - "--key-pass", `pass:${values.keyPassword!}`, "--out", signedApk, aligned, + "sign", + "--ks", + keystore, + "--ks-key-alias", + values.alias!, + "--ks-pass", + `pass:${values.storePassword!}`, + "--key-pass", + `pass:${values.keyPassword!}`, + "--out", + signedApk, + aligned, +]); +run(androidTool("apksigner"), [ + "verify", + "--verbose", + "--print-certs", + signedApk, ]); -run(androidTool("apksigner"), ["verify", "--verbose", "--print-certs", signedApk]); console.log(`[ok] Signed ${signedApk}`); diff --git a/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/note/asynctask/BitmapWorkerTask.java b/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/note/asynctask/BitmapWorkerTask.java index 1e5adbe..e4fcc6c 100755 --- a/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/note/asynctask/BitmapWorkerTask.java +++ b/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/note/asynctask/BitmapWorkerTask.java @@ -1,65 +1,64 @@ package ntx.note.asynctask; -import java.lang.ref.WeakReference; - import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.widget.ImageView; +import java.lang.ref.WeakReference; public class BitmapWorkerTask extends AsyncTask { - private final WeakReference imageViewReference; - private int reqWidth = 0; - private int reqHeight = 0; + private final WeakReference imageViewReference; + private int reqWidth = 0; + private int reqHeight = 0; - public BitmapWorkerTask(ImageView imageView, int reqWidth, int reqHeight) { - // Use a WeakReference to ensure the ImageView can be garbage collected - imageViewReference = new WeakReference(imageView); - this.reqWidth = reqWidth; - this.reqHeight = reqHeight; - } + public BitmapWorkerTask(ImageView imageView, int reqWidth, int reqHeight) { + // Use a WeakReference to ensure the ImageView can be garbage collected + imageViewReference = new WeakReference(imageView); + this.reqWidth = reqWidth; + this.reqHeight = reqHeight; + } - @Override - protected Bitmap doInBackground(String... params) { + @Override + protected Bitmap doInBackground(String... params) { - if (!params[0].equals("")) { - return resizeBitmapFromFile(params[0], reqWidth, reqHeight); - } - return null; - } + if (!params[0].equals("")) { + return resizeBitmapFromFile(params[0], reqWidth, reqHeight); + } + return null; + } - @Override - protected void onPostExecute(Bitmap bitmap) { - final ImageView imageView = imageViewReference.get(); - if (imageView != null && bitmap != null) { - imageView.setImageBitmap(bitmap); - } - } + @Override + protected void onPostExecute(Bitmap bitmap) { + final ImageView imageView = imageViewReference.get(); + if (imageView != null && bitmap != null) { + imageView.setImageBitmap(bitmap); + } + } - public Bitmap resizeBitmapFromFile(String path, int reqWidth, int reqHeight) { - // First decode with inJustDecodeBounds=true to check dimensions - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(path, options); + public Bitmap resizeBitmapFromFile(String path, int reqWidth, int reqHeight) { + // First decode with inJustDecodeBounds=true to check dimensions + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(path, options); - // Calculate inSampleSize Raw height and width of image - final int height = options.outHeight; - final int width = options.outWidth; - options.inPreferredConfig = Bitmap.Config.RGB_565; - int inSampleSize = 1; + // Calculate inSampleSize Raw height and width of image + final int height = options.outHeight; + final int width = options.outWidth; + options.inPreferredConfig = Bitmap.Config.RGB_565; + int inSampleSize = 1; - if (height > reqHeight) { - inSampleSize = Math.round((float) height / (float) reqHeight); - } + if (height > reqHeight) { + inSampleSize = Math.round((float) height / (float) reqHeight); + } - int expectedWidth = width / inSampleSize; + int expectedWidth = width / inSampleSize; - if (expectedWidth > reqWidth) { - inSampleSize = Math.round((float) width / (float) reqWidth); - } - options.inSampleSize = inSampleSize; - options.inJustDecodeBounds = false; + if (expectedWidth > reqWidth) { + inSampleSize = Math.round((float) width / (float) reqWidth); + } + options.inSampleSize = inSampleSize; + options.inJustDecodeBounds = false; - return BitmapFactory.decodeFile(path, options); - } + return BitmapFactory.decodeFile(path, options); + } } diff --git a/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/ImagePickerActivity.java b/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/ImagePickerActivity.java index 4d224d3..7d9583b 100755 --- a/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/ImagePickerActivity.java +++ b/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/ImagePickerActivity.java @@ -18,7 +18,6 @@ import android.widget.ImageView; import android.widget.SimpleAdapter; import android.widget.TextView; import android.widget.Toast; - import java.io.File; import java.util.ArrayList; import java.util.Collections; @@ -26,290 +25,298 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; - import ntx.note.asynctask.BitmapWorkerTask; @SuppressLint("DefaultLocale") public class ImagePickerActivity extends Activity implements OnItemClickListener { - private final static String TAG = "ImagePickerActivity"; + private static final String TAG = "ImagePickerActivity"; - public String[] searchImageType = new String[]{".png", ".jpg", ".jpeg", ".bmp"}; //auto list files + public String[] searchImageType = + new String[] {".png", ".jpg", ".jpeg", ".bmp"}; // auto list files - private final static int NumPerPage_Portrait = 9; //this should be fit real status - private final static int NumPerPage_Landscape = 8; //this should be fit real status + private static final int NumPerPage_Portrait = 9; // this should be fit real status + private static final int NumPerPage_Landscape = 8; // this should be fit real status - private final static int GridView_Landscape_height_6Inch = 830; - private final static int GridView_Landscape_height_13Inch = 930; + private static final int GridView_Landscape_height_6Inch = 830; + private static final int GridView_Landscape_height_13Inch = 930; - private int numPerPage = 0; //this should be fit real status + private int numPerPage = 0; // this should be fit real status - protected File sdcardDir, externalCardDir = null; + protected File sdcardDir, externalCardDir = null; - private GridView fileGrid; - private SimpleAdapter simpleAdapter; + private GridView fileGrid; + private SimpleAdapter simpleAdapter; - private List resultPath = new ArrayList(); - private List> items = new ArrayList>(); + private List resultPath = new ArrayList(); + private List> items = new ArrayList>(); - private TextView txtv_pageinfo; - private ImageView imgv_pre_page, imgv_next_page; - private int totalPage, currentPage = 0; - public BitmapWorkerTask[] asyncTask = new BitmapWorkerTask[NumPerPage_Portrait]; + private TextView txtv_pageinfo; + private ImageView imgv_pre_page, imgv_next_page; + private int totalPage, currentPage = 0; + public BitmapWorkerTask[] asyncTask = new BitmapWorkerTask[NumPerPage_Portrait]; - public final String PATH_SDCARD = Environment.getExternalStorageDirectory().getPath(); + public final String PATH_SDCARD = Environment.getExternalStorageDirectory().getPath(); - @Override - public void onCreate(Bundle savedInstanceState) { + @Override + public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.image_picker); - - int display_mode = getResources().getConfiguration().orientation; - if (display_mode == Configuration.ORIENTATION_PORTRAIT) { - numPerPage = NumPerPage_Portrait; - } else { - numPerPage = NumPerPage_Landscape; - } - - sdcardDir = new File(PATH_SDCARD); - searchFiles(sdcardDir); - - Collections.sort(resultPath, new SortIgnoreCase()); - - totalPage = getTotalPage(); - if (currentPage == 0) - currentPage = 1; - - //add into items for showing - updateItems(); - - fileGrid = (GridView) findViewById(R.id.filepicker_file_grid); - - - if (display_mode == Configuration.ORIENTATION_PORTRAIT) { - - } else { - - ViewGroup.LayoutParams layoutParams = fileGrid.getLayoutParams(); - if (NtxView.isEinkHandWritingHardwareType() && NtxView.isEink6InchHardwareType()) { - layoutParams.height = GridView_Landscape_height_6Inch; - } else { - layoutParams.height = GridView_Landscape_height_13Inch; - } - - fileGrid.setLayoutParams(layoutParams); - } - - simpleAdapter = new SimpleAdapter(this, - items, R.layout.image_picker_item, new String[]{"image", "text"}, - new int[]{R.id.picker_image, R.id.picker_text}) { - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - - int mWidth = 205 * 2; - int mHeight = 150 * 2; - if (convertView == null) { - convertView = super.getView(position, convertView, parent); - } - - final TextView nameView = (TextView) convertView.findViewById(R.id.picker_text); - - final int indexNameS1 = items.get(position).get("image").toString().lastIndexOf('/') + 1; - final String filenameS1 = ((indexNameS1 > 0) ? items.get(position).get("image").toString().substring(indexNameS1).toLowerCase().intern() : ""); - nameView.setText(filenameS1); - - final ImageView coverView = (ImageView) convertView.findViewById(R.id.picker_image); - if (position > 0) { - coverView.setImageBitmap(null); - } - - if (asyncTask[position] != null && asyncTask[position].getStatus().equals(Status.RUNNING)) { - asyncTask[position].cancel(true); - } - - asyncTask[position] = new BitmapWorkerTask(coverView, mWidth, mHeight); - asyncTask[position].execute(items.get(position).get("image").toString()); - return convertView; - } - }; - fileGrid.setAdapter(simpleAdapter); - fileGrid.setOnItemClickListener(this); - - if (resultPath.isEmpty()) { - Toast.makeText(ImagePickerActivity.this, R.string.image_pick_no_image, Toast.LENGTH_LONG).show(); - } - - setTitle(R.string.image_pick_title); - - txtv_pageinfo = (TextView) findViewById(R.id.page_info); - imgv_pre_page = (ImageView) findViewById(R.id.imgb_pre_page); - imgv_next_page = (ImageView) findViewById(R.id.imgb_next_page); - - imgv_pre_page.setOnClickListener(instantKeyListener); - imgv_next_page.setOnClickListener(instantKeyListener); + super.onCreate(savedInstanceState); + setContentView(R.layout.image_picker); + int display_mode = getResources().getConfiguration().orientation; + if (display_mode == Configuration.ORIENTATION_PORTRAIT) { + numPerPage = NumPerPage_Portrait; + } else { + numPerPage = NumPerPage_Landscape; } - private OnClickListener instantKeyListener = new OnClickListener() { + sdcardDir = new File(PATH_SDCARD); + searchFiles(sdcardDir); + + Collections.sort(resultPath, new SortIgnoreCase()); + + totalPage = getTotalPage(); + if (currentPage == 0) currentPage = 1; + + // add into items for showing + updateItems(); + + fileGrid = (GridView) findViewById(R.id.filepicker_file_grid); + + if (display_mode == Configuration.ORIENTATION_PORTRAIT) { + + } else { + + ViewGroup.LayoutParams layoutParams = fileGrid.getLayoutParams(); + if (NtxView.isEinkHandWritingHardwareType() && NtxView.isEink6InchHardwareType()) { + layoutParams.height = GridView_Landscape_height_6Inch; + } else { + layoutParams.height = GridView_Landscape_height_13Inch; + } + + fileGrid.setLayoutParams(layoutParams); + } + + simpleAdapter = + new SimpleAdapter( + this, + items, + R.layout.image_picker_item, + new String[] {"image", "text"}, + new int[] {R.id.picker_image, R.id.picker_text}) { + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + int mWidth = 205 * 2; + int mHeight = 150 * 2; + if (convertView == null) { + convertView = super.getView(position, convertView, parent); + } + + final TextView nameView = (TextView) convertView.findViewById(R.id.picker_text); + + final int indexNameS1 = + items.get(position).get("image").toString().lastIndexOf('/') + 1; + final String filenameS1 = + ((indexNameS1 > 0) + ? items + .get(position) + .get("image") + .toString() + .substring(indexNameS1) + .toLowerCase() + .intern() + : ""); + nameView.setText(filenameS1); + + final ImageView coverView = (ImageView) convertView.findViewById(R.id.picker_image); + if (position > 0) { + coverView.setImageBitmap(null); + } + + if (asyncTask[position] != null + && asyncTask[position].getStatus().equals(Status.RUNNING)) { + asyncTask[position].cancel(true); + } + + asyncTask[position] = new BitmapWorkerTask(coverView, mWidth, mHeight); + asyncTask[position].execute(items.get(position).get("image").toString()); + return convertView; + } + }; + fileGrid.setAdapter(simpleAdapter); + fileGrid.setOnItemClickListener(this); + + if (resultPath.isEmpty()) { + Toast.makeText(ImagePickerActivity.this, R.string.image_pick_no_image, Toast.LENGTH_LONG) + .show(); + } + + setTitle(R.string.image_pick_title); + + txtv_pageinfo = (TextView) findViewById(R.id.page_info); + imgv_pre_page = (ImageView) findViewById(R.id.imgb_pre_page); + imgv_next_page = (ImageView) findViewById(R.id.imgb_next_page); + + imgv_pre_page.setOnClickListener(instantKeyListener); + imgv_next_page.setOnClickListener(instantKeyListener); + } + + private OnClickListener instantKeyListener = + new OnClickListener() { // TODO @Override public void onClick(final View v) { - switch (v.getId()) { - case R.id.imgb_pre_page: - currentPage--; - if (currentPage <= 0) - currentPage = totalPage; + switch (v.getId()) { + case R.id.imgb_pre_page: + currentPage--; + if (currentPage <= 0) currentPage = totalPage; - txtv_pageinfo.setText(currentPage + " of " + totalPage); - updateItems(); - simpleAdapter.notifyDataSetChanged(); - break; - case R.id.imgb_next_page: - currentPage++; - if (currentPage > totalPage) - currentPage = 1; + txtv_pageinfo.setText(currentPage + " of " + totalPage); + updateItems(); + simpleAdapter.notifyDataSetChanged(); + break; + case R.id.imgb_next_page: + currentPage++; + if (currentPage > totalPage) currentPage = 1; - txtv_pageinfo.setText(currentPage + " of " + totalPage); - updateItems(); - simpleAdapter.notifyDataSetChanged(); - break; - default: - finish(); - break; + txtv_pageinfo.setText(currentPage + " of " + totalPage); + updateItems(); + simpleAdapter.notifyDataSetChanged(); + break; + default: + finish(); + break; + } + } + }; + + @Override + public void onItemClick(AdapterView adapter, View view, int pos, long id) { + int realPos = 0; + + realPos = numPerPage * (currentPage - 1) + pos; + String path = resultPath.get(realPos); + final File f = new File(path); + + if (f.isFile()) pickFile(f); + } + + @Override + protected void onPause() { + super.onPause(); + changeEinkControlPermission(false); + } + + private void pickFile(File file) { + Intent intent = getIntent(); + intent.setData(Uri.fromFile(file)); + setResult(RESULT_OK, intent); + finish(); + } + + // scan all the specific type files. ex:*.note + public void searchFiles(File file) { + File[] the_Files = file.listFiles(); + + if (the_Files == null) return; + + // search all dirs and files + for (File tempF : the_Files) { + if (tempF.isDirectory()) { + if (!tempF.isHidden()) searchFiles(tempF); + } else { + try { + boolean isImage = false; + // compare file. if the key is matched, return value > -1. + for (String type : searchImageType) { + + final int index = tempF.getPath().lastIndexOf('.'); + final String myExtension = + ((index > 0) ? tempF.getPath().substring(index).toLowerCase().intern() : ""); + + if (myExtension.equals(type) && tempF.getName().substring(0, 1).equals(".") == false) { + isImage = true; + break; } + } + if (isImage) { + // add matched path into array + resultPath.add(tempF.getPath()); + } + } catch (Exception e) { + Toast.makeText( + ImagePickerActivity.this, "error - searching image files.", Toast.LENGTH_LONG) + .show(); } - }; + } + } + } + @Override + protected void onResume() { + super.onResume(); + changeEinkControlPermission(true); + + // update page info + totalPage = getTotalPage(); + if (currentPage == 0) currentPage = 1; + txtv_pageinfo.setText(currentPage + " of " + totalPage); + + if (totalPage == 1) { // only one page + txtv_pageinfo.setVisibility(View.INVISIBLE); + imgv_pre_page.setVisibility(View.INVISIBLE); + imgv_next_page.setVisibility(View.INVISIBLE); + } else { + txtv_pageinfo.setVisibility(View.VISIBLE); + imgv_pre_page.setVisibility(View.VISIBLE); + imgv_next_page.setVisibility(View.VISIBLE); + } + } + + private void changeEinkControlPermission(boolean isForNtxAppsOnly) { + Intent changePermissionIntent = new Intent("ntx.eink_control.CHANGE_PERMISSON"); + changePermissionIntent.putExtra("isPermissionNtxApp", isForNtxAppsOnly); + sendBroadcast(changePermissionIntent); + } + + // calculate total page + private int getTotalPage() { + int totalpage = resultPath.size() / (numPerPage); + + if (resultPath.size() % (numPerPage) != 0 | resultPath.size() / (numPerPage) == 0) totalpage++; + return totalpage; + } + + // update items for showing + private void updateItems() { + if (items.size() != 0) items.clear(); + + for (int i = numPerPage * (currentPage - 1); i < numPerPage * currentPage; i++) { + if (i >= resultPath.size()) break; + + Map item = new HashMap(); + item.put("image", resultPath.get(i)); + items.add(item); + } + } + + public class SortIgnoreCase implements Comparator { @Override - public void onItemClick(AdapterView adapter, View view, int pos, long id) { - int realPos = 0; + public int compare(Object o1, Object o2) { + String s1 = (String) o1; + String s2 = (String) o2; - realPos = numPerPage * (currentPage - 1) + pos; - String path = resultPath.get(realPos); - final File f = new File(path); + final int indexNameS1 = s1.lastIndexOf('/'); + final String filenameS1 = + ((indexNameS1 > 1) ? s1.substring(indexNameS1).toLowerCase().intern() : ""); - if (f.isFile()) - pickFile(f); + final int indexNameS2 = s2.lastIndexOf('/'); + final String filenameS2 = + ((indexNameS2 > 1) ? s2.substring(indexNameS2).toLowerCase().intern() : ""); + + return filenameS1.toLowerCase().compareTo(filenameS2.toLowerCase()); } - - - @Override - protected void onPause() { - super.onPause(); - changeEinkControlPermission(false); - } - - private void pickFile(File file) { - Intent intent = getIntent(); - intent.setData(Uri.fromFile(file)); - setResult(RESULT_OK, intent); - finish(); - } - - //scan all the specific type files. ex:*.note - public void searchFiles(File file) { - File[] the_Files = file.listFiles(); - - if (the_Files == null) - return; - - //search all dirs and files - for (File tempF : the_Files) { - if (tempF.isDirectory()) { - if (!tempF.isHidden()) - searchFiles(tempF); - } else { - try { - boolean isImage = false; - //compare file. if the key is matched, return value > -1. - for (String type : searchImageType) { - - final int index = tempF.getPath().lastIndexOf('.'); - final String myExtension = ((index > 0) ? tempF.getPath().substring(index).toLowerCase().intern() : ""); - - if (myExtension.equals(type) && tempF.getName().substring(0, 1).equals(".") == false) { - isImage = true; - break; - } - } - if (isImage) { - //add matched path into array - resultPath.add(tempF.getPath()); - } - } catch (Exception e) { - Toast.makeText(ImagePickerActivity.this, "error - searching image files.", Toast.LENGTH_LONG).show(); - } - } - } - - } - - @Override - protected void onResume() { - super.onResume(); - changeEinkControlPermission(true); - - // update page info - totalPage = getTotalPage(); - if (currentPage == 0) - currentPage = 1; - txtv_pageinfo.setText(currentPage + " of " + totalPage); - - if (totalPage == 1) { //only one page - txtv_pageinfo.setVisibility(View.INVISIBLE); - imgv_pre_page.setVisibility(View.INVISIBLE); - imgv_next_page.setVisibility(View.INVISIBLE); - } else { - txtv_pageinfo.setVisibility(View.VISIBLE); - imgv_pre_page.setVisibility(View.VISIBLE); - imgv_next_page.setVisibility(View.VISIBLE); - } - } - - private void changeEinkControlPermission(boolean isForNtxAppsOnly) { - Intent changePermissionIntent = new Intent("ntx.eink_control.CHANGE_PERMISSON"); - changePermissionIntent.putExtra("isPermissionNtxApp", isForNtxAppsOnly); - sendBroadcast(changePermissionIntent); - } - - // calculate total page - private int getTotalPage() { - int totalpage = resultPath.size() / (numPerPage); - - if (resultPath.size() % (numPerPage) != 0 | resultPath.size() / (numPerPage) == 0) - totalpage++; - return totalpage; - } - - // update items for showing - private void updateItems() { - if (items.size() != 0) - items.clear(); - - for (int i = numPerPage * (currentPage - 1); i < numPerPage * currentPage; i++) { - if (i >= resultPath.size()) - break; - - Map item = new HashMap(); - item.put("image", resultPath.get(i)); - items.add(item); - } - } - - public class SortIgnoreCase implements Comparator { - @Override - public int compare(Object o1, Object o2) { - String s1 = (String) o1; - String s2 = (String) o2; - - final int indexNameS1 = s1.lastIndexOf('/'); - final String filenameS1 = ((indexNameS1 > 1) ? s1.substring(indexNameS1).toLowerCase().intern() : ""); - - final int indexNameS2 = s2.lastIndexOf('/'); - final String filenameS2 = ((indexNameS2 > 1) ? s2.substring(indexNameS2).toLowerCase().intern() : ""); - - return filenameS1.toLowerCase().compareTo(filenameS2.toLowerCase()); - } - } -} \ No newline at end of file + } +} diff --git a/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/MainActivity.java b/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/MainActivity.java index 41df23f..fba08eb 100755 --- a/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/MainActivity.java +++ b/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/MainActivity.java @@ -1,4 +1,3 @@ - package ntx.painter; import android.app.Activity; @@ -16,9 +15,7 @@ import android.os.Environment; import android.os.Handler; import android.os.Message; import android.os.SystemClock; -import android.provider.ContactsContract; import android.provider.Settings; -import android.text.TextUtils; import android.text.format.Time; import android.util.DisplayMetrics; import android.util.Log; @@ -26,7 +23,6 @@ import android.view.Display; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.OrientationEventListener; import android.view.View; import android.view.Window; import android.view.WindowManager; @@ -34,810 +30,753 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; -import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.Spinner; import android.widget.TextView; -import android.widget.Toast; -import android.hardware.SensorManager; - -import junit.framework.Assert; - -import ntx.draw.nDrawHelper; -import ntx.painter.NtxView.PenType; - import java.io.File; import java.util.Timer; import java.util.TimerTask; +import junit.framework.Assert; +import ntx.draw.nDrawHelper; +import ntx.painter.NtxView.PenType; public class MainActivity extends Activity { - private final static String TAG = "MainActivity"; + private static final String TAG = "MainActivity"; - private Context mContext; - private NtxView myNewView; - private LinearLayout mainLayout, painterViewLayout; + private Context mContext; + private NtxView myNewView; + private LinearLayout mainLayout, painterViewLayout; - Timer mTimer; - private long fixed_full_refresh_time = 3 * 60 * 1000; + Timer mTimer; + private long fixed_full_refresh_time = 3 * 60 * 1000; - private String strSaveImgPath; + private String strSaveImgPath; - private Handler mHandler = new Handler(); + private Handler mHandler = new Handler(); - protected ReselectSpinner thicknessSpinner; - protected ReselectSpinner mPenTypeSpinner; - private ImageButton refreshAButton, refreshButton; - private ImageButton thicknessButton, calibrationButton, cleanButton, rotateButton, backgroundButton, saveButton, infoButton; - private ImageButton mPenTypeButton; - protected TextView txtvThicknessNum; - protected TextView mTxtPenStyle; - protected Spinner mHorizontalSpinner, mVerticalSpinner, refreshModeSpinner; - private boolean isRefreshModeSpinnerInitRun, isThicknessSpinnerInitRun, isPenTypeSpinnerInitRun; + protected ReselectSpinner thicknessSpinner; + protected ReselectSpinner mPenTypeSpinner; + private ImageButton refreshAButton, refreshButton; + private ImageButton thicknessButton, + calibrationButton, + cleanButton, + rotateButton, + backgroundButton, + saveButton, + infoButton; + private ImageButton mPenTypeButton; + protected TextView txtvThicknessNum; + protected TextView mTxtPenStyle; + protected Spinner mHorizontalSpinner, mVerticalSpinner, refreshModeSpinner; + private boolean isRefreshModeSpinnerInitRun, isThicknessSpinnerInitRun, isPenTypeSpinnerInitRun; + private Button backgroundBlackButton, backgroundWhiteButton; + private Button autoDrawButton; + private CheckBox chkbox_finger_touch; + Dialog mDialog; - private Button backgroundBlackButton, backgroundWhiteButton; - private Button autoDrawButton; - private CheckBox chkbox_finger_touch; - Dialog mDialog; + AlertDialog.Builder mBuilder; + AlertDialog dialog; - AlertDialog.Builder mBuilder; - AlertDialog dialog; + private int calHorizontalPosition, calVerticalPosition; + private int initHorizontalPosition, initVerticalPosition; + private int isSavedInteger = 0; - private int calHorizontalPosition, calVerticalPosition; + static int pen_offset_x, pen_offset_y; - private int initHorizontalPosition, initVerticalPosition; - private int isSavedInteger=0; + private int[] thicknessChoices; + private String[] mPentypeChoices; + private boolean refresh_flag = false; - static int pen_offset_x, pen_offset_y; + private WindowManager mWindowManager; + private Display mDisplay; + private int getNavigationBarHeight() { + Resources resources = mContext.getResources(); + int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); + int height = resources.getDimensionPixelSize(resourceId); - private int[] thicknessChoices; - private String[] mPentypeChoices; + return height; + } - private boolean refresh_flag = false; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mContext = getApplicationContext(); - private WindowManager mWindowManager; - private Display mDisplay; + setContentView(R.layout.main); + mainLayout = (LinearLayout) findViewById(R.id.main_layout); - private int getNavigationBarHeight() { - Resources resources = mContext.getResources(); - int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); - int height = resources.getDimensionPixelSize(resourceId); + thicknessSpinner = (ReselectSpinner) findViewById(R.id.toolbox_thickness_spinner); + mPenTypeSpinner = (ReselectSpinner) findViewById(R.id.pen_type_spinner); + thicknessButton = (ImageButton) findViewById(R.id.toolbox_thickness_button); + mPenTypeButton = (ImageButton) findViewById(R.id.pen_type_button); + txtvThicknessNum = (TextView) findViewById(R.id.toolbox_thickness_number); + mTxtPenStyle = (TextView) findViewById(R.id.pen_type_string); + calibrationButton = (ImageButton) findViewById(R.id.toolbox_calibration); - return height; - } + cleanButton = (ImageButton) findViewById(R.id.toolbox_clean); + rotateButton = (ImageButton) findViewById(R.id.toolbox_rotate); + refreshModeSpinner = (Spinner) findViewById(R.id.toolbox_refresh_mode_spinner); + backgroundButton = (ImageButton) findViewById(R.id.toolbox_background); + saveButton = (ImageButton) findViewById(R.id.toolbox_save); + infoButton = (ImageButton) findViewById(R.id.toolbox_info); + refreshAButton = (ImageButton) findViewById(R.id.toolbox_full_refreshA); + refreshButton = (ImageButton) findViewById(R.id.toolbox_full_refresh); - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mContext = getApplicationContext(); + backgroundWhiteButton = (Button) findViewById(R.id.btn_bg_white); + backgroundBlackButton = (Button) findViewById(R.id.btn_bg_black); + chkbox_finger_touch = (CheckBox) findViewById(R.id.chkbox_finger_touch); + autoDrawButton = (Button) findViewById(R.id.auto_draw); - setContentView(R.layout.main); - mainLayout = (LinearLayout) findViewById(R.id.main_layout); + painterViewLayout = (LinearLayout) findViewById(R.id.painter_view); - thicknessSpinner = (ReselectSpinner) findViewById(R.id.toolbox_thickness_spinner); - mPenTypeSpinner = (ReselectSpinner) findViewById(R.id.pen_type_spinner); - thicknessButton = (ImageButton) findViewById(R.id.toolbox_thickness_button); - mPenTypeButton = (ImageButton) findViewById(R.id.pen_type_button); - txtvThicknessNum = (TextView) findViewById(R.id.toolbox_thickness_number); - mTxtPenStyle = (TextView) findViewById(R.id.pen_type_string); - calibrationButton = (ImageButton) findViewById(R.id.toolbox_calibration); + myNewView = new NtxView(this); + painterViewLayout.addView(myNewView); - cleanButton = (ImageButton) findViewById(R.id.toolbox_clean); - rotateButton = (ImageButton) findViewById(R.id.toolbox_rotate); - refreshModeSpinner = (Spinner) findViewById(R.id.toolbox_refresh_mode_spinner); - backgroundButton = (ImageButton) findViewById(R.id.toolbox_background); - saveButton = (ImageButton) findViewById(R.id.toolbox_save); - infoButton = (ImageButton) findViewById(R.id.toolbox_info); - refreshAButton = (ImageButton) findViewById(R.id.toolbox_full_refreshA); - refreshButton = (ImageButton) findViewById(R.id.toolbox_full_refresh); + myNewView.getBottomHeight(getNavigationBarHeight()); + myNewView.setCurrentRotation(getRequestedOrientation()); - backgroundWhiteButton = (Button) findViewById(R.id.btn_bg_white); - backgroundBlackButton = (Button) findViewById(R.id.btn_bg_black); - chkbox_finger_touch = (CheckBox) findViewById(R.id.chkbox_finger_touch); - autoDrawButton = (Button) findViewById(R.id.auto_draw); + thicknessButton.setOnClickListener(mOnClickListener); + mPenTypeButton.setOnClickListener(mOnClickListener); + calibrationButton.setOnClickListener(mOnClickListener); + cleanButton.setOnClickListener(mOnClickListener); + rotateButton.setOnClickListener(mOnClickListener); + backgroundButton.setOnClickListener(mOnClickListener); + saveButton.setOnClickListener(mOnClickListener); + infoButton.setOnClickListener(mOnClickListener); + refreshAButton.setOnClickListener(mOnClickListener); + refreshButton.setOnClickListener(mOnClickListener); - painterViewLayout = (LinearLayout) findViewById(R.id.painter_view); + thicknessChoices = getResources().getIntArray(R.array.dlg_thickness_values); + String[] thickness_spinner_string_table = new String[thicknessChoices.length]; + for (int i = 0; i < thicknessChoices.length; i++) { + if (i == thicknessChoices.length - 1) thickness_spinner_string_table[i] = (String) "Pressure"; + else thickness_spinner_string_table[i] = (String) String.valueOf(thicknessChoices[i]); + } + ArrayAdapter mAdapter = + new ArrayAdapter( + mContext, R.layout.toolbox_spinner_item, thickness_spinner_string_table); + mAdapter.setDropDownViewResource(R.layout.toolbox_spinner_item); + thicknessSpinner.setAdapter(mAdapter); + isThicknessSpinnerInitRun = true; + thicknessSpinner.setOnItemSelectedListener(mOnItemSelectedListener); + setToolboxThicknessValue(myNewView.getPenThickness()); - myNewView = new NtxView(this); - painterViewLayout.addView(myNewView); + // pen type + mPentypeChoices = getResources().getStringArray(R.array.pen_type_values); - myNewView.getBottomHeight(getNavigationBarHeight()); - myNewView.setCurrentRotation(getRequestedOrientation()); + mAdapter = new ArrayAdapter(mContext, R.layout.toolbox_spinner_item, mPentypeChoices); + mAdapter.setDropDownViewResource(R.layout.toolbox_spinner_item); + mPenTypeSpinner.setAdapter(mAdapter); + isPenTypeSpinnerInitRun = true; + mPenTypeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - thicknessButton.setOnClickListener(mOnClickListener); - mPenTypeButton.setOnClickListener(mOnClickListener); - calibrationButton.setOnClickListener(mOnClickListener); - cleanButton.setOnClickListener(mOnClickListener); - rotateButton.setOnClickListener(mOnClickListener); - backgroundButton.setOnClickListener(mOnClickListener); - saveButton.setOnClickListener(mOnClickListener); - infoButton.setOnClickListener(mOnClickListener); - refreshAButton.setOnClickListener(mOnClickListener); - refreshButton.setOnClickListener(mOnClickListener); + String[] refresh_mode_spinner_string_table = + getResources().getStringArray(R.array.toolbox_refresh_mode); + mAdapter = + new ArrayAdapter( + mContext, R.layout.toolbox_spinner_item, refresh_mode_spinner_string_table); + mAdapter.setDropDownViewResource(R.layout.toolbox_spinner_item); + refreshModeSpinner.setAdapter(mAdapter); + isRefreshModeSpinnerInitRun = true; + refreshModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); + refreshModeSpinner.setOnTouchListener(mSpinnerOnTouchListener); + setToolBoxRefreshModeCurrentValue(); - thicknessChoices = getResources().getIntArray(R.array.dlg_thickness_values); - String[] thickness_spinner_string_table = new String[thicknessChoices.length]; - for (int i = 0; i < thicknessChoices.length; i++) { - if (i == thicknessChoices.length - 1) - thickness_spinner_string_table[i] = (String) "Pressure"; - else - thickness_spinner_string_table[i] = (String) String.valueOf(thicknessChoices[i]); - } - ArrayAdapter mAdapter = new ArrayAdapter( - mContext, - R.layout.toolbox_spinner_item, - thickness_spinner_string_table); - mAdapter.setDropDownViewResource(R.layout.toolbox_spinner_item); - thicknessSpinner.setAdapter(mAdapter); - isThicknessSpinnerInitRun = true; - thicknessSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - setToolboxThicknessValue(myNewView.getPenThickness()); + backgroundWhiteButton.setOnClickListener(mOnClickListener); + backgroundBlackButton.setOnClickListener(mOnClickListener); + autoDrawButton.setOnClickListener(mOnClickListener); - //pen type - mPentypeChoices = getResources().getStringArray(R.array.pen_type_values); - - mAdapter = new ArrayAdapter( - mContext, - R.layout.toolbox_spinner_item, - mPentypeChoices); - mAdapter.setDropDownViewResource(R.layout.toolbox_spinner_item); - mPenTypeSpinner.setAdapter(mAdapter); - isPenTypeSpinnerInitRun = true; - mPenTypeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - - String[] refresh_mode_spinner_string_table = getResources().getStringArray(R.array.toolbox_refresh_mode); - mAdapter = new ArrayAdapter( - mContext, - R.layout.toolbox_spinner_item, - refresh_mode_spinner_string_table); - mAdapter.setDropDownViewResource(R.layout.toolbox_spinner_item); - refreshModeSpinner.setAdapter(mAdapter); - isRefreshModeSpinnerInitRun = true; - refreshModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - refreshModeSpinner.setOnTouchListener(mSpinnerOnTouchListener); - setToolBoxRefreshModeCurrentValue(); - - backgroundWhiteButton.setOnClickListener(mOnClickListener); - backgroundBlackButton.setOnClickListener(mOnClickListener); - autoDrawButton.setOnClickListener(mOnClickListener); - - chkbox_finger_touch.setOnClickListener(new CheckBox.OnClickListener() { - @Override - public void onClick(View v) { - myNewView.setFingerTouch(chkbox_finger_touch.isChecked()); - } + chkbox_finger_touch.setOnClickListener( + new CheckBox.OnClickListener() { + @Override + public void onClick(View v) { + myNewView.setFingerTouch(chkbox_finger_touch.isChecked()); + } }); - - refreshAButton.setOnTouchListener(new View.OnTouchListener(){ - @Override - public boolean onTouch(View v, MotionEvent event) { - if(event.getAction() == MotionEvent.ACTION_DOWN) - { - refreshAButton.setImageResource(R.color.black); - } - - return false; - } - }); - - - - mBuilder=new AlertDialog.Builder(MainActivity.this); - View mView = LayoutInflater.from(MainActivity.this).inflate(R.layout.dialog_calibration, null); - mBuilder.setTitle("Calibration"); - - mHorizontalSpinner = (Spinner) mView.findViewById(R.id.horizontal_offset_spinner); - mVerticalSpinner = (Spinner) mView.findViewById(R.id.vertical_offset_spinner); - - ArrayAdapter adapterHorizontal = new ArrayAdapter(MainActivity.this, - android.R.layout.simple_spinner_item, - getResources().getStringArray(R.array.horizontal_offset_list)); - ArrayAdapter adapterVertical = new ArrayAdapter(MainActivity.this, - android.R.layout.simple_spinner_item, - getResources().getStringArray(R.array.vertical_offset_list)); - - - adapterHorizontal.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - adapterVertical.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - - mHorizontalSpinner.setAdapter(adapterHorizontal); - mVerticalSpinner.setAdapter(adapterVertical); - - mHorizontalSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - mVerticalSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - - - - initHorizontalPosition=(adapterHorizontal.getCount()-1)/2; - initVerticalPosition=(adapterHorizontal.getCount()-1)/2; - - isSavedInteger=Settings.System.getInt(getContentResolver(),"isSavedInteger",999); - - if(isSavedInteger==1){ - - calHorizontalPosition=Settings.System.getInt(getContentResolver(),"calHorizontalPosition",0); - calVerticalPosition=Settings.System.getInt(getContentResolver(),"calVerticalPosition",0); - - - }else{ - - calHorizontalPosition=initHorizontalPosition; - calVerticalPosition=initVerticalPosition; - - } - - mHorizontalSpinner.setSelection(calHorizontalPosition); - mVerticalSpinner.setSelection(calVerticalPosition); - - - final int[] horizontal_offset_int_table=getResources().getIntArray(R.array.horizontal_offset); - final int[] vertical_offset_int_table=getResources().getIntArray(R.array.vertical_offset); - - - pen_offset_x=horizontal_offset_int_table[calHorizontalPosition]; - pen_offset_y=vertical_offset_int_table[calVerticalPosition]; - - - myNewView.setInputOffset(pen_offset_x, pen_offset_y); - - - mBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - - - - calHorizontalPosition=mHorizontalSpinner.getSelectedItemPosition(); - calVerticalPosition=mVerticalSpinner.getSelectedItemPosition(); - - pen_offset_x=horizontal_offset_int_table[calHorizontalPosition]; - pen_offset_y=vertical_offset_int_table[calVerticalPosition]; - - - myNewView.setInputOffset(pen_offset_x, pen_offset_y); - - - - Settings.System.putInt(getContentResolver(),"calHorizontalPosition", calHorizontalPosition); - Settings.System.putInt(getContentResolver(),"calVerticalPosition", calVerticalPosition); - Settings.System.putInt(getContentResolver(),"isSavedInteger",1); - - - dialogInterface.dismiss(); - - - } - }); - - - mBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - - - - mHorizontalSpinner.setSelection(calHorizontalPosition); - mVerticalSpinner.setSelection(calVerticalPosition); - dialogInterface.dismiss(); - - - - - } - }); - - - - - mBuilder.setView(mView); - - dialog = mBuilder.create(); - dialog.setCancelable(false); - - mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); - mDisplay = mWindowManager.getDefaultDisplay(); - - nDrawHelper.NDrawInit(this); - - - } - - @Override - public void onBackPressed() { - super.onBackPressed(); - changeEinkControlPermission(false); - Log.d(TAG,"onBackPressed"); - nDrawHelper.NDrawSkia2FBUnInit(); - //myNewView.onBackPressed(); - int pid = android.os.Process.myPid(); - android.os.Process.killProcess(pid); - } - - @Override - protected void onResume() { - super.onResume(); - changeEinkControlPermission(true); - myNewView.onResume(); - - mTimer = new Timer(); - mTimer.schedule(new TimerTask() { - @Override - public void run() { - while (!myNewView.getFullRefreshable()) { - SystemClock.sleep(1000); - } - //RefreshHandler.sendEmptyMessage(1);//Bugfix: show white region when resume from suspend - } - }, 0, fixed_full_refresh_time); - PowerEnhanceSet(1); - - - - if (refresh_flag){ - - changeToA2DitheringMode(); - - mHandler.postDelayed(runInvalidate,2000); - - }else{ - - changeToGlobalReset(); - - } - - - } - - private void changeEinkControlPermission(boolean isForNtxAppsOnly) { - Intent changePermissionIntent = new Intent("ntx.eink_control.CHANGE_PERMISSON"); - changePermissionIntent.putExtra("isPermissionNtxApp", isForNtxAppsOnly); - sendBroadcast(changePermissionIntent); - } - - private Handler RefreshHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case 1: - if (myNewView.isEinkHardwareType()) - myNewView.fullRefresh(mainLayout); - else - mainLayout.invalidate(); - break; - } - } - }; - - private Spinner.OnTouchListener mSpinnerOnTouchListener = new Spinner.OnTouchListener() { - @Override - public boolean onTouch(View view, MotionEvent motionEvent) { - if (motionEvent.getAction() == MotionEvent.ACTION_UP) { - nDrawHelper.NDrawSwitch(false); + refreshAButton.setOnTouchListener( + new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + refreshAButton.setImageResource(R.color.black); } return false; - } - }; + } + }); - private Button.OnClickListener mOnClickListener = new Button.OnClickListener() { + mBuilder = new AlertDialog.Builder(MainActivity.this); + View mView = LayoutInflater.from(MainActivity.this).inflate(R.layout.dialog_calibration, null); + mBuilder.setTitle("Calibration"); + + mHorizontalSpinner = (Spinner) mView.findViewById(R.id.horizontal_offset_spinner); + mVerticalSpinner = (Spinner) mView.findViewById(R.id.vertical_offset_spinner); + + ArrayAdapter adapterHorizontal = + new ArrayAdapter( + MainActivity.this, + android.R.layout.simple_spinner_item, + getResources().getStringArray(R.array.horizontal_offset_list)); + ArrayAdapter adapterVertical = + new ArrayAdapter( + MainActivity.this, + android.R.layout.simple_spinner_item, + getResources().getStringArray(R.array.vertical_offset_list)); + + adapterHorizontal.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + adapterVertical.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + mHorizontalSpinner.setAdapter(adapterHorizontal); + mVerticalSpinner.setAdapter(adapterVertical); + + mHorizontalSpinner.setOnItemSelectedListener(mOnItemSelectedListener); + mVerticalSpinner.setOnItemSelectedListener(mOnItemSelectedListener); + + initHorizontalPosition = (adapterHorizontal.getCount() - 1) / 2; + initVerticalPosition = (adapterHorizontal.getCount() - 1) / 2; + + isSavedInteger = Settings.System.getInt(getContentResolver(), "isSavedInteger", 999); + + if (isSavedInteger == 1) { + + calHorizontalPosition = + Settings.System.getInt(getContentResolver(), "calHorizontalPosition", 0); + calVerticalPosition = Settings.System.getInt(getContentResolver(), "calVerticalPosition", 0); + + } else { + + calHorizontalPosition = initHorizontalPosition; + calVerticalPosition = initVerticalPosition; + } + + mHorizontalSpinner.setSelection(calHorizontalPosition); + mVerticalSpinner.setSelection(calVerticalPosition); + + final int[] horizontal_offset_int_table = getResources().getIntArray(R.array.horizontal_offset); + final int[] vertical_offset_int_table = getResources().getIntArray(R.array.vertical_offset); + + pen_offset_x = horizontal_offset_int_table[calHorizontalPosition]; + pen_offset_y = vertical_offset_int_table[calVerticalPosition]; + + myNewView.setInputOffset(pen_offset_x, pen_offset_y); + + mBuilder.setPositiveButton( + "OK", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + + calHorizontalPosition = mHorizontalSpinner.getSelectedItemPosition(); + calVerticalPosition = mVerticalSpinner.getSelectedItemPosition(); + + pen_offset_x = horizontal_offset_int_table[calHorizontalPosition]; + pen_offset_y = vertical_offset_int_table[calVerticalPosition]; + + myNewView.setInputOffset(pen_offset_x, pen_offset_y); + + Settings.System.putInt( + getContentResolver(), "calHorizontalPosition", calHorizontalPosition); + Settings.System.putInt( + getContentResolver(), "calVerticalPosition", calVerticalPosition); + Settings.System.putInt(getContentResolver(), "isSavedInteger", 1); + + dialogInterface.dismiss(); + } + }); + + mBuilder.setNegativeButton( + "Cancel", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + + mHorizontalSpinner.setSelection(calHorizontalPosition); + mVerticalSpinner.setSelection(calVerticalPosition); + dialogInterface.dismiss(); + } + }); + + mBuilder.setView(mView); + + dialog = mBuilder.create(); + dialog.setCancelable(false); + + mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); + mDisplay = mWindowManager.getDefaultDisplay(); + + nDrawHelper.NDrawInit(this); + } + + @Override + public void onBackPressed() { + super.onBackPressed(); + changeEinkControlPermission(false); + Log.d(TAG, "onBackPressed"); + nDrawHelper.NDrawSkia2FBUnInit(); + // myNewView.onBackPressed(); + int pid = android.os.Process.myPid(); + android.os.Process.killProcess(pid); + } + + @Override + protected void onResume() { + super.onResume(); + changeEinkControlPermission(true); + myNewView.onResume(); + + mTimer = new Timer(); + mTimer.schedule( + new TimerTask() { + @Override + public void run() { + while (!myNewView.getFullRefreshable()) { + SystemClock.sleep(1000); + } + // RefreshHandler.sendEmptyMessage(1);//Bugfix: show white region when resume from + // suspend + } + }, + 0, + fixed_full_refresh_time); + PowerEnhanceSet(1); + + if (refresh_flag) { + + changeToA2DitheringMode(); + + mHandler.postDelayed(runInvalidate, 2000); + + } else { + + changeToGlobalReset(); + } + } + + private void changeEinkControlPermission(boolean isForNtxAppsOnly) { + Intent changePermissionIntent = new Intent("ntx.eink_control.CHANGE_PERMISSON"); + changePermissionIntent.putExtra("isPermissionNtxApp", isForNtxAppsOnly); + sendBroadcast(changePermissionIntent); + } + + private Handler RefreshHandler = + new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case 1: + if (myNewView.isEinkHardwareType()) myNewView.fullRefresh(mainLayout); + else mainLayout.invalidate(); + break; + } + } + }; + + private Spinner.OnTouchListener mSpinnerOnTouchListener = + new Spinner.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + if (motionEvent.getAction() == MotionEvent.ACTION_UP) { + nDrawHelper.NDrawSwitch(false); + } + + return false; + } + }; + + private Button.OnClickListener mOnClickListener = + new Button.OnClickListener() { @Override public void onClick(View v) { - Log.d(TAG, "onClick"); - nDrawHelper.NDrawSwitch(false); - switch (v.getId()) { - case R.id.toolbox_thickness_button: - //trigger thickness spinner - ((ReselectSpinner) findViewById(R.id.toolbox_thickness_spinner)).performClick(); - break; - case R.id.pen_type_button: - //trigger thickness spinner - ((ReselectSpinner) findViewById(R.id.pen_type_spinner)).performClick(); - break; - case R.id.toolbox_calibration: + Log.d(TAG, "onClick"); + nDrawHelper.NDrawSwitch(false); + switch (v.getId()) { + case R.id.toolbox_thickness_button: + // trigger thickness spinner + ((ReselectSpinner) findViewById(R.id.toolbox_thickness_spinner)).performClick(); + break; + case R.id.pen_type_button: + // trigger thickness spinner + ((ReselectSpinner) findViewById(R.id.pen_type_spinner)).performClick(); + break; + case R.id.toolbox_calibration: + dialog.show(); + break; + case R.id.toolbox_clean: + myNewView.clear(); + break; + case R.id.toolbox_rotate: + // set Landscape/ Portrait + startRotate(); - dialog.show(); + break; + case R.id.toolbox_background: + AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); + builder.setMessage(getString(R.string.wait)); + mDialog = builder.create(); + mDialog.show(); - break; - case R.id.toolbox_clean: - myNewView.clear(); - break; - case R.id.toolbox_rotate: - // set Landscape/ Portrait - startRotate(); - - break; - case R.id.toolbox_background: - - AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); - builder.setMessage(getString(R.string.wait)); - mDialog = builder.create(); - mDialog.show(); - - new Thread(new Runnable() { + new Thread( + new Runnable() { @Override public void run() { - Intent intent = new Intent(MainActivity.this, ImagePickerActivity.class); - startActivityForResult(intent, REQUEST_CODE_PICK_IMAGE); + Intent intent = new Intent(MainActivity.this, ImagePickerActivity.class); + startActivityForResult(intent, REQUEST_CODE_PICK_IMAGE); } - }).start(); - break; - case R.id.toolbox_save: - Time now = new Time(); - now.setToNow(); - strSaveImgPath = Environment.getExternalStorageDirectory().getPath() + "/painter" + now.format("_%Y%m%d-%H%M%S.png"); - if (myNewView.FileOutput(strSaveImgPath) == 0) { - mDialog = createDialog( - MainActivity.this, - String.format(getString(R.string.toolbox_save_msg_success), strSaveImgPath), - new View.OnClickListener() { - @Override - public void onClick(View v) { - mDialog.dismiss(); - } - }); - mDialog.show(); - } else { - mDialog = createDialog( - MainActivity.this, - String.format(getString(R.string.toolbox_save_msg_fail), strSaveImgPath), - new View.OnClickListener() { - @Override - public void onClick(View v) { - mDialog.dismiss(); - } - }); - mDialog.show(); - } - break; - case R.id.toolbox_info: - mDialog = createDialog( - MainActivity.this, - getString(R.string.msg_info), - new View.OnClickListener() { - @Override - public void onClick(View v) { - mDialog.dismiss(); - } - }); - mDialog.show(); - break; - case R.id.btn_bg_white: - myNewView.setCanvasStyle(1); - myNewView.setBackgroundPath(""); - myNewView.clear(); - break; - case R.id.btn_bg_black: - myNewView.setCanvasStyle(2); - myNewView.setBackgroundPath(""); - myNewView.clear(); - break; - case R.id.toolbox_full_refreshA: - // refreshButton.setImageResource(R.drawable.ic_menu_reset); - if (refresh_flag == false){ + }) + .start(); + break; + case R.id.toolbox_save: + Time now = new Time(); + now.setToNow(); + strSaveImgPath = + Environment.getExternalStorageDirectory().getPath() + + "/painter" + + now.format("_%Y%m%d-%H%M%S.png"); + if (myNewView.FileOutput(strSaveImgPath) == 0) { + mDialog = + createDialog( + MainActivity.this, + String.format(getString(R.string.toolbox_save_msg_success), strSaveImgPath), + new View.OnClickListener() { + @Override + public void onClick(View v) { + mDialog.dismiss(); + } + }); + mDialog.show(); + } else { + mDialog = + createDialog( + MainActivity.this, + String.format(getString(R.string.toolbox_save_msg_fail), strSaveImgPath), + new View.OnClickListener() { + @Override + public void onClick(View v) { + mDialog.dismiss(); + } + }); + mDialog.show(); + } + break; + case R.id.toolbox_info: + mDialog = + createDialog( + MainActivity.this, + getString(R.string.msg_info), + new View.OnClickListener() { + @Override + public void onClick(View v) { + mDialog.dismiss(); + } + }); + mDialog.show(); + break; + case R.id.btn_bg_white: + myNewView.setCanvasStyle(1); + myNewView.setBackgroundPath(""); + myNewView.clear(); + break; + case R.id.btn_bg_black: + myNewView.setCanvasStyle(2); + myNewView.setBackgroundPath(""); + myNewView.clear(); + break; + case R.id.toolbox_full_refreshA: + // refreshButton.setImageResource(R.drawable.ic_menu_reset); + if (refresh_flag == false) { - refresh_flag = true; + refresh_flag = true; - }else if (refresh_flag == true){ + } else if (refresh_flag == true) { - refresh_flag = false; - } + refresh_flag = false; + } + if (refresh_flag) { + refreshAButton.setImageResource(R.drawable.ic_menu_admode); - if (refresh_flag){ + changeToA2DitheringMode(); - refreshAButton.setImageResource(R.drawable.ic_menu_admode); + myNewView.invalidate(); - changeToA2DitheringMode(); + } else { - myNewView.invalidate(); + refreshAButton.setImageResource(R.drawable.ic_menu_reset); - }else{ + changeToGlobalReset(); - refreshAButton.setImageResource(R.drawable.ic_menu_reset); + myNewView.invalidate(); + } - changeToGlobalReset(); + break; - myNewView.invalidate(); + case R.id.toolbox_full_refresh: + myNewView.invalidate(); - } + Intent ac = new Intent("ntx.eink_control.QUICK_REFRESH"); + ac.putExtra("updatemode", NtxView.UPDATE_MODE_FULL_GC16); + ac.putExtra("commandFromNtxApp", true); + sendBroadcast(ac); + break; - break; - - case R.id.toolbox_full_refresh: - - myNewView.invalidate(); - - Intent ac = new Intent("ntx.eink_control.QUICK_REFRESH"); - ac.putExtra("updatemode",NtxView.UPDATE_MODE_FULL_GC16); - ac.putExtra("commandFromNtxApp", true); - sendBroadcast(ac); - - - break; - - - default: - break; - } + default: + break; + } } - }; + }; + public void changeToA2DitheringMode() { - public void changeToA2DitheringMode(){ + doDuRefresh(); - doDuRefresh(); + Intent a = new Intent("ntx.eink_control.GLOBAL_REFRESH"); + a.putExtra("updatemode", NtxView.UPDATE_MODE_GLOBAL_FULL_A2_WITH_DITHER); + a.putExtra("commandFromNtxApp", true); + sendBroadcast(a); + } - Intent a = new Intent("ntx.eink_control.GLOBAL_REFRESH"); - a.putExtra("updatemode",NtxView.UPDATE_MODE_GLOBAL_FULL_A2_WITH_DITHER); - a.putExtra("commandFromNtxApp", true); - sendBroadcast(a); + private void doDuRefresh() { - } + Intent quickDuRefreshIntent = new Intent("ntx.eink_control.QUICK_REFRESH"); + quickDuRefreshIntent.putExtra("updatemode", NtxView.UPDATE_MODE_FULL_DU_WITH_DITHER); + quickDuRefreshIntent.putExtra("commandFromNtxApp", true); + sendBroadcast(quickDuRefreshIntent); + } - private void doDuRefresh(){ + public void changeToGlobalReset() { - Intent quickDuRefreshIntent = new Intent("ntx.eink_control.QUICK_REFRESH"); - quickDuRefreshIntent.putExtra("updatemode",NtxView.UPDATE_MODE_FULL_DU_WITH_DITHER); - quickDuRefreshIntent.putExtra("commandFromNtxApp", true); - sendBroadcast(quickDuRefreshIntent); + Intent al = new Intent("ntx.eink_control.GLOBAL_REFRESH"); + al.putExtra("updatemode", NtxView.UPDATE_MODE_GLOBAL_RESET); + al.putExtra("commandFromNtxApp", true); + sendBroadcast(al); + } - } + public static Dialog createDialog(Context context, String msg, View.OnClickListener callbackOk) { + Dialog dialog = new Dialog(context, R.style.dialog_custom); + dialog.setCancelable(false); + dialog.setContentView(R.layout.dialog_custom); + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + int vWidth = dm.widthPixels; + int vHeight = dm.heightPixels; + LinearLayout ll = (LinearLayout) dialog.findViewById(R.id.lldialog); + ll.getLayoutParams().width = (int) (vWidth * 0.5); + Window dialogWindow = dialog.getWindow(); + WindowManager.LayoutParams lp = dialogWindow.getAttributes(); + dialogWindow.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL); + lp.y = (int) (vWidth * 0.4); - public void changeToGlobalReset(){ + Button btnOk = (Button) dialog.findViewById(R.id.dialog_button_ok); + btnOk.setOnClickListener(callbackOk); + TextView txtMessage = (TextView) dialog.findViewById(R.id.message); + txtMessage.setText(msg); + return dialog; + } - Intent al = new Intent("ntx.eink_control.GLOBAL_REFRESH"); - al.putExtra("updatemode",NtxView.UPDATE_MODE_GLOBAL_RESET); - al.putExtra("commandFromNtxApp", true); - sendBroadcast(al); - - } - - - - - public static Dialog createDialog(Context context, String msg, View.OnClickListener callbackOk) { - Dialog dialog = new Dialog(context, R.style.dialog_custom); - dialog.setCancelable(false); - dialog.setContentView(R.layout.dialog_custom); - - DisplayMetrics dm = context.getResources().getDisplayMetrics(); - int vWidth = dm.widthPixels; - int vHeight = dm.heightPixels; - LinearLayout ll = (LinearLayout) dialog.findViewById(R.id.lldialog); - ll.getLayoutParams().width = (int) (vWidth * 0.5); - Window dialogWindow = dialog.getWindow(); - WindowManager.LayoutParams lp = dialogWindow.getAttributes(); - dialogWindow.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL); - lp.y = (int) (vWidth * 0.4); - - Button btnOk = (Button) dialog.findViewById(R.id.dialog_button_ok); - btnOk.setOnClickListener(callbackOk); - TextView txtMessage = (TextView) dialog.findViewById(R.id.message); - txtMessage.setText(msg); - return dialog; - } - - private AdapterView.OnItemSelectedListener mOnItemSelectedListener = new AdapterView.OnItemSelectedListener() { + private AdapterView.OnItemSelectedListener mOnItemSelectedListener = + new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View item, int position, long id) { - if (parent.getId() == R.id.toolbox_thickness_spinner) { - if (isThicknessSpinnerInitRun) { - isThicknessSpinnerInitRun = false; - return; - } - if (position == thicknessChoices.length - 1) { - myNewView.setPenThicknessWithPressure(true); - setToolboxThicknessValue(); - } else { - int thickness = thicknessChoices[position]; - //save current setting to the customized - thicknessChoices[thicknessChoices.length - 1] = thickness; - if (thickness == myNewView.getPenThickness() && (!myNewView.getPenThicknessWithPressure())) - return; - myNewView.setPenThickness(thickness); - setToolboxThicknessValue(thickness); - } - } else if (parent.getId() == R.id.toolbox_refresh_mode_spinner) { - if (isRefreshModeSpinnerInitRun) { - isRefreshModeSpinnerInitRun = false; - return; - } - int refresh_mode_selected = NtxView.refresh_mode_table[position]; - if (refresh_mode_selected == myNewView.getRefreshMode()) return; - myNewView.setRefreshMode(refresh_mode_selected); - } else if (parent.getId() == R.id.pen_type_spinner) { - if (isPenTypeSpinnerInitRun) { - isPenTypeSpinnerInitRun = false; - return; - } - - int pen_type_selected = NtxView.pen_type_table[position]; - if (pen_type_selected == myNewView.getPenType()) return; - myNewView.setPenType(pen_type_selected); - updateIcon(); + if (parent.getId() == R.id.toolbox_thickness_spinner) { + if (isThicknessSpinnerInitRun) { + isThicknessSpinnerInitRun = false; + return; } + if (position == thicknessChoices.length - 1) { + myNewView.setPenThicknessWithPressure(true); + setToolboxThicknessValue(); + } else { + int thickness = thicknessChoices[position]; + // save current setting to the customized + thicknessChoices[thicknessChoices.length - 1] = thickness; + if (thickness == myNewView.getPenThickness() + && (!myNewView.getPenThicknessWithPressure())) return; + myNewView.setPenThickness(thickness); + setToolboxThicknessValue(thickness); + } + } else if (parent.getId() == R.id.toolbox_refresh_mode_spinner) { + if (isRefreshModeSpinnerInitRun) { + isRefreshModeSpinnerInitRun = false; + return; + } + int refresh_mode_selected = NtxView.refresh_mode_table[position]; + if (refresh_mode_selected == myNewView.getRefreshMode()) return; + myNewView.setRefreshMode(refresh_mode_selected); + } else if (parent.getId() == R.id.pen_type_spinner) { + if (isPenTypeSpinnerInitRun) { + isPenTypeSpinnerInitRun = false; + return; + } + + int pen_type_selected = NtxView.pen_type_table[position]; + if (pen_type_selected == myNewView.getPenType()) return; + myNewView.setPenType(pen_type_selected); + updateIcon(); + } } @Override - public void onNothingSelected(AdapterView arg0) { - } + public void onNothingSelected(AdapterView arg0) {} + }; - }; + private void updateIcon() { + // Log.d(TAG, "updateIcon:" + mPentypeChoices[myNewView.getPenType()]); + if (myNewView.getPenType() == NtxView.PenType.PENCIL.ordinal()) { + mPenTypeButton.setImageResource(R.drawable.writing_pen_pencil); - private void updateIcon() { - //Log.d(TAG, "updateIcon:" + mPentypeChoices[myNewView.getPenType()]); - if ( myNewView.getPenType() == NtxView.PenType.PENCIL.ordinal() ) { - mPenTypeButton.setImageResource(R.drawable.writing_pen_pencil); + } else if (myNewView.getPenType() == PenType.PEN.ordinal()) { + mPenTypeButton.setImageResource(R.drawable.writing_pen_pen); - } else if (myNewView.getPenType() == PenType.PEN.ordinal()) { - mPenTypeButton.setImageResource(R.drawable.writing_pen_pen); - - } else if (myNewView.getPenType() == PenType.CHINESE_BRUSH.ordinal()) { - mPenTypeButton.setImageResource(R.drawable.writing_pen_brush); - } + } else if (myNewView.getPenType() == PenType.CHINESE_BRUSH.ordinal()) { + mPenTypeButton.setImageResource(R.drawable.writing_pen_brush); } + } - @Override - protected void onPause() { - super.onPause(); - changeEinkControlPermission(false); - myNewView.onPause(); - mTimer.cancel(); - PowerEnhanceSet(0); - - - changeToGlobalReset(); + @Override + protected void onPause() { + super.onPause(); + changeEinkControlPermission(false); + myNewView.onPause(); + mTimer.cancel(); + PowerEnhanceSet(0); + changeToGlobalReset(); + } + private void startRotate() { + if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } else if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); + } else if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + } else if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } else { + // default + // setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } + myNewView.setCurrentRotation(getRequestedOrientation()); + myNewView.setBackgroundPath(""); + } - private void startRotate() { - if(getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){ - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - } else if(getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){ - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); - } else if(getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT){ - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); - } else if(getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE){ - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } else{ - //default - // setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } - myNewView.setCurrentRotation(getRequestedOrientation()); - myNewView.setBackgroundPath(""); + public void setToolBoxRefreshModeCurrentValue() { + mHandler.removeCallbacks(runbSetRefreshMode); + mHandler.postDelayed(runbSetRefreshMode, 500); + } - - } - - public void setToolBoxRefreshModeCurrentValue() { - mHandler.removeCallbacks(runbSetRefreshMode); - mHandler.postDelayed(runbSetRefreshMode, 500); - } - - private Runnable runbSetRefreshMode = new Runnable() { + private Runnable runbSetRefreshMode = + new Runnable() { @Override public void run() { - setRefreshModeValue(myNewView.getRefreshMode()); + setRefreshModeValue(myNewView.getRefreshMode()); } - }; + }; - - private Runnable runInvalidate = new Runnable() { + private Runnable runInvalidate = + new Runnable() { @Override public void run() { - - myNewView.invalidate(); - + myNewView.invalidate(); } - }; + }; + public void setRefreshModeValue(int refresh_mode) { + for (int i = 0; i < NtxView.refresh_mode_table.length; i++) { + if (refresh_mode == NtxView.refresh_mode_table[i]) { + refreshModeSpinner.setSelection(i); + } + } + } + public void setToolboxThicknessValue() { + setToolboxThicknessValue(0); + } - public void setRefreshModeValue(int refresh_mode) { - for (int i = 0; i < NtxView.refresh_mode_table.length; i++) { - if (refresh_mode == NtxView.refresh_mode_table[i]) { - refreshModeSpinner.setSelection(i); - } - } + public void setToolboxThicknessValue(int thickness) { + for (int i = 0; i < thicknessChoices.length; i++) { + if (thickness == thicknessChoices[i]) { + thicknessSpinner.setSelection(i); + break; + } } - public void setToolboxThicknessValue() { - setToolboxThicknessValue(0); + if (myNewView.getPenThicknessWithPressure()) { + thicknessButton.setImageResource(R.drawable.ic_menu_thickness_pressure); + txtvThicknessNum.setText("P"); + return; } - public void setToolboxThicknessValue(int thickness) { - for (int i = 0; i < thicknessChoices.length; i++) { - if (thickness == thicknessChoices[i]) { - thicknessSpinner.setSelection(i); - break; - } + // update thickness button + if ((thickness >= 9) && (thickness <= 14)) { + thicknessButton.setImageResource(R.drawable.ic_menu_thickness_medium); + } else if (thickness >= 15) { + thicknessButton.setImageResource(R.drawable.ic_menu_thickness_large); + } else { + thicknessButton.setImageResource(R.drawable.ic_menu_thickness_small); + } + + // update thickness number + setThicknessNumText(String.valueOf(thickness)); + + // sync to customized thickness + thicknessChoices[thicknessChoices.length - 1] = thickness; + } + + public void setThicknessNumText(String mString) { + txtvThicknessNum.setText(mString); + } + + private static final int REQUEST_CODE_PICK_IMAGE = 1; + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + + if (mDialog.isShowing()) mDialog.dismiss(); + + switch (requestCode) { + case REQUEST_CODE_PICK_IMAGE: + if (resultCode != RESULT_OK) break; + Uri selectedImage = intent.getData(); + if (selectedImage != null) { + File mFile = new File(selectedImage.getPath()); + myNewView.setBackgroundPath(mFile.getPath()); } - - if (myNewView.getPenThicknessWithPressure()) { - thicknessButton.setImageResource(R.drawable.ic_menu_thickness_pressure); - txtvThicknessNum.setText("P"); - return; - } - - //update thickness button - if ((thickness >= 9) && (thickness <= 14)) { - thicknessButton.setImageResource(R.drawable.ic_menu_thickness_medium); - } else if (thickness >= 15) { - thicknessButton.setImageResource(R.drawable.ic_menu_thickness_large); - } else { - thicknessButton.setImageResource(R.drawable.ic_menu_thickness_small); - } - - //update thickness number - setThicknessNumText(String.valueOf(thickness)); - - //sync to customized thickness - thicknessChoices[thicknessChoices.length - 1] = thickness; - + break; + default: + Assert.fail("Unknown request code"); } + } - public void setThicknessNumText(String mString) { - txtvThicknessNum.setText(mString); + /** + * Control the 2-Step-Suspend for Netronix eInk devices + * + * @param state 1 is enable. 0 is disable. + */ + private void PowerEnhanceSet(int state) { + Log.d(TAG, "PowerEnhanceSet = " + state); + try { + Settings.System.putInt(mContext.getContentResolver(), "power_enhance_enable", state); + } catch (Exception e) { + e.printStackTrace(); + Log.d(TAG, "set POWER_ENHANCE_ENABLE error!!"); } + } - private final static int REQUEST_CODE_PICK_IMAGE = 1; - - @Override - protected void onActivityResult(int requestCode, int resultCode, - Intent intent) { - super.onActivityResult(requestCode, resultCode, intent); - - if (mDialog.isShowing()) - mDialog.dismiss(); - - switch (requestCode) { - case REQUEST_CODE_PICK_IMAGE: - if (resultCode != RESULT_OK) - break; - Uri selectedImage = intent.getData(); - if (selectedImage != null) { - File mFile = new File(selectedImage.getPath()); - myNewView.setBackgroundPath(mFile.getPath()); - } - break; - default: - Assert.fail("Unknown request code"); - } - } - - - /** - * Control the 2-Step-Suspend for Netronix eInk devices - * - * @param state - * 1 is enable. - * 0 is disable. - */ - private void PowerEnhanceSet(int state) { - Log.d(TAG, "PowerEnhanceSet = " + state); - try { - Settings.System.putInt(mContext.getContentResolver(), "power_enhance_enable", state); - } catch (Exception e) { - e.printStackTrace(); - Log.d(TAG, "set POWER_ENHANCE_ENABLE error!!"); - } - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - Log.d(TAG, "Rotation = " + mDisplay.getRotation()); - } - -} \ No newline at end of file + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + Log.d(TAG, "Rotation = " + mDisplay.getRotation()); + } +} diff --git a/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/NtxView.java b/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/NtxView.java index 01c3158..fca7541 100755 --- a/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/NtxView.java +++ b/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/NtxView.java @@ -1,7 +1,6 @@ package ntx.painter; import android.content.Context; -import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; @@ -10,953 +9,968 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; -import android.os.Debug; import android.os.Handler; import android.os.SystemProperties; import android.util.AttributeSet; import android.util.Log; -import android.view.KeyCharacterMap; -import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.widget.Toast; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; - import ntx.draw.nDrawHelper; public class NtxView extends View { - private final static String TAG = "NtxView"; + private static final String TAG = "NtxView"; - private Bitmap mBitmap; - private Canvas mCanvas; + private Bitmap mBitmap; + private Canvas mCanvas; - private Paint mPaint; - private Path mPath; + private Paint mPaint; + private Path mPath; - private Rect mCurrentUpdateEndPointRegion; - private Rect mCurrentUpdateRect; + private Rect mCurrentUpdateEndPointRegion; + private Rect mCurrentUpdateRect; - boolean mStartFlag = false; - boolean mFirstPointFlag = true; - boolean mFinishUpdateFlag = false; - boolean isFingerTouch = false; + boolean mStartFlag = false; + boolean mFirstPointFlag = true; + boolean mFinishUpdateFlag = false; + boolean isFingerTouch = false; - private int mCurX = -1; - private int mCurY = -1; - private int mOldX = -1; - private int mOldY = -1; - private int mCurWidth; + private int mCurX = -1; + private int mCurY = -1; + private int mOldX = -1; + private int mOldY = -1; + private int mCurWidth; - private int intCanvasHeight = 0; - private int intCanvasWidth = 0; + private int intCanvasHeight = 0; + private int intCanvasWidth = 0; - private int fLinewidth = 6; - private int refreshMode; - private int mPenType = PenType.PENCIL.ordinal(); - private int max_pressure = CHINESE_BRUSH_STROKEWIDTH; - private String strBackgroundPath = ""; + private int fLinewidth = 6; + private int refreshMode; + private int mPenType = PenType.PENCIL.ordinal(); + private int max_pressure = CHINESE_BRUSH_STROKEWIDTH; + private String strBackgroundPath = ""; - private static final int CHINESE_BRUSH_STROKEWIDTH = 100; + private static final int CHINESE_BRUSH_STROKEWIDTH = 100; - private boolean isFullRefreshable = true; - private boolean isEnablePenPressure = false; + private boolean isFullRefreshable = true; + private boolean isEnablePenPressure = false; - private Object mDrawLock; - public final int NOTSET = -1; + private Object mDrawLock; + public final int NOTSET = -1; - boolean isEraser = false; - private ArrayList tPath; - private Rect mCurrDrawRect; - private Paint mCurrDrawPaint; + boolean isEraser = false; + private ArrayList tPath; + private Rect mCurrDrawRect; + private Paint mCurrDrawPaint; - public static int mCurrentRotation; - public static int mNavigationBarHeight; + public static int mCurrentRotation; + public static int mNavigationBarHeight; - static private int intCanvasOffsetX; - static private int intCanvasOffsetY; - static private boolean isOutSide = false; + private static int intCanvasOffsetX; + private static int intCanvasOffsetY; + private static boolean isOutSide = false; - public static final int UPDATE_MODE_PARTIAL_DU = - android.view.View.EINK_WAVEFORM_MODE_DU - | android.view.View.EINK_UPDATE_MODE_PARTIAL; + public static final int UPDATE_MODE_PARTIAL_DU = + android.view.View.EINK_WAVEFORM_MODE_DU | android.view.View.EINK_UPDATE_MODE_PARTIAL; - public static final int UPDATE_MODE_PARTIAL_DU_WITH_DITHER = - android.view.View.EINK_WAVEFORM_MODE_DU - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER; + public static final int UPDATE_MODE_PARTIAL_DU_WITH_DITHER = + android.view.View.EINK_WAVEFORM_MODE_DU + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER; - public static final int UPDATE_MODE_PARTIAL_DU_WITH_MONO = - android.view.View.EINK_WAVEFORM_MODE_DU - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER - | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; + public static final int UPDATE_MODE_PARTIAL_DU_WITH_MONO = + android.view.View.EINK_WAVEFORM_MODE_DU + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER + | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; - public static final int UPDATE_MODE_PARTIAL_A2 = - android.view.View.EINK_WAVEFORM_MODE_A2 - | android.view.View.EINK_UPDATE_MODE_PARTIAL; + public static final int UPDATE_MODE_PARTIAL_A2 = + android.view.View.EINK_WAVEFORM_MODE_A2 | android.view.View.EINK_UPDATE_MODE_PARTIAL; - public static final int UPDATE_MODE_PARTIAL_A2_WITH_DITHER = - android.view.View.EINK_WAVEFORM_MODE_A2 - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER; + public static final int UPDATE_MODE_PARTIAL_A2_WITH_DITHER = + android.view.View.EINK_WAVEFORM_MODE_A2 + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER; - public static final int UPDATE_MODE_PARTIAL_A2_WITH_MONO = - android.view.View.EINK_WAVEFORM_MODE_A2 - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER - | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; + public static final int UPDATE_MODE_PARTIAL_A2_WITH_MONO = + android.view.View.EINK_WAVEFORM_MODE_A2 + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER + | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; - public static final int UPDATE_MODE_PARTIAL_GC4 = - android.view.View.EINK_WAVEFORM_MODE_GC4 - | android.view.View.EINK_UPDATE_MODE_PARTIAL; + public static final int UPDATE_MODE_PARTIAL_GC4 = + android.view.View.EINK_WAVEFORM_MODE_GC4 | android.view.View.EINK_UPDATE_MODE_PARTIAL; - public static final int UPDATE_MODE_PARTIAL_GC4_WITH_DITHER = - android.view.View.EINK_WAVEFORM_MODE_GC4 - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER; + public static final int UPDATE_MODE_PARTIAL_GC4_WITH_DITHER = + android.view.View.EINK_WAVEFORM_MODE_GC4 + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER; - public static final int UPDATE_MODE_PARTIAL_GC4_WITH_MONO = - android.view.View.EINK_WAVEFORM_MODE_GC4 - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER - | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; + public static final int UPDATE_MODE_PARTIAL_GC4_WITH_MONO = + android.view.View.EINK_WAVEFORM_MODE_GC4 + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER + | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; - public static final int UPDATE_MODE_FULL_GC16 = - android.view.View.EINK_WAVEFORM_MODE_GC16 - | android.view.View.EINK_UPDATE_MODE_FULL; + public static final int UPDATE_MODE_FULL_GC16 = + android.view.View.EINK_WAVEFORM_MODE_GC16 | android.view.View.EINK_UPDATE_MODE_FULL; - public static final int UPDATE_MODE_PARTIAL_GC16 = - android.view.View.EINK_WAVEFORM_MODE_GC16 - | android.view.View.EINK_UPDATE_MODE_PARTIAL; + public static final int UPDATE_MODE_PARTIAL_GC16 = + android.view.View.EINK_WAVEFORM_MODE_GC16 | android.view.View.EINK_UPDATE_MODE_PARTIAL; - public static final int UPDATE_MODE_PARTIAL_AUTO = - android.view.View.EINK_WAVEFORM_MODE_AUTO - | android.view.View.EINK_UPDATE_MODE_PARTIAL; + public static final int UPDATE_MODE_PARTIAL_AUTO = + android.view.View.EINK_WAVEFORM_MODE_AUTO | android.view.View.EINK_UPDATE_MODE_PARTIAL; - public static final int UPDATE_MODE_FULL_A2 = - android.view.View.EINK_WAVEFORM_MODE_A2 - | android.view.View.EINK_UPDATE_MODE_FULL - | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; + public static final int UPDATE_MODE_FULL_A2 = + android.view.View.EINK_WAVEFORM_MODE_A2 + | android.view.View.EINK_UPDATE_MODE_FULL + | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; - public static final int UPDATE_MODE_FULL_DU = - android.view.View.EINK_WAVEFORM_MODE_DU - | android.view.View.EINK_UPDATE_MODE_FULL - | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; + public static final int UPDATE_MODE_FULL_DU = + android.view.View.EINK_WAVEFORM_MODE_DU + | android.view.View.EINK_UPDATE_MODE_FULL + | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; - public static final int UPDATE_MODE_FULL_DU_WITH_DITHER = - android.view.View.EINK_WAVEFORM_MODE_DU - | android.view.View.EINK_UPDATE_MODE_FULL - | android.view.View.EINK_DITHER_MODE_DITHER; + public static final int UPDATE_MODE_FULL_DU_WITH_DITHER = + android.view.View.EINK_WAVEFORM_MODE_DU + | android.view.View.EINK_UPDATE_MODE_FULL + | android.view.View.EINK_DITHER_MODE_DITHER; - public static final int EINK_STATIC_MODE_SET = 0x10000000; - public static final int EINK_STATIC_MODE_CLEAR = 0x20000000; - public static final int UPDATE_MODE_GLOBAL_PARTIAL_A2_WITH_DITHER = - android.view.View.EINK_AUTO_MODE_REGIONAL - | android.view.View.EINK_WAIT_MODE_NOWAIT - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_WAVEFORM_MODE_A2 - | android.view.View.EINK_DITHER_MODE_DITHER - | EINK_STATIC_MODE_SET; - public static final int UPDATE_MODE_GLOBAL_RESET = EINK_STATIC_MODE_CLEAR; + public static final int EINK_STATIC_MODE_SET = 0x10000000; + public static final int EINK_STATIC_MODE_CLEAR = 0x20000000; + public static final int UPDATE_MODE_GLOBAL_PARTIAL_A2_WITH_DITHER = + android.view.View.EINK_AUTO_MODE_REGIONAL + | android.view.View.EINK_WAIT_MODE_NOWAIT + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_WAVEFORM_MODE_A2 + | android.view.View.EINK_DITHER_MODE_DITHER + | EINK_STATIC_MODE_SET; + public static final int UPDATE_MODE_GLOBAL_RESET = EINK_STATIC_MODE_CLEAR; - public static final int UPDATE_MODE_GLOBAL_FULL_GC16 = - android.view.View.EINK_UPDATE_MODE_FULL - | android.view.View.EINK_WAVEFORM_MODE_GC16 - | EINK_STATIC_MODE_SET; + public static final int UPDATE_MODE_GLOBAL_FULL_GC16 = + android.view.View.EINK_UPDATE_MODE_FULL + | android.view.View.EINK_WAVEFORM_MODE_GC16 + | EINK_STATIC_MODE_SET; - public static final int UPDATE_MODE_GLOBAL_FULL_A2_WITH_DITHER = - android.view.View.EINK_AUTO_MODE_REGIONAL - | android.view.View.EINK_WAIT_MODE_WAIT - | android.view.View.EINK_UPDATE_MODE_FULL - | android.view.View.EINK_WAVEFORM_MODE_A2 - | android.view.View.EINK_DITHER_MODE_DITHER - | EINK_STATIC_MODE_SET; + public static final int UPDATE_MODE_GLOBAL_FULL_A2_WITH_DITHER = + android.view.View.EINK_AUTO_MODE_REGIONAL + | android.view.View.EINK_WAIT_MODE_WAIT + | android.view.View.EINK_UPDATE_MODE_FULL + | android.view.View.EINK_WAVEFORM_MODE_A2 + | android.view.View.EINK_DITHER_MODE_DITHER + | EINK_STATIC_MODE_SET; - public static final int MODE_APPNDRAWSTROKESYNC = 0x01000000; + public static final int MODE_APPNDRAWSTROKESYNC = 0x01000000; - public static int[] refresh_mode_table = { - UPDATE_MODE_PARTIAL_DU, - UPDATE_MODE_PARTIAL_DU_WITH_DITHER, - UPDATE_MODE_PARTIAL_DU_WITH_MONO, - UPDATE_MODE_PARTIAL_A2, - UPDATE_MODE_PARTIAL_A2_WITH_DITHER, - UPDATE_MODE_PARTIAL_A2_WITH_MONO, - UPDATE_MODE_PARTIAL_GC4, - UPDATE_MODE_PARTIAL_GC4_WITH_DITHER, - UPDATE_MODE_PARTIAL_GC4_WITH_MONO - }; + public static int[] refresh_mode_table = { + UPDATE_MODE_PARTIAL_DU, + UPDATE_MODE_PARTIAL_DU_WITH_DITHER, + UPDATE_MODE_PARTIAL_DU_WITH_MONO, + UPDATE_MODE_PARTIAL_A2, + UPDATE_MODE_PARTIAL_A2_WITH_DITHER, + UPDATE_MODE_PARTIAL_A2_WITH_MONO, + UPDATE_MODE_PARTIAL_GC4, + UPDATE_MODE_PARTIAL_GC4_WITH_DITHER, + UPDATE_MODE_PARTIAL_GC4_WITH_MONO + }; - public enum PenType { - PENCIL, - PEN, - CHINESE_BRUSH, + public enum PenType { + PENCIL, + PEN, + CHINESE_BRUSH, + } + + public static int[] pen_type_table = { + PenType.PENCIL.ordinal(), PenType.PEN.ordinal(), PenType.CHINESE_BRUSH.ordinal(), + }; + + public static boolean isEinkHardwareType() { + return SystemProperties.get("ro.product.hardwareType", "").equals("E60Q20") + || SystemProperties.get("ro.product.hardwareType", "").equals("E60Q30") + || SystemProperties.get("ro.product.hardwareType", "").equals("E60Q50") + || SystemProperties.get("ro.product.hardwareType", "").equals("E60Q60") + || SystemProperties.get("ro.product.hardwareType", "").equals("ED0Q00") + || SystemProperties.get("ro.product.hardwareType", "").equals("E60QD0") + || SystemProperties.get("ro.product.hardwareType", "").equals("E70Q30") + || SystemProperties.get("ro.product.hardwareType", "").equals("EA0Q00") + || SystemProperties.get("ro.product.hardwareType", "").equals("E60QR2"); + } + + public static boolean isEinkHandWritingHardwareType() { + return SystemProperties.get("ro.product.hardwareType", "").equals("E60Q60") + || SystemProperties.get("ro.product.hardwareType", "").equals("ED0Q00") + || SystemProperties.get("ro.product.hardwareType", "").equals("E70Q30") + || SystemProperties.get("ro.product.hardwareType", "").equals("EA0Q00") + || SystemProperties.get("ro.product.hardwareType", "").equals("E60QR2"); + } + + public static boolean isEink6InchHardwareType() { + return SystemProperties.get("ro.product.hardwareType", "").equals("E60Q60") + || SystemProperties.get("ro.product.hardwareType", "").equals("E60QR2") + || SystemProperties.get("ro.product.hardwareType", "").equals("EA0Q00"); + } + + public static boolean isPenUpdateModeDU() { + return SystemProperties.get("ro.product.hardwareType", "").equals("ED0Q00"); + } + + public void setCurrentRotation(int mCurrent_Rotation) { + + this.mCurrentRotation = mCurrent_Rotation; + nDrawHelper.NDrawSetInputRotation(mCurrentRotation); + } + + public NtxView(Context context) { + this(context, null); + } + + public NtxView(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.textViewStyle); + } + + public NtxView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + public void init() { + + mPaint = new Paint(); + mPaint.setAntiAlias(false); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setColor(Color.BLACK); + mPaint.setStrokeJoin(Paint.Join.ROUND); + mPaint.setStrokeCap(Paint.Cap.ROUND); + + mPath = new Path(); + tPath = new ArrayList(); + + mCurrentUpdateEndPointRegion = new Rect(0, 0, 0, 0); + mCurrentUpdateRect = new Rect(NOTSET, NOTSET, NOTSET, NOTSET); + + mCurrDrawPaint = new Paint(); + mCurrDrawPaint.setAntiAlias(false); + mCurrDrawPaint.setStyle(Paint.Style.STROKE); + mCurrDrawPaint.setColor(Color.BLACK); + mCurrDrawPaint.setStrokeJoin(Paint.Join.ROUND); + mCurrDrawPaint.setStrokeCap(Paint.Cap.ROUND); + + mFirstPointFlag = true; + + mDrawLock = new Object(); + + // update_mode initial + if (isPenUpdateModeDU()) { + setRefreshMode(UPDATE_MODE_PARTIAL_DU); + } else { + setRefreshMode(UPDATE_MODE_PARTIAL_A2); + } + setPenThickness(fLinewidth); + } + + private int getRelativeTop(View myView) { + if (myView.getParent() == myView.getRootView()) return myView.getTop(); + else return myView.getTop() + getRelativeTop((View) myView.getParent()); + } + + public void getBottomHeight(int mNavigationBar_Height) { + + this.mNavigationBarHeight = mNavigationBar_Height; + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + // Log.d(TAG, "onSizeChanged w h oldw oldh = " + "( " + w + ", " + h + ", " + oldw + ", " + oldh + // + ")"); + int curW = 0; + int curH = 0; + + if (curW < w) curW = w; + if (curH < h) curH = h; + + intCanvasHeight = curH; + intCanvasWidth = curW; + + Canvas newCanvas = new Canvas(); + Bitmap newBitmap = Bitmap.createBitmap(curW, curH, Bitmap.Config.ARGB_8888); + newCanvas.setBitmap(newBitmap); + newCanvas.drawColor(Color.WHITE); + if (mBitmap != null) { + newCanvas.drawBitmap(mBitmap, 0, 0, null); + } + mBitmap = newBitmap; + mCanvas = newCanvas; + + synchronized (mDrawLock) { + mDrawLock.notifyAll(); } - public static int[] pen_type_table = { - PenType.PENCIL.ordinal(), - PenType.PEN.ordinal(), - PenType.CHINESE_BRUSH.ordinal(), - }; + int Ndraw_region_left = 0; + int Ndraw_region_top = getRelativeTop(NtxView.this); + int Ndraw_region_right = Ndraw_region_left + w; + int Ndraw_region_bottom = Ndraw_region_top + h; + int[] packet1 = {Ndraw_region_left, Ndraw_region_top, Ndraw_region_right, Ndraw_region_bottom}; - public static boolean isEinkHardwareType() { - return SystemProperties.get("ro.product.hardwareType", "").equals("E60Q20") - || SystemProperties.get("ro.product.hardwareType", "").equals("E60Q30") - || SystemProperties.get("ro.product.hardwareType", "").equals("E60Q50") - || SystemProperties.get("ro.product.hardwareType", "").equals("E60Q60") - || SystemProperties.get("ro.product.hardwareType", "").equals("ED0Q00") - || SystemProperties.get("ro.product.hardwareType", "").equals("E60QD0") - || SystemProperties.get("ro.product.hardwareType", "").equals("E70Q30") - || SystemProperties.get("ro.product.hardwareType", "").equals("EA0Q00") - || SystemProperties.get("ro.product.hardwareType", "").equals("E60QR2"); + // Log.d(TAG, "packet1: " + Arrays.toString(packet1)); + // nDrawHelper.NDrawInit(); + nDrawHelper.NDrawSetDrawRegion(packet1); + + // test_setNDrawMultiActiveRegion(w, h); + + } + + private void test_NDrawDropFrames() { + nDrawHelper.NDrawDropFrames(400000000); // 400 ms + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawBitmap(mBitmap, 0, 0, null); + } + + /** + * Return a String describing the calling method and location at a particular stack depth. + * + * @param callStack the Thread stack + * @param depth the depth of stack to return information for. + * @return the String describing the caller at that depth. + */ + private static String getCaller(StackTraceElement callStack[], int depth) { + // callStack[4] is the caller of the method that called getCallers() + if (4 + depth >= callStack.length) { + return ""; + } + StackTraceElement caller = callStack[4 + depth]; + return caller.getClassName() + "." + caller.getMethodName() + ":" + caller.getLineNumber(); + } + + /** + * Return a string consisting of methods and locations at multiple call stack levels. + * + * @param depth the number of levels to return, starting with the immediate caller. + * @return a string describing the call stack. {@hide} + */ + public static String getCallers(final int depth) { + final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < depth; i++) { + sb.append(getCaller(callStack, i)).append(" "); + } + return sb.toString(); + } + + /** + * Return a string consisting of methods and locations at multiple call stack levels. + * + * @param depth the number of levels to return, starting with the immediate caller. + * @return a string describing the call stack. {@hide} + */ + public static String getCallers(final int start, int depth) { + final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); + StringBuffer sb = new StringBuffer(); + depth += start; + for (int i = start; i < depth; i++) { + sb.append(getCaller(callStack, i)).append(" "); + } + return sb.toString(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + processOnTouchEventNtx(event); + return true; + } + + @Override + public boolean onHoverEvent(MotionEvent event) { + + super.onHoverEvent(event); + if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { + mHandler.removeCallbacks(runPostInvalidate); + mHandler.postDelayed(runPostInvalidate, 3000); + } else if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) { + mHandler.removeCallbacks(runPostInvalidate); + nDrawHelper.NDrawSwitch(true); } - public static boolean isEinkHandWritingHardwareType() { - return SystemProperties.get("ro.product.hardwareType", "").equals("E60Q60") - || SystemProperties.get("ro.product.hardwareType", "").equals("ED0Q00") - || SystemProperties.get("ro.product.hardwareType", "").equals("E70Q30") - || SystemProperties.get("ro.product.hardwareType", "").equals("EA0Q00") - || SystemProperties.get("ro.product.hardwareType", "").equals("E60QR2"); + return true; + } + + boolean processOnTouchEventNtx(MotionEvent event) { + // Check Finger Touch or Pen + switch (getToolType(event)) { + case MotionEvent.TOOL_TYPE_STYLUS: // set Pan touch + if (isFingerTouch) return true; + + break; + case MotionEvent.TOOL_TYPE_FINGER: // set Hand touch + if (false == isFingerTouch) return true; + + break; } - public static boolean isEink6InchHardwareType() { - return SystemProperties.get("ro.product.hardwareType", "").equals("E60Q60") - || SystemProperties.get("ro.product.hardwareType", "").equals("E60QR2") - || SystemProperties.get("ro.product.hardwareType", "").equals("EA0Q00"); - } + if (event.getAction() == MotionEvent.ACTION_DOWN) { - public static boolean isPenUpdateModeDU() { - return SystemProperties.get("ro.product.hardwareType", "").equals("ED0Q00"); - } - - public void setCurrentRotation(int mCurrent_Rotation) { - - this.mCurrentRotation = mCurrent_Rotation; - nDrawHelper.NDrawSetInputRotation(mCurrentRotation); - - - } - - public NtxView(Context context) { - this(context, null); - } - - public NtxView(Context context, AttributeSet attrs) { - this(context, attrs, android.R.attr.textViewStyle); - } - - public NtxView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - public void init() { - - mPaint = new Paint(); - mPaint.setAntiAlias(false); - mPaint.setStyle(Paint.Style.STROKE); - mPaint.setColor(Color.BLACK); - mPaint.setStrokeJoin(Paint.Join.ROUND); - mPaint.setStrokeCap(Paint.Cap.ROUND); - - mPath = new Path(); - tPath = new ArrayList(); - - mCurrentUpdateEndPointRegion = new Rect(0, 0, 0, 0); - mCurrentUpdateRect = new Rect(NOTSET, NOTSET, NOTSET, NOTSET); - - mCurrDrawPaint = new Paint(); - mCurrDrawPaint.setAntiAlias(false); - mCurrDrawPaint.setStyle(Paint.Style.STROKE); - mCurrDrawPaint.setColor(Color.BLACK); - mCurrDrawPaint.setStrokeJoin(Paint.Join.ROUND); - mCurrDrawPaint.setStrokeCap(Paint.Cap.ROUND); - - mFirstPointFlag = true; - - mDrawLock = new Object(); - - // update_mode initial - if (isPenUpdateModeDU()) { - setRefreshMode(UPDATE_MODE_PARTIAL_DU); - } else { - setRefreshMode(UPDATE_MODE_PARTIAL_A2); - } - setPenThickness(fLinewidth); - } - - private int getRelativeTop(View myView) { - if (myView.getParent() == myView.getRootView()) - return myView.getTop(); - else - return myView.getTop() + getRelativeTop((View) myView.getParent()); - } - - public void getBottomHeight(int mNavigationBar_Height) { - - this.mNavigationBarHeight = mNavigationBar_Height; - - } - - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - //Log.d(TAG, "onSizeChanged w h oldw oldh = " + "( " + w + ", " + h + ", " + oldw + ", " + oldh + ")"); - int curW = 0; - int curH = 0; - - if (curW < w) - curW = w; - if (curH < h) - curH = h; - - intCanvasHeight = curH; - intCanvasWidth = curW; - - Canvas newCanvas = new Canvas(); - Bitmap newBitmap = Bitmap.createBitmap(curW, curH, Bitmap.Config.ARGB_8888); - newCanvas.setBitmap(newBitmap); - newCanvas.drawColor(Color.WHITE); - if (mBitmap != null) { - newCanvas.drawBitmap(mBitmap, 0, 0, null); - } - mBitmap = newBitmap; - mCanvas = newCanvas; - - synchronized (mDrawLock) { - mDrawLock.notifyAll(); - } - - int Ndraw_region_left = 0; - int Ndraw_region_top = getRelativeTop(NtxView.this); - int Ndraw_region_right = Ndraw_region_left + w; - int Ndraw_region_bottom = Ndraw_region_top + h; - int[] packet1 = {Ndraw_region_left, Ndraw_region_top, Ndraw_region_right, Ndraw_region_bottom}; - - //Log.d(TAG, "packet1: " + Arrays.toString(packet1)); - //nDrawHelper.NDrawInit(); - nDrawHelper.NDrawSetDrawRegion(packet1); - - //test_setNDrawMultiActiveRegion(w, h); - - } - - private void test_NDrawDropFrames() { - nDrawHelper.NDrawDropFrames(400000000); //400 ms - } - - @Override - protected void onDraw(Canvas canvas) { - canvas.drawBitmap(mBitmap, 0, 0, null); - } - - /** - * Return a String describing the calling method and location at a particular stack depth. - * - * @param callStack the Thread stack - * @param depth the depth of stack to return information for. - * @return the String describing the caller at that depth. - */ - private static String getCaller(StackTraceElement callStack[], int depth) { - // callStack[4] is the caller of the method that called getCallers() - if (4 + depth >= callStack.length) { - return ""; - } - StackTraceElement caller = callStack[4 + depth]; - return caller.getClassName() + "." + caller.getMethodName() + ":" + caller.getLineNumber(); - } - - /** - * Return a string consisting of methods and locations at multiple call stack levels. - * - * @param depth the number of levels to return, starting with the immediate caller. - * @return a string describing the call stack. - * {@hide} - */ - public static String getCallers(final int depth) { - final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < depth; i++) { - sb.append(getCaller(callStack, i)).append(" "); - } - return sb.toString(); - } - - /** - * Return a string consisting of methods and locations at multiple call stack levels. - * - * @param depth the number of levels to return, starting with the immediate caller. - * @return a string describing the call stack. - * {@hide} - */ - public static String getCallers(final int start, int depth) { - final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); - StringBuffer sb = new StringBuffer(); - depth += start; - for (int i = start; i < depth; i++) { - sb.append(getCaller(callStack, i)).append(" "); - } - return sb.toString(); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - processOnTouchEventNtx(event); - return true; - } - - @Override - public boolean onHoverEvent(MotionEvent event) { - - super.onHoverEvent(event); - if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { - mHandler.removeCallbacks(runPostInvalidate); - mHandler.postDelayed(runPostInvalidate, 3000); - } else if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) { - mHandler.removeCallbacks(runPostInvalidate); - nDrawHelper.NDrawSwitch(true); - } - - return true; - } - - boolean processOnTouchEventNtx(MotionEvent event) { - // Check Finger Touch or Pen - switch (getToolType(event)) { - case MotionEvent.TOOL_TYPE_STYLUS: // set Pan touch - if (isFingerTouch) return true; - - break; - case MotionEvent.TOOL_TYPE_FINGER: // set Hand touch - if (false == isFingerTouch) return true; - - break; - - } - - if (event.getAction() == MotionEvent.ACTION_DOWN) { - - // Check Pen ERASER and HIGHLIGHT button pressed - if ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) > 0 ? true : false) { // NTX Eraser - isEraser = true; - nDrawHelper.NDrawSwitch(false); - } else if ((event.getButtonState() & MotionEvent.BUTTON_TERTIARY) > 0 ? true : false) { // Lin Eraser - isEraser = true; - nDrawHelper.NDrawSwitch(false); - } else if ((event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER)) { - isEraser = true; - nDrawHelper.NDrawSwitch(false); - } else { - isEraser = false; - nDrawHelper.NDrawSwitch(true); - - //nDrawHelper.NDrawSetInputOffset(intCanvasOffsetX,intCanvasOffsetY); - - } - - nDrawHelper.NDrawSetStrokeWidth(fLinewidth); - - startUpdating(); - } - - int historySize = event.getHistorySize(); - for (int i = 0; i < historySize; i++) { - drawPointNtx(event.getHistoricalX(i) + intCanvasOffsetX, - event.getHistoricalY(i) + intCanvasOffsetY + 1, - event.getHistoricalPressure(i), - event.getHistoricalSize(i)); - } - - drawPointNtx(event.getX() + intCanvasOffsetX, - event.getY() + intCanvasOffsetY + 1, - event.getPressure(), - event.getSize()); - - if (event.getAction() == MotionEvent.ACTION_UP) { - stopUpdating(); - //nDrawHelper.NDrawSwitch(false); - } else if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (isEraser) - eraseDraw(); - } - - return true; - } - - void startUpdating() { - setFullRefreshable(false); - mHandler.removeCallbacks(runPostInvalidate); - mStartFlag = true; - mCurrDrawRect = new Rect(0, 0, 0, 0); - - } - - void stopUpdating() { - setFullRefreshable(true); - mStartFlag = false; - - mFirstPointFlag = true; - synchronized (mDrawLock) { - mCurrentUpdateRect.set(NOTSET, NOTSET, NOTSET, NOTSET); - } - int mode = MODE_APPNDRAWSTROKESYNC | android.view.View.EINK_WAVEFORM_MODE_GC16; - Log.d(TAG, "app invalidate() " + Integer.toHexString(mode)); - invalidate(mode); - } - - private void drawPointNtx(float x, float y, float pressure, float size) { - - int x_int = 0; - int y_int = 0; - - x_int = (int) x; - y_int = (int) y; - - currDrawRectMove((int) x, (int) y); - - if (mCurX != x_int || mCurY != y_int) { - if (mFinishUpdateFlag) { - mFinishUpdateFlag = false; - } - /*if (isEnablePenPressure) { - mPaint.setStrokeWidth(((float) max_pressure) * pressure); - //nDrawHelper.NDrawSetStrokeWidth((int) (((float) max_pressure) * pressure)); - }*/ - updatePenStroke(pressure); - mCurX = x_int; - mCurY = y_int; - if (mCurY > intCanvasHeight) { - mCurY = intCanvasHeight; - } - if (mCurY < 0) { - mCurY = 0; - isOutSide = true; - return; - } - if (isOutSide == true) { - isOutSide = false; - stopUpdating(); - } - if (mCurX > intCanvasWidth) { - mCurX = intCanvasWidth; - } - if (mCurX < 0) { - mCurX = 0; - } - if (isEnablePenPressure) { - mCurWidth = (int) ((((float) max_pressure) * pressure) / 2.0f); - } else { - mCurWidth = getPenThickness() / 2; - } - if (mCurWidth < 1) { - mCurWidth = 1; - } - - if (mCanvas != null) { - int newleft = mCurX - mCurWidth; - int newtop = mCurY - mCurWidth; - int newright = mCurX + mCurWidth; - int newbottom = mCurY + mCurWidth; - synchronized (mDrawLock) { - if (mFirstPointFlag) { - mFirstPointFlag = false; - mCurrentUpdateRect.set(newleft, newtop, newright, newbottom); - } else { - mCurrentUpdateRect.union(newleft, newtop, newright, newbottom); - - if (false == isEraser) { - mCanvas.drawLine((float) mOldX, (float) mOldY, (float) mCurX, (float) mCurY, mPaint); - - - if (getPenType() == PenType.PEN.ordinal()){ - tPath.add(new TouchPath( (int)(fLinewidth * pressure), mPaint.getColor(), mOldX, mOldY, mCurX, mCurY, mCurrDrawRect)); - - }else if (getPenType() == PenType.CHINESE_BRUSH.ordinal()) { - tPath.add(new TouchPath((int)(float)(max_pressure * Math.pow(pressure,3)), mPaint.getColor(), mOldX, mOldY, mCurX, mCurY, mCurrDrawRect)); - - }else{ - // PenType.PENCIL.ordinal() - tPath.add(new TouchPath(fLinewidth, mPaint.getColor(), mOldX, mOldY, mCurX, mCurY, mCurrDrawRect)); - - } - } - } - mOldX = mCurX; - mOldY = mCurY; - mCurrentUpdateEndPointRegion.set(newleft, newtop, newright, newbottom); - } - } - } - } - - private Handler mHandler = new Handler(); - - public synchronized void clear() { - if (mCanvas != null) { - tPath.clear(); - - mPath.reset(); - drawBackground(); - mHandler.removeCallbacks(runClear); - mHandler.postDelayed(runClear, 1000); - } - } - - private Runnable runPostInvalidate = new Runnable() { - @Override - public void run() { - postInvalidate(); - } - }; - - private Runnable runClear = new Runnable() { - @Override - public void run() { - - mFirstPointFlag = true; - fullRefresh(NtxView.this); - } - }; - - public int FileOutput(String output_path) { - - Bitmap bmpBackground = null; - - if (strBackgroundPath.equals("")) { - if (mBitmap == null) - return -1; // retrun -1 ==> fail; return 0 ==> success; - - bmpBackground = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888); - } else { - bmpBackground = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(strBackgroundPath), mBitmap.getWidth(), - mBitmap.getHeight(), true); - } - - if (mBitmap != null) { - // Create new canvas to merge paint and background - bmpBackground = combineBitmap(bmpBackground, mBitmap); - } - - try { - FileOutputStream fos = new FileOutputStream(output_path); - bmpBackground.compress(Bitmap.CompressFormat.PNG, 100, fos); - fos.close(); - return 0; - } catch (IOException e) { - e.printStackTrace(); - return -1; - } - - } - - /** - * @param background - * @param foreground - * @return Bitmap - */ - public static Bitmap combineBitmap(Bitmap background, Bitmap foreground) { - if (background == null) { - return null; - } - int bgWidth = background.getWidth(); - int bgHeight = background.getHeight(); - int fgWidth = foreground.getWidth(); - int fgHeight = foreground.getHeight(); - Bitmap newmap = Bitmap.createBitmap(bgWidth, bgHeight, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(newmap); - canvas.drawBitmap(background, 0, 0, null); - canvas.drawBitmap(foreground, (bgWidth - fgWidth) / 2, - (bgHeight - fgHeight) / 2, null); - canvas.save(Canvas.ALL_SAVE_FLAG); - canvas.restore(); - return newmap; - } - - public int getPenThickness() { - return fLinewidth; - } - - public void setPenThickness(int i) { - fLinewidth = i; - mPaint.setStrokeWidth(fLinewidth); - nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(NOTSET); - nDrawHelper.NDrawSetStrokeWidth(fLinewidth); - isEnablePenPressure = false; - return; - } - - private void updatePenStroke( float pressure ) { - - if ( mPenType == PenType.PENCIL.ordinal() ) { - mPaint.setStrokeWidth(fLinewidth); - nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(NOTSET); - nDrawHelper.NDrawSetStrokeWidth(fLinewidth); - } else if ( mPenType == PenType.PEN.ordinal() ) { - isEnablePenPressure = true; - mPaint.setStrokeWidth(fLinewidth * pressure); - nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(fLinewidth); - - } else if ( mPenType == PenType.CHINESE_BRUSH.ordinal() ) { - isEnablePenPressure = true; - mPaint.setStrokeWidth((float)(max_pressure * Math.pow(pressure,3))); - nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(max_pressure); - - } - } - - - - public boolean getPenThicknessWithPressure() { - return isEnablePenPressure; - } - - public void setPenThicknessWithPressure(boolean enable_pen_pressure) { - nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(max_pressure); - isEnablePenPressure = enable_pen_pressure; - return; - } - - public void setFullRefreshable(boolean fullRefreshable) { - isFullRefreshable = fullRefreshable; - } - - public boolean getFullRefreshable() { - return isFullRefreshable; - } - - public void setFingerTouch(boolean checked) { - isFingerTouch = checked; - } - - private int intCanvasStyle = 1; - - public void setCanvasStyle(int i) { - if (i == 1) { - intCanvasStyle = 1; - mPaint.setColor(Color.BLACK); - mCanvas.drawColor(Color.WHITE); - nDrawHelper.NDrawSetPaintColor(Color.BLACK); - } else if (i == 2) { - intCanvasStyle = 2; - mPaint.setColor(Color.WHITE); - mCanvas.drawColor(Color.BLACK); - nDrawHelper.NDrawSetPaintColor(Color.WHITE); - } - mPaint.setXfermode(null); - setPenThickness(fLinewidth); - } - - public void setRefreshMode(int refresh_mode) { - refreshMode = refresh_mode; - nDrawHelper.NDrawSetUpdateMode(refresh_mode); - } - - public int getRefreshMode() { - return refreshMode; - } - - public void setPenType(int type) { - mPenType = type; - nDrawHelper.NDrawSetPenType(type); - } - - public int getPenType() { - //Log.d(TAG, "getPenType: " + mPenType); - return mPenType; - } - - public void setBackgroundPath(String background_path) { - strBackgroundPath = background_path; - - if (new File(background_path).exists()) { - tPath.clear(); - drawBackground(); - invalidate(); - } - } - - public void onPause() { + // Check Pen ERASER and HIGHLIGHT button pressed + if ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) > 0 + ? true + : false) { // NTX Eraser + isEraser = true; nDrawHelper.NDrawSwitch(false); - } - - public void onResume() { + } else if ((event.getButtonState() & MotionEvent.BUTTON_TERTIARY) > 0 + ? true + : false) { // Lin Eraser + isEraser = true; + nDrawHelper.NDrawSwitch(false); + } else if ((event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER)) { + isEraser = true; + nDrawHelper.NDrawSwitch(false); + } else { + isEraser = false; nDrawHelper.NDrawSwitch(true); + + // nDrawHelper.NDrawSetInputOffset(intCanvasOffsetX,intCanvasOffsetY); + + } + + nDrawHelper.NDrawSetStrokeWidth(fLinewidth); + + startUpdating(); } - public void drawBackground() { - if (intCanvasStyle == 1) { - mBitmap.eraseColor(Color.WHITE); - } else if (intCanvasStyle == 2) { - mBitmap.eraseColor(Color.BLACK); - } - - if (new File(strBackgroundPath).exists()) { - mBitmap.eraseColor(Color.TRANSPARENT); - setBackgroundDrawable(new BitmapDrawable(strBackgroundPath)); - } + int historySize = event.getHistorySize(); + for (int i = 0; i < historySize; i++) { + drawPointNtx( + event.getHistoricalX(i) + intCanvasOffsetX, + event.getHistoricalY(i) + intCanvasOffsetY + 1, + event.getHistoricalPressure(i), + event.getHistoricalSize(i)); } - private int getToolType(MotionEvent event) { - return hasToolStylus(event) ? MotionEvent.TOOL_TYPE_STYLUS : event.getToolType(event.getActionIndex()); + drawPointNtx( + event.getX() + intCanvasOffsetX, + event.getY() + intCanvasOffsetY + 1, + event.getPressure(), + event.getSize()); + + if (event.getAction() == MotionEvent.ACTION_UP) { + stopUpdating(); + // nDrawHelper.NDrawSwitch(false); + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (isEraser) eraseDraw(); } - private boolean hasToolStylus(MotionEvent event) { - for (int i = 0; i < event.getPointerCount(); i++) { - if (event.getToolType(i) == MotionEvent.TOOL_TYPE_STYLUS) { - return true; + return true; + } + + void startUpdating() { + setFullRefreshable(false); + mHandler.removeCallbacks(runPostInvalidate); + mStartFlag = true; + mCurrDrawRect = new Rect(0, 0, 0, 0); + } + + void stopUpdating() { + setFullRefreshable(true); + mStartFlag = false; + + mFirstPointFlag = true; + synchronized (mDrawLock) { + mCurrentUpdateRect.set(NOTSET, NOTSET, NOTSET, NOTSET); + } + int mode = MODE_APPNDRAWSTROKESYNC | android.view.View.EINK_WAVEFORM_MODE_GC16; + Log.d(TAG, "app invalidate() " + Integer.toHexString(mode)); + invalidate(mode); + } + + private void drawPointNtx(float x, float y, float pressure, float size) { + + int x_int = 0; + int y_int = 0; + + x_int = (int) x; + y_int = (int) y; + + currDrawRectMove((int) x, (int) y); + + if (mCurX != x_int || mCurY != y_int) { + if (mFinishUpdateFlag) { + mFinishUpdateFlag = false; + } + /*if (isEnablePenPressure) { + mPaint.setStrokeWidth(((float) max_pressure) * pressure); + //nDrawHelper.NDrawSetStrokeWidth((int) (((float) max_pressure) * pressure)); + }*/ + updatePenStroke(pressure); + mCurX = x_int; + mCurY = y_int; + if (mCurY > intCanvasHeight) { + mCurY = intCanvasHeight; + } + if (mCurY < 0) { + mCurY = 0; + isOutSide = true; + return; + } + if (isOutSide == true) { + isOutSide = false; + stopUpdating(); + } + if (mCurX > intCanvasWidth) { + mCurX = intCanvasWidth; + } + if (mCurX < 0) { + mCurX = 0; + } + if (isEnablePenPressure) { + mCurWidth = (int) ((((float) max_pressure) * pressure) / 2.0f); + } else { + mCurWidth = getPenThickness() / 2; + } + if (mCurWidth < 1) { + mCurWidth = 1; + } + + if (mCanvas != null) { + int newleft = mCurX - mCurWidth; + int newtop = mCurY - mCurWidth; + int newright = mCurX + mCurWidth; + int newbottom = mCurY + mCurWidth; + synchronized (mDrawLock) { + if (mFirstPointFlag) { + mFirstPointFlag = false; + mCurrentUpdateRect.set(newleft, newtop, newright, newbottom); + } else { + mCurrentUpdateRect.union(newleft, newtop, newright, newbottom); + + if (false == isEraser) { + mCanvas.drawLine((float) mOldX, (float) mOldY, (float) mCurX, (float) mCurY, mPaint); + + if (getPenType() == PenType.PEN.ordinal()) { + tPath.add( + new TouchPath( + (int) (fLinewidth * pressure), + mPaint.getColor(), + mOldX, + mOldY, + mCurX, + mCurY, + mCurrDrawRect)); + + } else if (getPenType() == PenType.CHINESE_BRUSH.ordinal()) { + tPath.add( + new TouchPath( + (int) (float) (max_pressure * Math.pow(pressure, 3)), + mPaint.getColor(), + mOldX, + mOldY, + mCurX, + mCurY, + mCurrDrawRect)); + + } else { + // PenType.PENCIL.ordinal() + tPath.add( + new TouchPath( + fLinewidth, mPaint.getColor(), mOldX, mOldY, mCurX, mCurY, mCurrDrawRect)); + } } + } + mOldX = mCurX; + mOldY = mCurY; + mCurrentUpdateEndPointRegion.set(newleft, newtop, newright, newbottom); } - return false; + } + } + } + + private Handler mHandler = new Handler(); + + public synchronized void clear() { + if (mCanvas != null) { + tPath.clear(); + + mPath.reset(); + drawBackground(); + mHandler.removeCallbacks(runClear); + mHandler.postDelayed(runClear, 1000); + } + } + + private Runnable runPostInvalidate = + new Runnable() { + @Override + public void run() { + postInvalidate(); + } + }; + + private Runnable runClear = + new Runnable() { + @Override + public void run() { + + mFirstPointFlag = true; + fullRefresh(NtxView.this); + } + }; + + public int FileOutput(String output_path) { + + Bitmap bmpBackground = null; + + if (strBackgroundPath.equals("")) { + if (mBitmap == null) return -1; // retrun -1 ==> fail; return 0 ==> success; + + bmpBackground = + Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888); + } else { + bmpBackground = + Bitmap.createScaledBitmap( + BitmapFactory.decodeFile(strBackgroundPath), + mBitmap.getWidth(), + mBitmap.getHeight(), + true); } - private void eraseDraw() { - if (isEraser && tPath.size() > 0 && isRectEmpty(mCurrDrawRect)) { - TouchPath tp; - boolean eraseStatus = false; - for (int i2 = 0; i2 < 2; i2++) { - for (int i1 = tPath.size() - 1; i1 >= 0; i1--) { - tp = (TouchPath) tPath.get(i1); - Rect rect = tp.mRect; - if (isRectEmpty(rect) && Rect.intersects(mCurrDrawRect, rect)) { - if (i2 == 1) { - tPath.remove(tp); - } else { - eraseRect(rect); - } - eraseStatus = true; - } - } - if (!eraseStatus) { - break; - } - } - if (eraseStatus) { - int currStrokeWidth = fLinewidth; - int currColor = mPaint.getColor(); - drawBackground(); - - Iterator it = tPath.iterator(); - while (it.hasNext()) { - tp = (TouchPath) it.next(); - mCurrDrawPaint.setStrokeWidth((float) tp.mStrokeWidth); - mCurrDrawPaint.setColor(tp.mColor); - nDrawHelper.NDrawSetStrokeWidth(tp.mStrokeWidth); - nDrawHelper.NDrawSetPaintColor(tp.mColor); - mCanvas.drawLine((float) tp.mOldX, (float) tp.mOldY, (float) tp.mCurX, (float) tp.mCurY, mCurrDrawPaint); - } - nDrawHelper.NDrawSetStrokeWidth(currStrokeWidth); - nDrawHelper.NDrawSetPaintColor(currColor); - - mCurrDrawPaint.setStrokeWidth((float) currStrokeWidth); - mCurrDrawPaint.setColor(currColor); - invalidate(); - } - } + if (mBitmap != null) { + // Create new canvas to merge paint and background + bmpBackground = combineBitmap(bmpBackground, mBitmap); } - private boolean isRectEmpty(Rect rect) { - if ((rect.right - rect.left) + 1 <= 0 || (rect.bottom - rect.top) + 1 <= 0) { - return false; - } + try { + FileOutputStream fos = new FileOutputStream(output_path); + bmpBackground.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.close(); + return 0; + } catch (IOException e) { + e.printStackTrace(); + return -1; + } + } + + /** + * @param background + * @param foreground + * @return Bitmap + */ + public static Bitmap combineBitmap(Bitmap background, Bitmap foreground) { + if (background == null) { + return null; + } + int bgWidth = background.getWidth(); + int bgHeight = background.getHeight(); + int fgWidth = foreground.getWidth(); + int fgHeight = foreground.getHeight(); + Bitmap newmap = Bitmap.createBitmap(bgWidth, bgHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(newmap); + canvas.drawBitmap(background, 0, 0, null); + canvas.drawBitmap(foreground, (bgWidth - fgWidth) / 2, (bgHeight - fgHeight) / 2, null); + canvas.save(Canvas.ALL_SAVE_FLAG); + canvas.restore(); + return newmap; + } + + public int getPenThickness() { + return fLinewidth; + } + + public void setPenThickness(int i) { + fLinewidth = i; + mPaint.setStrokeWidth(fLinewidth); + nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(NOTSET); + nDrawHelper.NDrawSetStrokeWidth(fLinewidth); + isEnablePenPressure = false; + return; + } + + private void updatePenStroke(float pressure) { + + if (mPenType == PenType.PENCIL.ordinal()) { + mPaint.setStrokeWidth(fLinewidth); + nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(NOTSET); + nDrawHelper.NDrawSetStrokeWidth(fLinewidth); + } else if (mPenType == PenType.PEN.ordinal()) { + isEnablePenPressure = true; + mPaint.setStrokeWidth(fLinewidth * pressure); + nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(fLinewidth); + + } else if (mPenType == PenType.CHINESE_BRUSH.ordinal()) { + isEnablePenPressure = true; + mPaint.setStrokeWidth((float) (max_pressure * Math.pow(pressure, 3))); + nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(max_pressure); + } + } + + public boolean getPenThicknessWithPressure() { + return isEnablePenPressure; + } + + public void setPenThicknessWithPressure(boolean enable_pen_pressure) { + nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(max_pressure); + isEnablePenPressure = enable_pen_pressure; + return; + } + + public void setFullRefreshable(boolean fullRefreshable) { + isFullRefreshable = fullRefreshable; + } + + public boolean getFullRefreshable() { + return isFullRefreshable; + } + + public void setFingerTouch(boolean checked) { + isFingerTouch = checked; + } + + private int intCanvasStyle = 1; + + public void setCanvasStyle(int i) { + if (i == 1) { + intCanvasStyle = 1; + mPaint.setColor(Color.BLACK); + mCanvas.drawColor(Color.WHITE); + nDrawHelper.NDrawSetPaintColor(Color.BLACK); + } else if (i == 2) { + intCanvasStyle = 2; + mPaint.setColor(Color.WHITE); + mCanvas.drawColor(Color.BLACK); + nDrawHelper.NDrawSetPaintColor(Color.WHITE); + } + mPaint.setXfermode(null); + setPenThickness(fLinewidth); + } + + public void setRefreshMode(int refresh_mode) { + refreshMode = refresh_mode; + nDrawHelper.NDrawSetUpdateMode(refresh_mode); + } + + public int getRefreshMode() { + return refreshMode; + } + + public void setPenType(int type) { + mPenType = type; + nDrawHelper.NDrawSetPenType(type); + } + + public int getPenType() { + // Log.d(TAG, "getPenType: " + mPenType); + return mPenType; + } + + public void setBackgroundPath(String background_path) { + strBackgroundPath = background_path; + + if (new File(background_path).exists()) { + tPath.clear(); + drawBackground(); + invalidate(); + } + } + + public void onPause() { + nDrawHelper.NDrawSwitch(false); + } + + public void onResume() { + nDrawHelper.NDrawSwitch(true); + } + + public void drawBackground() { + if (intCanvasStyle == 1) { + mBitmap.eraseColor(Color.WHITE); + } else if (intCanvasStyle == 2) { + mBitmap.eraseColor(Color.BLACK); + } + + if (new File(strBackgroundPath).exists()) { + mBitmap.eraseColor(Color.TRANSPARENT); + setBackgroundDrawable(new BitmapDrawable(strBackgroundPath)); + } + } + + private int getToolType(MotionEvent event) { + return hasToolStylus(event) + ? MotionEvent.TOOL_TYPE_STYLUS + : event.getToolType(event.getActionIndex()); + } + + private boolean hasToolStylus(MotionEvent event) { + for (int i = 0; i < event.getPointerCount(); i++) { + if (event.getToolType(i) == MotionEvent.TOOL_TYPE_STYLUS) { return true; + } } + return false; + } - private void eraseRect(Rect rect) { - if (mCurrDrawRect.left > rect.left) { - mCurrDrawRect.left = rect.left; + private void eraseDraw() { + if (isEraser && tPath.size() > 0 && isRectEmpty(mCurrDrawRect)) { + TouchPath tp; + boolean eraseStatus = false; + for (int i2 = 0; i2 < 2; i2++) { + for (int i1 = tPath.size() - 1; i1 >= 0; i1--) { + tp = (TouchPath) tPath.get(i1); + Rect rect = tp.mRect; + if (isRectEmpty(rect) && Rect.intersects(mCurrDrawRect, rect)) { + if (i2 == 1) { + tPath.remove(tp); + } else { + eraseRect(rect); + } + eraseStatus = true; + } } - if (mCurrDrawRect.top > rect.top) { - mCurrDrawRect.top = rect.top; + if (!eraseStatus) { + break; } - if (mCurrDrawRect.right < rect.right) { - mCurrDrawRect.right = rect.right; - } - if (mCurrDrawRect.bottom < rect.bottom) { - mCurrDrawRect.bottom = rect.bottom; + } + if (eraseStatus) { + int currStrokeWidth = fLinewidth; + int currColor = mPaint.getColor(); + drawBackground(); + + Iterator it = tPath.iterator(); + while (it.hasNext()) { + tp = (TouchPath) it.next(); + mCurrDrawPaint.setStrokeWidth((float) tp.mStrokeWidth); + mCurrDrawPaint.setColor(tp.mColor); + nDrawHelper.NDrawSetStrokeWidth(tp.mStrokeWidth); + nDrawHelper.NDrawSetPaintColor(tp.mColor); + mCanvas.drawLine( + (float) tp.mOldX, + (float) tp.mOldY, + (float) tp.mCurX, + (float) tp.mCurY, + mCurrDrawPaint); } + nDrawHelper.NDrawSetStrokeWidth(currStrokeWidth); + nDrawHelper.NDrawSetPaintColor(currColor); + + mCurrDrawPaint.setStrokeWidth((float) currStrokeWidth); + mCurrDrawPaint.setColor(currColor); + invalidate(); + } } + } - private void currDrawRectMove(int mCurX, int mCurY) { - if (mCurrDrawRect.left == 0 || mCurrDrawRect.left > mCurX) { - mCurrDrawRect.left = mCurX; - } - if (mCurrDrawRect.top == 0 || mCurrDrawRect.top > mCurY) { - mCurrDrawRect.top = mCurY; - } - if (mCurrDrawRect.right == 0 || mCurrDrawRect.right < mCurX) { - mCurrDrawRect.right = mCurX; - } - if (mCurrDrawRect.bottom == 0 || mCurrDrawRect.bottom < mCurY) { - mCurrDrawRect.bottom = mCurY; - } + private boolean isRectEmpty(Rect rect) { + if ((rect.right - rect.left) + 1 <= 0 || (rect.bottom - rect.top) + 1 <= 0) { + return false; } + return true; + } - public void fullRefresh(View view) { - view.invalidate(UPDATE_MODE_FULL_GC16); + private void eraseRect(Rect rect) { + if (mCurrDrawRect.left > rect.left) { + mCurrDrawRect.left = rect.left; } - - - public void setInputOffset(int offset_x, int offset_y) { - - nDrawHelper.NDrawSetInputOffset(offset_x, offset_y); - intCanvasOffsetX = offset_x; - intCanvasOffsetY = offset_y; - + if (mCurrDrawRect.top > rect.top) { + mCurrDrawRect.top = rect.top; } - - - class TouchPath { - public int mColor; - public int mCurX; - public int mCurY; - public int mOldX; - public int mOldY; - public Rect mRect; - public int mStrokeWidth; - - public TouchPath(int strokeWidth, int currColor, int currOldX, int currOldY, int currCurX, int currCurY, Rect currmRect) { - this.mStrokeWidth = strokeWidth; - this.mColor = currColor; - this.mRect = currmRect; - this.mOldX = currOldX; - this.mOldY = currOldY; - this.mCurX = currCurX; - this.mCurY = currCurY; - } + if (mCurrDrawRect.right < rect.right) { + mCurrDrawRect.right = rect.right; } - - public void test_setNDrawMultiActiveRegion(int w, int h) { - int Ndraw_region_left = 0; - int Ndraw_region_top = getRelativeTop(NtxView.this); - int Ndraw_region_right = Ndraw_region_left + w/3; - int Ndraw_region_bottom = Ndraw_region_top + h/3; - - int Ndraw_region_left_2 = (w * 2)/3 ; - int Ndraw_region_top_2 = getRelativeTop(NtxView.this); - int Ndraw_region_right_2 = w; - int Ndraw_region_bottom_2 = Ndraw_region_top_2 + h/3; - - int Ndraw_region_left_3 = w/3 ; - int Ndraw_region_top_3 = getRelativeTop(NtxView.this) + h/3; - int Ndraw_region_right_3 = Ndraw_region_left_3 + w/3; - int Ndraw_region_bottom_3 = Ndraw_region_top_3 + h/3; - - - int[] multi_active_region = {Ndraw_region_left, Ndraw_region_top, Ndraw_region_right, Ndraw_region_bottom, - Ndraw_region_left_2, Ndraw_region_top_2, Ndraw_region_right_2, Ndraw_region_bottom_2, - Ndraw_region_left_3, Ndraw_region_top_3, Ndraw_region_right_3, Ndraw_region_bottom_3}; - - - - Log.d(TAG, "multi_active_region: " + Arrays.toString(multi_active_region)); - //nDrawHelper.NDrawInit(multi_active_region); - nDrawHelper.NDrawSetDrawRegion(multi_active_region); - + if (mCurrDrawRect.bottom < rect.bottom) { + mCurrDrawRect.bottom = rect.bottom; } + } + + private void currDrawRectMove(int mCurX, int mCurY) { + if (mCurrDrawRect.left == 0 || mCurrDrawRect.left > mCurX) { + mCurrDrawRect.left = mCurX; + } + if (mCurrDrawRect.top == 0 || mCurrDrawRect.top > mCurY) { + mCurrDrawRect.top = mCurY; + } + if (mCurrDrawRect.right == 0 || mCurrDrawRect.right < mCurX) { + mCurrDrawRect.right = mCurX; + } + if (mCurrDrawRect.bottom == 0 || mCurrDrawRect.bottom < mCurY) { + mCurrDrawRect.bottom = mCurY; + } + } + + public void fullRefresh(View view) { + view.invalidate(UPDATE_MODE_FULL_GC16); + } + + public void setInputOffset(int offset_x, int offset_y) { + + nDrawHelper.NDrawSetInputOffset(offset_x, offset_y); + intCanvasOffsetX = offset_x; + intCanvasOffsetY = offset_y; + } + + class TouchPath { + public int mColor; + public int mCurX; + public int mCurY; + public int mOldX; + public int mOldY; + public Rect mRect; + public int mStrokeWidth; + + public TouchPath( + int strokeWidth, + int currColor, + int currOldX, + int currOldY, + int currCurX, + int currCurY, + Rect currmRect) { + this.mStrokeWidth = strokeWidth; + this.mColor = currColor; + this.mRect = currmRect; + this.mOldX = currOldX; + this.mOldY = currOldY; + this.mCurX = currCurX; + this.mCurY = currCurY; + } + } + + public void test_setNDrawMultiActiveRegion(int w, int h) { + int Ndraw_region_left = 0; + int Ndraw_region_top = getRelativeTop(NtxView.this); + int Ndraw_region_right = Ndraw_region_left + w / 3; + int Ndraw_region_bottom = Ndraw_region_top + h / 3; + + int Ndraw_region_left_2 = (w * 2) / 3; + int Ndraw_region_top_2 = getRelativeTop(NtxView.this); + int Ndraw_region_right_2 = w; + int Ndraw_region_bottom_2 = Ndraw_region_top_2 + h / 3; + + int Ndraw_region_left_3 = w / 3; + int Ndraw_region_top_3 = getRelativeTop(NtxView.this) + h / 3; + int Ndraw_region_right_3 = Ndraw_region_left_3 + w / 3; + int Ndraw_region_bottom_3 = Ndraw_region_top_3 + h / 3; + + int[] multi_active_region = { + Ndraw_region_left, + Ndraw_region_top, + Ndraw_region_right, + Ndraw_region_bottom, + Ndraw_region_left_2, + Ndraw_region_top_2, + Ndraw_region_right_2, + Ndraw_region_bottom_2, + Ndraw_region_left_3, + Ndraw_region_top_3, + Ndraw_region_right_3, + Ndraw_region_bottom_3 + }; + + Log.d(TAG, "multi_active_region: " + Arrays.toString(multi_active_region)); + // nDrawHelper.NDrawInit(multi_active_region); + nDrawHelper.NDrawSetDrawRegion(multi_active_region); + } } diff --git a/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/ReselectSpinner.java b/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/ReselectSpinner.java index 253ace5..f6e2f6c 100755 --- a/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/ReselectSpinner.java +++ b/third_party/enote-sdk/Sample Code/Painter/app/src/main/java/ntx/painter/ReselectSpinner.java @@ -4,36 +4,41 @@ import android.content.Context; import android.util.AttributeSet; import android.widget.Spinner; -public class ReselectSpinner extends Spinner{ - - public ReselectSpinner(Context context) - { super(context); } +public class ReselectSpinner extends Spinner { - public ReselectSpinner(Context context, AttributeSet attrs) - { super(context, attrs); } + public ReselectSpinner(Context context) { + super(context); + } - public ReselectSpinner(Context context, AttributeSet attrs, int defStyle) - { super(context, attrs, defStyle); } + public ReselectSpinner(Context context, AttributeSet attrs) { + super(context, attrs); + } - @Override public void - setSelection(int position, boolean animate) - { - boolean sameSelected = position == getSelectedItemPosition(); - super.setSelection(position, animate); - if (sameSelected) { - // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now - getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId()); - } + public ReselectSpinner(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void setSelection(int position, boolean animate) { + boolean sameSelected = position == getSelectedItemPosition(); + super.setSelection(position, animate); + if (sameSelected) { + // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it + // manually now + getOnItemSelectedListener() + .onItemSelected(this, getSelectedView(), position, getSelectedItemId()); } + } - @Override public void - setSelection(int position) - { - boolean sameSelected = position == getSelectedItemPosition(); - super.setSelection(position); - if (sameSelected) { - // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now - getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId()); - } + @Override + public void setSelection(int position) { + boolean sameSelected = position == getSelectedItemPosition(); + super.setSelection(position); + if (sameSelected) { + // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it + // manually now + getOnItemSelectedListener() + .onItemSelected(this, getSelectedView(), position, getSelectedItemId()); } + } } diff --git a/third_party/enote-sdk/Sample Code/nRefresh/src/ntx/demo/MainActivity.java b/third_party/enote-sdk/Sample Code/nRefresh/src/ntx/demo/MainActivity.java index 624bfab..1b2ac0c 100755 --- a/third_party/enote-sdk/Sample Code/nRefresh/src/ntx/demo/MainActivity.java +++ b/third_party/enote-sdk/Sample Code/nRefresh/src/ntx/demo/MainActivity.java @@ -1,4 +1,3 @@ - package ntx.demo; import android.app.Activity; @@ -20,600 +19,566 @@ import android.widget.Button; import android.widget.FrameLayout; import android.widget.Spinner; import android.widget.Toast; -import ntx.refresh.R; - import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; +import ntx.refresh.R; -public class MainActivity extends Activity -{ - - +public class MainActivity extends Activity { - /** - * The waveform mode of init - * - * @hide - */ - public static final int EINK_WAVEFORM_MODE_INIT = 0x00000000; + /** + * The waveform mode of init + * + * @hide + */ + public static final int EINK_WAVEFORM_MODE_INIT = 0x00000000; - /** - * The waveform mode of 2 gray levels(Grey->White/Grey->Black) - * - * @hide - */ - public static final int EINK_WAVEFORM_MODE_DU = 0x00000001; + /** + * The waveform mode of 2 gray levels(Grey->White/Grey->Black) + * + * @hide + */ + public static final int EINK_WAVEFORM_MODE_DU = 0x00000001; - /** - * The waveform mode of 16 gray levels - * - * @hide - */ - public static final int EINK_WAVEFORM_MODE_GC16 = 0x00000002; + /** + * The waveform mode of 16 gray levels + * + * @hide + */ + public static final int EINK_WAVEFORM_MODE_GC16 = 0x00000002; - /** - * The waveform mode of 4 gray levels - * - * @hide - */ - public static final int EINK_WAVEFORM_MODE_GC4 = 0x00000003; + /** + * The waveform mode of 4 gray levels + * + * @hide + */ + public static final int EINK_WAVEFORM_MODE_GC4 = 0x00000003; - /** - * The waveform mode for fast page turn - * - * @hide - */ - public static final int EINK_WAVEFORM_MODE_A2 = 0x00000005; + /** + * The waveform mode for fast page turn + * + * @hide + */ + public static final int EINK_WAVEFORM_MODE_A2 = 0x00000005; - /** - * The waveform mode for text with white background - * - * @hide - */ - public static final int EINK_WAVEFORM_MODE_GL16 = 0x00000006; + /** + * The waveform mode for text with white background + * + * @hide + */ + public static final int EINK_WAVEFORM_MODE_GL16 = 0x00000006; - /** - * The waveform mode for text with white background - * - * @hide - */ - public static final int EINK_WAVEFORM_MODE_GLR16 = 0x00000007; + /** + * The waveform mode for text with white background + * + * @hide + */ + public static final int EINK_WAVEFORM_MODE_GLR16 = 0x00000007; - /** - * The waveform mode for text and graphics with white background - * - * @hide - */ - public static final int EINK_WAVEFORM_MODE_GLD16 = 0x00000008; + /** + * The waveform mode for text and graphics with white background + * + * @hide + */ + public static final int EINK_WAVEFORM_MODE_GLD16 = 0x00000008; - /** - * The waveform mode of auto selected gray levels by painted data - * - * @hide - */ - public static final int EINK_WAVEFORM_MODE_AUTO = 0x00000004; + /** + * The waveform mode of auto selected gray levels by painted data + * + * @hide + */ + public static final int EINK_WAVEFORM_MODE_AUTO = 0x00000004; - /** - * The Mask of waveform mode - * - * @hide - */ - public static final int EINK_WAVEFORM_MODE_MASK = 0x0000000F; + /** + * The Mask of waveform mode + * + * @hide + */ + public static final int EINK_WAVEFORM_MODE_MASK = 0x0000000F; - /** - * The regional update mode - * - * @hide - */ - public static final int EINK_AUTO_MODE_REGIONAL = 0x00000000; + /** + * The regional update mode + * + * @hide + */ + public static final int EINK_AUTO_MODE_REGIONAL = 0x00000000; - /** - * The automatic update mode - * - * @hide - */ - public static final int EINK_AUTO_MODE_AUTOMATIC = 0x00000010; + /** + * The automatic update mode + * + * @hide + */ + public static final int EINK_AUTO_MODE_AUTOMATIC = 0x00000010; - /** - * The mask of the auto mode - * - * @hide - */ - public static final int EINK_AUTO_MODE_MASK = 0x00000010; + /** + * The mask of the auto mode + * + * @hide + */ + public static final int EINK_AUTO_MODE_MASK = 0x00000010; - /** - * The partial update mode in region - * - * @hide - */ - public static final int EINK_UPDATE_MODE_PARTIAL = 0x00000000; + /** + * The partial update mode in region + * + * @hide + */ + public static final int EINK_UPDATE_MODE_PARTIAL = 0x00000000; - /** - * The full update mode in region - * - * @hide - */ - public static final int EINK_UPDATE_MODE_FULL = 0x00000020; + /** + * The full update mode in region + * + * @hide + */ + public static final int EINK_UPDATE_MODE_FULL = 0x00000020; - /** - * The Mask of update mode - * - * @hide - */ - public static final int EINK_UPDATE_MODE_MASK = 0x00000020; + /** + * The Mask of update mode + * + * @hide + */ + public static final int EINK_UPDATE_MODE_MASK = 0x00000020; - /** - * The waveform mode of no wait update - * - * @hide - */ - public static final int EINK_WAIT_MODE_NOWAIT = 0x00000000; + /** + * The waveform mode of no wait update + * + * @hide + */ + public static final int EINK_WAIT_MODE_NOWAIT = 0x00000000; - /** - * The waveform mode of wait update - * - * @hide - */ - public static final int EINK_WAIT_MODE_WAIT = 0x00000040; + /** + * The waveform mode of wait update + * + * @hide + */ + public static final int EINK_WAIT_MODE_WAIT = 0x00000040; - /** - * The Mask of waveform mode - * - * @hide - */ - public static final int EINK_WAIT_MODE_MASK = 0x00000040; + /** + * The Mask of waveform mode + * + * @hide + */ + public static final int EINK_WAIT_MODE_MASK = 0x00000040; - /** - * The waveform mode of nocombination - * - * @hide - */ - public static final int EINK_COMBINE_MODE_NOCOMBINE = 0x00000000; + /** + * The waveform mode of nocombination + * + * @hide + */ + public static final int EINK_COMBINE_MODE_NOCOMBINE = 0x00000000; - /** - * The waveform mode of combine(inactive) - * - * @hide - */ - public static final int EINK_COMBINE_MODE_COMBINE = 0x00000080; + /** + * The waveform mode of combine(inactive) + * + * @hide + */ + public static final int EINK_COMBINE_MODE_COMBINE = 0x00000080; - /** - * The Mask of combination mode - * - * @hide - */ - public static final int EINK_COMBINE_MODE_MASK = 0x00000080; + /** + * The Mask of combination mode + * + * @hide + */ + public static final int EINK_COMBINE_MODE_MASK = 0x00000080; - /** - * The waveform mode of no dithering - * - * @hide - */ - public static final int EINK_DITHER_MODE_NODITHER = 0x00000000; + /** + * The waveform mode of no dithering + * + * @hide + */ + public static final int EINK_DITHER_MODE_NODITHER = 0x00000000; - /** - * The waveform mode of dithering - * - * The dithering processing will be done to enhance the gray-tone of graphics. The target color depth is selected in - * dither_color ( Y4 or Y1 ). Since auto waveform is selected before dithering, the dithering flag will not affect - * the result of auto waveform selection. - * - * @hide - */ - public static final int EINK_DITHER_MODE_DITHER = 0x00000100; + /** + * The waveform mode of dithering + * + *

The dithering processing will be done to enhance the gray-tone of graphics. The target color + * depth is selected in dither_color ( Y4 or Y1 ). Since auto waveform is selected before + * dithering, the dithering flag will not affect the result of auto waveform selection. + * + * @hide + */ + public static final int EINK_DITHER_MODE_DITHER = 0x00000100; - /** - * The Mask of dithering mode - * - * @hide - */ - public static final int EINK_DITHER_MODE_MASK = 0x00000100; + /** + * The Mask of dithering mode + * + * @hide + */ + public static final int EINK_DITHER_MODE_MASK = 0x00000100; - /** - * The waveform mode of no invert - * - * @hide - */ - public static final int EINK_INVERT_MODE_NOINVERT = 0x00000000; + /** + * The waveform mode of no invert + * + * @hide + */ + public static final int EINK_INVERT_MODE_NOINVERT = 0x00000000; - /** - * The waveform mode of invert - * - * @hide - */ - public static final int EINK_INVERT_MODE_INVERT = 0x00000200; + /** + * The waveform mode of invert + * + * @hide + */ + public static final int EINK_INVERT_MODE_INVERT = 0x00000200; - /** - * The Mask of invert mode - * - * @hide - */ - public static final int EINK_INVERT_MODE_MASK = 0x00000200; + /** + * The Mask of invert mode + * + * @hide + */ + public static final int EINK_INVERT_MODE_MASK = 0x00000200; - /** - * The waveform mode of no converting - * - * @hide - */ - public static final int EINK_CONVERT_MODE_NOCONVERT = 0x00000000; + /** + * The waveform mode of no converting + * + * @hide + */ + public static final int EINK_CONVERT_MODE_NOCONVERT = 0x00000000; - /** - * The waveform mode of convert - * - * @hide - */ - public static final int EINK_CONVERT_MODE_CONVERT = 0x00000400; + /** + * The waveform mode of convert + * + * @hide + */ + public static final int EINK_CONVERT_MODE_CONVERT = 0x00000400; - /** - * The Mask of converting mode - * - * @hide - */ - public static final int EINK_CONVERT_MODE_MASK = 0x00000400; + /** + * The Mask of converting mode + * + * @hide + */ + public static final int EINK_CONVERT_MODE_MASK = 0x00000400; - /** - * The waveform mode of using reagl algo - * - * @hide - */ - public static final int EINK_REAGL_MODE_REAGL = 0x00000000; + /** + * The waveform mode of using reagl algo + * + * @hide + */ + public static final int EINK_REAGL_MODE_REAGL = 0x00000000; - /** - * The waveform mode of using reagl-D algo - * - * @hide - */ - public static final int EINK_REAGL_MODE_REAGLD = 0x00001000; + /** + * The waveform mode of using reagl-D algo + * + * @hide + */ + public static final int EINK_REAGL_MODE_REAGLD = 0x00001000; - /** - * The Mask of reagl algo - * - * @hide - */ - public static final int EINK_REAGL_MODE_MASK = 0x00001000; + /** + * The Mask of reagl algo + * + * @hide + */ + public static final int EINK_REAGL_MODE_MASK = 0x00001000; - /** - * The waveform mode of no monochrome - * - * @hide - */ - public static final int EINK_MONOCHROME_MODE_NOMONOCHROME = 0x00000000; + /** + * The waveform mode of no monochrome + * + * @hide + */ + public static final int EINK_MONOCHROME_MODE_NOMONOCHROME = 0x00000000; - /** - * The waveform mode of monochrome - * - * @hide - */ - public static final int EINK_MONOCHROME_MODE_MONOCHROME = 0x00000800; + /** + * The waveform mode of monochrome + * + * @hide + */ + public static final int EINK_MONOCHROME_MODE_MONOCHROME = 0x00000800; - /** - * The Mask of monochrome mode - * - * @hide - */ - public static final int EINK_MONOCHROME_MODE_MASK = 0x00000800; + /** + * The Mask of monochrome mode + * + * @hide + */ + public static final int EINK_MONOCHROME_MODE_MASK = 0x00000800; + /** @hide */ + // public static final int UI_DEFAULT_MODE = + // EINK_CONVERT_MODE_NOCONVERT|EINK_INVERT_MODE_NOINVERT|EINK_DITHER_MODE_NODITHER|EINK_COMBINE_MODE_NOCOMBINE| + // EINK_WAIT_MODE_NOWAIT|EINK_UPDATE_MODE_PARTIAL|EINK_AUTO_MODE_REGIONAL|EINK_WAVEFORM_MODE_AUTO; + public static final int UI_DEFAULT_MODE = 0x02; - /** @hide */ - //public static final int UI_DEFAULT_MODE = EINK_CONVERT_MODE_NOCONVERT|EINK_INVERT_MODE_NOINVERT|EINK_DITHER_MODE_NODITHER|EINK_COMBINE_MODE_NOCOMBINE| - //EINK_WAIT_MODE_NOWAIT|EINK_UPDATE_MODE_PARTIAL|EINK_AUTO_MODE_REGIONAL|EINK_WAVEFORM_MODE_AUTO; - public static final int UI_DEFAULT_MODE = 0x02; + /** @hide */ + public static final int UI_FULLREFRESH_MODE = + EINK_CONVERT_MODE_NOCONVERT + | EINK_INVERT_MODE_NOINVERT + | EINK_DITHER_MODE_NODITHER + | EINK_COMBINE_MODE_NOCOMBINE + | EINK_WAIT_MODE_NOWAIT + | EINK_UPDATE_MODE_FULL + | EINK_AUTO_MODE_REGIONAL + | EINK_WAVEFORM_MODE_GC16; - /** @hide */ - public static final int UI_FULLREFRESH_MODE = EINK_CONVERT_MODE_NOCONVERT - | EINK_INVERT_MODE_NOINVERT - | EINK_DITHER_MODE_NODITHER - | EINK_COMBINE_MODE_NOCOMBINE - | EINK_WAIT_MODE_NOWAIT | EINK_UPDATE_MODE_FULL - | EINK_AUTO_MODE_REGIONAL - | EINK_WAVEFORM_MODE_GC16; + /** @hide */ + public static final int UI_MONOCHROME_MODE = + EINK_MONOCHROME_MODE_MONOCHROME + | EINK_CONVERT_MODE_NOCONVERT + | EINK_INVERT_MODE_NOINVERT + | EINK_DITHER_MODE_NODITHER + | EINK_COMBINE_MODE_NOCOMBINE + | EINK_WAIT_MODE_NOWAIT + | EINK_UPDATE_MODE_PARTIAL + | EINK_AUTO_MODE_REGIONAL + | EINK_WAVEFORM_MODE_GC16; - /** @hide */ - public static final int UI_MONOCHROME_MODE = EINK_MONOCHROME_MODE_MONOCHROME - | EINK_CONVERT_MODE_NOCONVERT - | EINK_INVERT_MODE_NOINVERT - | EINK_DITHER_MODE_NODITHER - | EINK_COMBINE_MODE_NOCOMBINE - | EINK_WAIT_MODE_NOWAIT - | EINK_UPDATE_MODE_PARTIAL - | EINK_AUTO_MODE_REGIONAL - | EINK_WAVEFORM_MODE_GC16; + private Spinner spinner1, spinner2, spinner3, spinner4, spinner5; + private Button btnApply, btnFullRefresh; - private Spinner spinner1, spinner2, spinner3, spinner4, spinner5; - private Button btnApply, btnFullRefresh; - - - private static final String TAG = "MainActivity"; + private static final String TAG = "MainActivity"; - private Activity mActivity = null; + private Activity mActivity = null; - private Context mContext; + private Context mContext; - private NtxImageViewClass iv; + private NtxImageViewClass iv; - private FrameLayout mFrameLayout; + private FrameLayout mFrameLayout; - PowerManager mPM; - - private int einkMode; - + PowerManager mPM; - @Override - public void onCreate(Bundle paramBundle) { - super.onCreate(paramBundle); - mActivity = this; - mContext = getApplicationContext(); + private int einkMode; - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); + @Override + public void onCreate(Bundle paramBundle) { + super.onCreate(paramBundle); + mActivity = this; + mContext = getApplicationContext(); - setContentView(R.layout.main); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow() + .setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - mFrameLayout = (FrameLayout) findViewById(R.id.show_img_framelayout); - iv = new NtxImageViewClass(getApplicationContext()); - mFrameLayout.addView(iv); + setContentView(R.layout.main); - mPM = (PowerManager) mActivity.getSystemService(Context.POWER_SERVICE); + mFrameLayout = (FrameLayout) findViewById(R.id.show_img_framelayout); + iv = new NtxImageViewClass(getApplicationContext()); + mFrameLayout.addView(iv); - setResImages(); - iv.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - setResImages(); - } + mPM = (PowerManager) mActivity.getSystemService(Context.POWER_SERVICE); + + setResImages(); + iv.setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View v) { + setResImages(); + } }); - - - initUI(); - - - - } - - - public void initUI() { - spinner1 = (Spinner) findViewById(R.id.spinner1); - spinner2 = (Spinner) findViewById(R.id.spinner2); - spinner3 = (Spinner) findViewById(R.id.spinner3); - spinner4 = (Spinner) findViewById(R.id.spinner4); - spinner5 = (Spinner) findViewById(R.id.spinner5); - btnApply=(Button) findViewById(R.id.btnApply); - btnFullRefresh=(Button) findViewById(R.id.btnFullRefresh); - - - // set spinner default value - String defaultitem1 = "GC16"; - String defaultitem2 = "FULL"; - ArrayAdapter myAdap = (ArrayAdapter) spinner1.getAdapter(); - int spinnerPosition = myAdap.getPosition(defaultitem1); - spinner1.setSelection(spinnerPosition); - - myAdap = (ArrayAdapter) spinner2.getAdapter(); - spinnerPosition = myAdap.getPosition(defaultitem2); - spinner2.setSelection(spinnerPosition); - + initUI(); + } - btnApply.setOnClickListener(new OnClickListener() { + public void initUI() { - @Override - public void onClick(View v) { + spinner1 = (Spinner) findViewById(R.id.spinner1); + spinner2 = (Spinner) findViewById(R.id.spinner2); + spinner3 = (Spinner) findViewById(R.id.spinner3); + spinner4 = (Spinner) findViewById(R.id.spinner4); + spinner5 = (Spinner) findViewById(R.id.spinner5); + btnApply = (Button) findViewById(R.id.btnApply); + btnFullRefresh = (Button) findViewById(R.id.btnFullRefresh); + // set spinner default value + String defaultitem1 = "GC16"; + String defaultitem2 = "FULL"; + ArrayAdapter myAdap = (ArrayAdapter) spinner1.getAdapter(); + int spinnerPosition = myAdap.getPosition(defaultitem1); + spinner1.setSelection(spinnerPosition); - applyUpdateParams(); - - Toast toast = Toast.makeText(getApplicationContext(), "Applied Successfully", Toast.LENGTH_SHORT); - toast.setGravity(Gravity.TOP, 0, 0); - toast.show(); - - - - } + myAdap = (ArrayAdapter) spinner2.getAdapter(); + spinnerPosition = myAdap.getPosition(defaultitem2); + spinner2.setSelection(spinnerPosition); - }); - - btnFullRefresh.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View arg0) { + btnApply.setOnClickListener( + new OnClickListener() { - doGC16FullRefresh(); - - } - }); - - - applyUpdateParams(); - - - } - - - public void applyUpdateParams() { - + @Override + public void onClick(View v) { + applyUpdateParams(); - String waveformStr = String.valueOf(spinner1.getSelectedItem()); - String updateStr = String.valueOf(spinner2.getSelectedItem()); - String waitStr = String.valueOf(spinner3.getSelectedItem()); - String monochromeStr = String.valueOf(spinner4.getSelectedItem()); - String hardwareDitherStr = String.valueOf(spinner5.getSelectedItem()); - + Toast toast = + Toast.makeText(getApplicationContext(), "Applied Successfully", Toast.LENGTH_SHORT); + toast.setGravity(Gravity.TOP, 0, 0); + toast.show(); + } + }); - int waveform = 0; - if (waveformStr.equalsIgnoreCase("DU")) { - waveform = EINK_WAVEFORM_MODE_DU; - } else if (waveformStr.equalsIgnoreCase("GC16")) { - waveform = EINK_WAVEFORM_MODE_GC16; - } else if (waveformStr.equalsIgnoreCase("GC4")) { - waveform = EINK_WAVEFORM_MODE_GC4; - } else if (waveformStr.equalsIgnoreCase("A2")) { - waveform = EINK_WAVEFORM_MODE_A2; - } else if (waveformStr.equalsIgnoreCase("GL16")) { - waveform = EINK_WAVEFORM_MODE_GL16; - } else if (waveformStr.equalsIgnoreCase("GLR16")) { - waveform = EINK_WAVEFORM_MODE_GLR16; - } else if (waveformStr.equalsIgnoreCase("GLD16")) { - waveform = EINK_WAVEFORM_MODE_GLD16; - } + btnFullRefresh.setOnClickListener( + new OnClickListener() { - int update = 0; - if (updateStr.equalsIgnoreCase("Patrial")) { - update = EINK_UPDATE_MODE_PARTIAL; - } else if (updateStr.equalsIgnoreCase("FULL")) { - update = EINK_UPDATE_MODE_FULL; - } + @Override + public void onClick(View arg0) { - int wait = 0; - if (waitStr.equalsIgnoreCase("NOWAIT")) { - wait = EINK_WAIT_MODE_NOWAIT; - } else if (waitStr.equalsIgnoreCase("WAIT")) { - wait = EINK_WAIT_MODE_WAIT; - } + doGC16FullRefresh(); + } + }); - int monochrome = 0; - if (monochromeStr.equalsIgnoreCase("NOMONOCHROME")) { - monochrome = EINK_MONOCHROME_MODE_NOMONOCHROME; - } else if (monochromeStr.equalsIgnoreCase("MONOCHROME")) { - monochrome = EINK_MONOCHROME_MODE_MONOCHROME; - } - - int hardware_dither = 0; - if(hardwareDitherStr.equals("NONE")){ - hardware_dither = EINK_DITHER_MODE_NODITHER; - } else if(hardwareDitherStr.equals("DITHER")){ - hardware_dither = EINK_DITHER_MODE_DITHER; - } - + applyUpdateParams(); + } + public void applyUpdateParams() { - einkMode = waveform | update | wait | monochrome | hardware_dither; - - iv.setUPDATE_MODE_SCREEN(einkMode); - - - } - - - public void doGC16FullRefresh(){ + String waveformStr = String.valueOf(spinner1.getSelectedItem()); + String updateStr = String.valueOf(spinner2.getSelectedItem()); + String waitStr = String.valueOf(spinner3.getSelectedItem()); + String monochromeStr = String.valueOf(spinner4.getSelectedItem()); + String hardwareDitherStr = String.valueOf(spinner5.getSelectedItem()); - Intent fullRefreshIntent = new Intent("ntx.eink_control.QUICK_REFRESH"); - fullRefreshIntent.putExtra("updatemode",NtxImageViewClass.UPDATE_MODE_FULL_GC16); - - sendBroadcast(fullRefreshIntent); - - - } - - - - @Override - public void onResume() { - super.onResume(); - setWakeLock(mContext); - PowerEnhanceSet(1); + int waveform = 0; + if (waveformStr.equalsIgnoreCase("DU")) { + waveform = EINK_WAVEFORM_MODE_DU; + } else if (waveformStr.equalsIgnoreCase("GC16")) { + waveform = EINK_WAVEFORM_MODE_GC16; + } else if (waveformStr.equalsIgnoreCase("GC4")) { + waveform = EINK_WAVEFORM_MODE_GC4; + } else if (waveformStr.equalsIgnoreCase("A2")) { + waveform = EINK_WAVEFORM_MODE_A2; + } else if (waveformStr.equalsIgnoreCase("GL16")) { + waveform = EINK_WAVEFORM_MODE_GL16; + } else if (waveformStr.equalsIgnoreCase("GLR16")) { + waveform = EINK_WAVEFORM_MODE_GLR16; + } else if (waveformStr.equalsIgnoreCase("GLD16")) { + waveform = EINK_WAVEFORM_MODE_GLD16; } - private static List imageValues = getImageValues(); + int update = 0; + if (updateStr.equalsIgnoreCase("Patrial")) { + update = EINK_UPDATE_MODE_PARTIAL; + } else if (updateStr.equalsIgnoreCase("FULL")) { + update = EINK_UPDATE_MODE_FULL; + } - public static List getImageValues() - { - try - { - Field[] drawableFields = R.drawable.class.getFields(); - List resourceValues = new ArrayList(); - for (Field field : drawableFields) - { - if (field.getName().indexOf("a_") != -1) - { - resourceValues.add(field.getInt(R.drawable.class)); - } - } - return resourceValues; - } catch (Exception e) - { - return null; + int wait = 0; + if (waitStr.equalsIgnoreCase("NOWAIT")) { + wait = EINK_WAIT_MODE_NOWAIT; + } else if (waitStr.equalsIgnoreCase("WAIT")) { + wait = EINK_WAIT_MODE_WAIT; + } + + int monochrome = 0; + if (monochromeStr.equalsIgnoreCase("NOMONOCHROME")) { + monochrome = EINK_MONOCHROME_MODE_NOMONOCHROME; + } else if (monochromeStr.equalsIgnoreCase("MONOCHROME")) { + monochrome = EINK_MONOCHROME_MODE_MONOCHROME; + } + + int hardware_dither = 0; + if (hardwareDitherStr.equals("NONE")) { + hardware_dither = EINK_DITHER_MODE_NODITHER; + } else if (hardwareDitherStr.equals("DITHER")) { + hardware_dither = EINK_DITHER_MODE_DITHER; + } + + einkMode = waveform | update | wait | monochrome | hardware_dither; + + iv.setUPDATE_MODE_SCREEN(einkMode); + } + + public void doGC16FullRefresh() { + + Intent fullRefreshIntent = new Intent("ntx.eink_control.QUICK_REFRESH"); + fullRefreshIntent.putExtra("updatemode", NtxImageViewClass.UPDATE_MODE_FULL_GC16); + + sendBroadcast(fullRefreshIntent); + } + + @Override + public void onResume() { + super.onResume(); + setWakeLock(mContext); + PowerEnhanceSet(1); + } + + private static List imageValues = getImageValues(); + + public static List getImageValues() { + try { + Field[] drawableFields = R.drawable.class.getFields(); + List resourceValues = new ArrayList(); + for (Field field : drawableFields) { + if (field.getName().indexOf("a_") != -1) { + resourceValues.add(field.getInt(R.drawable.class)); } + } + return resourceValues; + } catch (Exception e) { + return null; } + } - private int page_index = -1; + private int page_index = -1; - private void setResImages() { - if (imageValues != null) { - if (imageValues.size() != 0) - { - if (++page_index >= imageValues.size()) - { - page_index = 0; - } - } - } - if (page_index > -1) - { - iv.setImageDrawable(getResources().getDrawable(imageValues.get(page_index))); - } else { - iv.setImageDrawable(getResources().getDrawable(R.drawable.default_photo)); + private void setResImages() { + if (imageValues != null) { + if (imageValues.size() != 0) { + if (++page_index >= imageValues.size()) { + page_index = 0; } + } } - - private WakeLock mWakeLock = null; - - private void setWakeLock(Context context) { - if ((mWakeLock != null) && (mWakeLock.isHeld())) { - return; - } - mWakeLock = mPM.newWakeLock( - PowerManager.FULL_WAKE_LOCK - , this.getClass().getCanonicalName()); - mWakeLock.setReferenceCounted(false); - mWakeLock.acquire(); + if (page_index > -1) { + iv.setImageDrawable(getResources().getDrawable(imageValues.get(page_index))); + } else { + iv.setImageDrawable(getResources().getDrawable(R.drawable.default_photo)); } + } - public void cancelWakeLock() { - final Handler handler = new Handler(); - if ((mWakeLock != null) && (mWakeLock.isHeld())) { - new Thread(new Runnable() { + private WakeLock mWakeLock = null; + + private void setWakeLock(Context context) { + if ((mWakeLock != null) && (mWakeLock.isHeld())) { + return; + } + mWakeLock = mPM.newWakeLock(PowerManager.FULL_WAKE_LOCK, this.getClass().getCanonicalName()); + mWakeLock.setReferenceCounted(false); + mWakeLock.acquire(); + } + + public void cancelWakeLock() { + final Handler handler = new Handler(); + if ((mWakeLock != null) && (mWakeLock.isHeld())) { + new Thread( + new Runnable() { @Override public void run() { - handler.post(runnable); + handler.post(runnable); } - }).start(); - } + }) + .start(); } + } - private Runnable runnable = new Runnable() { + private Runnable runnable = + new Runnable() { @Override public void run() { - try { - mWakeLock.release(); - } catch (NullPointerException e) { - e.printStackTrace(); - } - mWakeLock = null; - } - }; - - @Override - public void onPause() { - Log.i(TAG, "onPause"); - super.onPause(); - cancelWakeLock(); - PowerEnhanceSet(0); - } - - /** - * Control the 2-Step-Suspend for Netronix eInk devices - * - * @param state 1 is enable. 0 is disable. - */ - private void PowerEnhanceSet(int state) { - Log.e("DANIEL", "PowerEnhanceSet(int state) = " + state); - try { - Settings.System.putInt(mContext.getContentResolver(), "power_enhance_enable", state); - } catch (Exception e) { + try { + mWakeLock.release(); + } catch (NullPointerException e) { e.printStackTrace(); + } + mWakeLock = null; } + }; + + @Override + public void onPause() { + Log.i(TAG, "onPause"); + super.onPause(); + cancelWakeLock(); + PowerEnhanceSet(0); + } + + /** + * Control the 2-Step-Suspend for Netronix eInk devices + * + * @param state 1 is enable. 0 is disable. + */ + private void PowerEnhanceSet(int state) { + Log.e("DANIEL", "PowerEnhanceSet(int state) = " + state); + try { + Settings.System.putInt(mContext.getContentResolver(), "power_enhance_enable", state); + } catch (Exception e) { + e.printStackTrace(); } + } } diff --git a/third_party/enote-sdk/Sample Code/nRefresh/src/ntx/demo/NtxImageViewClass.java b/third_party/enote-sdk/Sample Code/nRefresh/src/ntx/demo/NtxImageViewClass.java index bd58de1..b691514 100755 --- a/third_party/enote-sdk/Sample Code/nRefresh/src/ntx/demo/NtxImageViewClass.java +++ b/third_party/enote-sdk/Sample Code/nRefresh/src/ntx/demo/NtxImageViewClass.java @@ -1,4 +1,5 @@ package ntx.demo; + import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -7,141 +8,125 @@ import android.view.Display; import android.view.WindowManager; import android.widget.ImageView; - public class NtxImageViewClass extends ImageView { +public class NtxImageViewClass extends ImageView { - private static final String TAG = "ntxImageView"; - // private final Bitmap mScreen; - private boolean mfirsttimedraw; + private static final String TAG = "ntxImageView"; + // private final Bitmap mScreen; + private boolean mfirsttimedraw; - public static int FULL_REFRESH_COUNT = 600; + public static int FULL_REFRESH_COUNT = 600; + /* update mode for handwriting in eink */ + public static final int UPDATE_MODE_PARTIAL_DU = + android.view.View.EINK_WAVEFORM_MODE_DU | android.view.View.EINK_UPDATE_MODE_PARTIAL; - /* update mode for handwriting in eink */ - public static final int UPDATE_MODE_PARTIAL_DU = - android.view.View.EINK_WAVEFORM_MODE_DU - | android.view.View.EINK_UPDATE_MODE_PARTIAL; + public static final int UPDATE_MODE_PARTIAL_A2 = + android.view.View.EINK_WAVEFORM_MODE_A2 | android.view.View.EINK_UPDATE_MODE_PARTIAL; - public static final int UPDATE_MODE_PARTIAL_A2 = - android.view.View.EINK_WAVEFORM_MODE_A2 - | android.view.View.EINK_UPDATE_MODE_PARTIAL; + public static final int UPDATE_MODE_PARTIAL_GC4 = + android.view.View.EINK_WAVEFORM_MODE_GC4 | android.view.View.EINK_UPDATE_MODE_PARTIAL; - public static final int UPDATE_MODE_PARTIAL_GC4 = - android.view.View.EINK_WAVEFORM_MODE_GC4 - | android.view.View.EINK_UPDATE_MODE_PARTIAL; + public static final int UPDATE_MODE_PARTIAL_DU_WITH_DITHER = + android.view.View.EINK_WAVEFORM_MODE_DU + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER; - public static final int UPDATE_MODE_PARTIAL_DU_WITH_DITHER = - android.view.View.EINK_WAVEFORM_MODE_DU - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER; + public static final int UPDATE_MODE_PARTIAL_A2_WITH_DITHER = + android.view.View.EINK_WAVEFORM_MODE_A2 + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER; - public static final int UPDATE_MODE_PARTIAL_A2_WITH_DITHER = - android.view.View.EINK_WAVEFORM_MODE_A2 - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER; + public static final int UPDATE_MODE_PARTIAL_GC4_WITH_DITHER = + android.view.View.EINK_WAVEFORM_MODE_GC4 + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER; - public static final int UPDATE_MODE_PARTIAL_GC4_WITH_DITHER = - android.view.View.EINK_WAVEFORM_MODE_GC4 - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER; + public static final int UPDATE_MODE_PARTIAL_DU_WITH_MONO = + android.view.View.EINK_WAVEFORM_MODE_DU + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER + | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; - public static final int UPDATE_MODE_PARTIAL_DU_WITH_MONO = - android.view.View.EINK_WAVEFORM_MODE_DU - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER - | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; + public static final int UPDATE_MODE_PARTIAL_A2_WITH_MONO = + android.view.View.EINK_WAVEFORM_MODE_A2 + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER + | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; - public static final int UPDATE_MODE_PARTIAL_A2_WITH_MONO = - android.view.View.EINK_WAVEFORM_MODE_A2 - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER - | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; + public static final int UPDATE_MODE_PARTIAL_GC4_WITH_MONO = + android.view.View.EINK_WAVEFORM_MODE_GC4 + | android.view.View.EINK_UPDATE_MODE_PARTIAL + | android.view.View.EINK_DITHER_MODE_DITHER + | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; - public static final int UPDATE_MODE_PARTIAL_GC4_WITH_MONO = - android.view.View.EINK_WAVEFORM_MODE_GC4 - | android.view.View.EINK_UPDATE_MODE_PARTIAL - | android.view.View.EINK_DITHER_MODE_DITHER - | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; + public static final int UPDATE_MODE_PARTIAL_GC16 = + android.view.View.EINK_WAVEFORM_MODE_GC16 | android.view.View.EINK_UPDATE_MODE_PARTIAL; - public static final int UPDATE_MODE_PARTIAL_GC16 = - android.view.View.EINK_WAVEFORM_MODE_GC16 - | android.view.View.EINK_UPDATE_MODE_PARTIAL; + public static final int UPDATE_MODE_PARTIAL_AUTO = + android.view.View.EINK_WAVEFORM_MODE_AUTO | android.view.View.EINK_UPDATE_MODE_PARTIAL; - public static final int UPDATE_MODE_PARTIAL_AUTO = - android.view.View.EINK_WAVEFORM_MODE_AUTO - | android.view.View.EINK_UPDATE_MODE_PARTIAL; + public static final int UPDATE_MODE_FULL_GC16 = + android.view.View.EINK_WAVEFORM_MODE_GC16 | android.view.View.EINK_UPDATE_MODE_FULL; - public static final int UPDATE_MODE_FULL_GC16 = - android.view.View.EINK_WAVEFORM_MODE_GC16 - | android.view.View.EINK_UPDATE_MODE_FULL; + public static final int UPDATE_MODE_FULL_A2 = + android.view.View.EINK_WAVEFORM_MODE_A2 + | android.view.View.EINK_UPDATE_MODE_FULL + | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; - public static final int UPDATE_MODE_FULL_A2 = - android.view.View.EINK_WAVEFORM_MODE_A2 - | android.view.View.EINK_UPDATE_MODE_FULL - | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; + public static final int UPDATE_MODE_FULL_DU = + android.view.View.EINK_WAVEFORM_MODE_DU + | android.view.View.EINK_UPDATE_MODE_FULL + | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; - public static final int UPDATE_MODE_FULL_DU = - android.view.View.EINK_WAVEFORM_MODE_DU - | android.view.View.EINK_UPDATE_MODE_FULL - | android.view.View.EINK_MONOCHROME_MODE_MONOCHROME; + public static int UPDATE_MODE_SCREEN = UPDATE_MODE_FULL_GC16; - public static int UPDATE_MODE_SCREEN = UPDATE_MODE_FULL_GC16; + public NtxImageViewClass(Context context) { + super(context); + mContext = context; + mfirsttimedraw = false; + } - public NtxImageViewClass(Context context) { - super(context); - mContext = context; - mfirsttimedraw = false; - } + public Bitmap loadBitmap(Drawable sprite) { + return loadBitmap(sprite, Bitmap.Config.RGB_565); + } - public Bitmap loadBitmap(Drawable sprite) { - return loadBitmap(sprite, Bitmap.Config.RGB_565); - } + public Bitmap loadBitmap(Drawable sprite, Bitmap.Config bitmapConfig) { + Display d = + ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + int width = d.getWidth(); + int height = d.getHeight(); - public Bitmap loadBitmap(Drawable sprite, Bitmap.Config bitmapConfig) { - Display d = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)) - .getDefaultDisplay(); - int width = d.getWidth(); - int height = d.getHeight(); + Bitmap bitmap = Bitmap.createBitmap(width, height, bitmapConfig); + Canvas canvas = new Canvas(bitmap); + sprite.setBounds(0, 0, width, height); + sprite.draw(canvas); + return bitmap; + } - Bitmap bitmap = Bitmap.createBitmap(width, height, bitmapConfig); - Canvas canvas = new Canvas(bitmap); - sprite.setBounds(0, 0, width, height); - sprite.draw(canvas); - return bitmap; - } + @Override + public void setImageDrawable(Drawable drawable) { + invalidate(UPDATE_MODE_SCREEN); + super.setImageDrawable(drawable); + } - @Override - public void setImageDrawable(Drawable drawable) { - invalidate(UPDATE_MODE_SCREEN); - super.setImageDrawable(drawable); - } + @Override + public void setImageBitmap(Bitmap bm) { + invalidate(UPDATE_MODE_SCREEN); + super.setImageBitmap(bm); + } - @Override - public void setImageBitmap(Bitmap bm) - { - invalidate(UPDATE_MODE_SCREEN); - super.setImageBitmap(bm); - } + @Override + public void draw(Canvas canvas) { + if (mfirsttimedraw == false) { + invalidate(UPDATE_MODE_SCREEN); + mfirsttimedraw = true; + } else { + super.draw(canvas); + } + } - @Override - public void draw(Canvas canvas) { - if (mfirsttimedraw == false) { - invalidate(UPDATE_MODE_SCREEN); - mfirsttimedraw = true; - } else { - super.draw(canvas); - } - } - - - - public void setUPDATE_MODE_SCREEN(int updateMode) { - - - UPDATE_MODE_SCREEN=updateMode; - - - } - - - - } \ No newline at end of file + public void setUPDATE_MODE_SCREEN(int updateMode) { + + UPDATE_MODE_SCREEN = updateMode; + } +} diff --git a/unote/src/main/java/rip/crit/ms/unote/XiaomiEinkRenderer.java b/unote/src/main/java/rip/crit/ms/unote/XiaomiEinkRenderer.java index d65da7d..b2c725d 100644 --- a/unote/src/main/java/rip/crit/ms/unote/XiaomiEinkRenderer.java +++ b/unote/src/main/java/rip/crit/ms/unote/XiaomiEinkRenderer.java @@ -4,12 +4,6 @@ import android.graphics.Bitmap; import android.graphics.Rect; import java.lang.reflect.Method; -/** - * Xiaomi Mi Reader E-Ink transport via reverse-engineered EpdUtil surface. Uses reflection only (no - * vendor SDK link). Falls back gracefully when the system image denies the request. Symbol map: - * EpdUtil.sInitEBC (ctor), sRecycleEBC (close), sCommitImage (commit), sCommitColor (clear), - * sSetFastBgRefresh / sSetFastHandWrite (modes). - */ public final class XiaomiEinkRenderer implements Renderer { private static final String EPD_UTIL = "android.eink.EpdUtil"; private static final String EPD_MANAGER = "android.eink.EinkManager";