ok
This commit is contained in:
parent
b8c8ff98e2
commit
1e953b8132
59 changed files with 4278 additions and 3044 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -65,3 +65,6 @@ skills-lock.json
|
|||
notable/models/
|
||||
|
||||
translator/__pycache__/
|
||||
|
||||
.gradle-user/
|
||||
.tessl/
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;-><init>(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;-><init>(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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -45,6 +45,79 @@ export interface PatchConfig {
|
|||
}
|
||||
|
||||
const cjk = /[\u3400-\u9fff]/;
|
||||
const preferredTranslations: Readonly<Record<string, string>> = {
|
||||
" 搜索...": "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}.`)
|
||||
// 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) =>
|
||||
` <string name="${entry.name}"${entry.attributes}>${escapeXmlText(translations[index]!)}</string>`,
|
||||
);
|
||||
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,
|
||||
`<?xml version="1.0" encoding="utf-8"?>\n<resources>\n${strings.join("\n")}\n</resources>\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) =>
|
||||
` <string name="${entry.name}"${entry.attributes}>${escapeXmlText(response.translations[index]!)}</string>`,
|
||||
);
|
||||
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",
|
||||
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,
|
||||
);
|
||||
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
destination,
|
||||
`<?xml version="1.0" encoding="utf-8"?>\n<resources>\n${strings.join("\n")}\n</resources>\n`,
|
||||
);
|
||||
console.log(
|
||||
` -> Added ${strings.length} ${target} translations through ${config.backend ?? "argos"}`,
|
||||
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[],
|
||||
|
|
|
|||
3
jfmt.sh
3
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
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,32 +2,81 @@ 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.
|
||||
*
|
||||
* <p>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:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Active layer: {@code mEditLayerID} plus the live pointer {@code c = a.get(id)}, saved and
|
||||
* refreshed. New strokes attach to that layer.
|
||||
* <li>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.
|
||||
* </ul>
|
||||
*/
|
||||
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
|
||||
// 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) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 {
|
||||
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,
|
||||
|
|
@ -35,121 +84,197 @@ public final class LayerPanel {
|
|||
int.class,
|
||||
layerClass)
|
||||
.invoke(null, activity, page, pageId, null);
|
||||
if (config == null) return;
|
||||
popup(activity, canvas, config, layerClass);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private static void popup(Activity activity, Object canvas, Object config, 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);
|
||||
private static LinearLayout body(
|
||||
Activity activity, Object canvas, Object config, Class<?> layerClass) throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
ArrayList<Integer> visible = (ArrayList<Integer>) 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);
|
||||
body.addView(row);
|
||||
if (index > 0) body.addView(PanelDocker.hairline(activity));
|
||||
}
|
||||
|
||||
public void onStartTrackingTouch(SeekBar bar) {}
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
Button add = button(activity, "Add layer");
|
||||
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<Object> runtimes = (ArrayList<Object>) 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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) {
|
||||
}
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
private static int snapTagId(Activity activity) {
|
||||
return PanelDocker.id(activity, "moannotes_tools_fab");
|
||||
}
|
||||
|
||||
private static LinearLayout findBar(Activity activity) {
|
||||
View view = activity.findViewById(id(activity, "edit_button_bar"));
|
||||
return view instanceof LinearLayout ? (LinearLayout) view : null;
|
||||
static boolean isSnapEnabled(Object listener) {
|
||||
try {
|
||||
Method isEnabled = listener.getClass().getMethod("isEnabled");
|
||||
return Boolean.TRUE.equals(isEnabled.invoke(listener));
|
||||
} catch (Exception ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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 int id(Activity activity, String name) {
|
||||
return activity
|
||||
.getResources()
|
||||
.getIdentifier(
|
||||
name, name.startsWith("icp_") ? "drawable" : "id", activity.getPackageName());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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'"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,15 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="54dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="58dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="22dp"
|
||||
android:paddingEnd="22dp">
|
||||
android:paddingEnd="22dp"
|
||||
android:paddingTop="9dp"
|
||||
android:paddingBottom="9dp">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
<ImageView
|
||||
android:id="@id/iv_app_icon"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="34dp"
|
||||
android:contentDescription="App icon"
|
||||
android:scaleType="centerInside" />
|
||||
<TextView
|
||||
|
|
@ -22,4 +30,10 @@
|
|||
android:singleLine="true"
|
||||
android:textColor="@color/textcolor1319"
|
||||
android:textSize="17sp" />
|
||||
</LinearLayout>
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="9dp"
|
||||
android:background="#ffe0e0e0" />
|
||||
</LinearLayout>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="72dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="76dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp">
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
<ImageView
|
||||
android:id="@id/iv_app_icon"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:contentDescription="App icon"
|
||||
android:scaleType="centerInside" />
|
||||
<TextView
|
||||
|
|
@ -21,5 +29,11 @@
|
|||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="19sp" />
|
||||
android:textSize="18sp" />
|
||||
</LinearLayout>
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:background="#ffe0e0e0" />
|
||||
</LinearLayout>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
23
patches/moannotes/res/drawable/icp_check.xml
Normal file
23
patches/moannotes/res/drawable/icp_check.xml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!-- Ported from ByteDance IconPark "check-one" (@icon-park/svg 1.4.2, Apache-2.0).
|
||||
48-grid outline theme, stroke-width 4, converted path-for-path to VectorDrawable. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M24 44C29.5228 44 34.5228 41.7614 38.1421 38.1421C41.7614 34.5228 44 29.5228 44 24C44 18.4772 41.7614 13.4772 38.1421 9.85786C34.5228 6.23858 29.5228 4 24 4C18.4772 4 13.4772 6.23858 9.85786 9.85786C6.23858 13.4772 4 18.4772 4 24C4 29.5228 6.23858 34.5228 9.85786 38.1421C13.4772 41.7614 18.4772 44 24 44Z"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M16 24L22 30L34 18"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
</vector>
|
||||
24
patches/moannotes/res/drawable/icp_close.xml
Normal file
24
patches/moannotes/res/drawable/icp_close.xml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<!-- Ported from ByteDance IconPark "close" (@icon-park/svg 1.4.2, Apache-2.0).
|
||||
48-grid outline theme, stroke-width 4, converted path-for-path to VectorDrawable. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M8 8L40 40"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M8 40L40 8"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
</vector>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:fillColor="#FF1A1A1A" android:pathData="M13,3v8.2l2.6,-2.6L17,10l-5,5 -5,-5 1.4,-1.4 2.6,2.6V3h2zM5,17h14v4H5v-4z"/>
|
||||
</vector>
|
||||
48
patches/moannotes/res/drawable/icp_eye_closed.xml
Normal file
48
patches/moannotes/res/drawable/icp_eye_closed.xml
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<!-- Ported from ByteDance IconPark "preview-close" (@icon-park/svg 1.4.2, Apache-2.0).
|
||||
48-grid outline theme, stroke-width 4, converted path-for-path to VectorDrawable. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M6 16C6.63472 17.2193 7.59646 18.3504 8.82276 19.3554C12.261 22.1733 17.779 24 24 24C30.221 24 35.739 22.1733 39.1772 19.3554C40.4035 18.3504 41.3653 17.2193 42 16"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M28.9775 24L31.048 31.7274"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M37.3535 21.3536L43.0103 27.0104"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M5.00004 27.0103L10.6569 21.3534"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M16.9278 31.7276L18.9983 24.0001"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
</vector>
|
||||
22
patches/moannotes/res/drawable/icp_eye_open.xml
Normal file
22
patches/moannotes/res/drawable/icp_eye_open.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!-- Ported from ByteDance IconPark "preview-open" (@icon-park/svg 1.4.2, Apache-2.0).
|
||||
48-grid outline theme, stroke-width 4, converted path-for-path to VectorDrawable. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M24 36C35.0457 36 44 24 44 24C44 24 35.0457 12 24 12C12.9543 12 4 24 4 24C4 24 12.9543 36 24 36Z"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M24 29C26.7614 29 29 26.7614 29 24C29 21.2386 26.7614 19 24 19C21.2386 19 19 21.2386 19 24C19 26.7614 21.2386 29 24 29Z"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
</vector>
|
||||
23
patches/moannotes/res/drawable/icp_ink.xml
Normal file
23
patches/moannotes/res/drawable/icp_ink.xml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!-- Ported from ByteDance IconPark "write" (@icon-park/svg 1.4.2, Apache-2.0).
|
||||
48-grid outline theme, stroke-width 4, converted path-for-path to VectorDrawable. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M5.32497 43.4996L13.81 43.4998L44.9227 12.3871L36.4374 3.90186L5.32471 35.0146L5.32497 43.4996Z"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M27.9521 12.3872L36.4374 20.8725"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
</vector>
|
||||
|
|
@ -1 +1,32 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M5,3L15,3L20,8L20,21L5,21ZM15,4L15,9L19,9M12,11L12,17M9,14L15,14"/></vector>
|
||||
<!-- Ported from ByteDance IconPark "export" (@icon-park/svg 1.4.2, Apache-2.0).
|
||||
48-grid outline theme, stroke-width 4, converted path-for-path to VectorDrawable. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M42 27C42 33 38 43 24 43C10 43 6 33 6 27"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M24.0078 5.10059V33.0001"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M12 17L24 5L36 17"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
</vector>
|
||||
|
|
|
|||
|
|
@ -1 +1,39 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M3,5L21,5L21,7L3,7M3,11L21,11L21,13L3,13M3,17L21,17L21,19L3,19"/></vector>
|
||||
<!-- Ported from ByteDance IconPark "layers" (@icon-park/svg 1.4.2, Apache-2.0).
|
||||
48-grid outline theme, stroke-width 4, converted path-for-path to VectorDrawable. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M4 11.9143L24 19L44 11.9143L24 5L4 11.9143Z"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M4 20L24 27L44 20"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M4 28L24 35L44 28"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M4 36L24 43L44 36"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
</vector>
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:fillColor="#FF1A1A1A" android:pathData="M12,3C6.5,3 2,6.8 2,11.5S6.5,20 12,20h1.2c1.5,0 2.2,-1.8 1.1,-2.9c-0.5,-0.5 -0.2,-1.4 0.5,-1.4H16c3.3,0 6,-2.7 6,-6C22,5.9 17.5,3 12,3zM7,11.5a1.3,1.3 0,1 1,0,-2.6 1.3,1.3 0,0 1,0,2.6zM11,8.5a1.3,1.3 0,1 1,0,-2.6 1.3,1.3 0,0 1,0,2.6zM16,9.5a1.3,1.3 0,1 1,0,-2.6 1.3,1.3 0,0 1,0,2.6z"/>
|
||||
</vector>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:fillColor="#FF1A1A1A" android:pathData="M4.4,16.7L4,20l3.3,-0.4L18.6,8.3l-2.9,-2.9L4.4,16.7zM17.1,4L19,2.1c0.4,-0.4 1,-0.4 1.4,0l0.5,0.5c0.4,0.4 0.4,1 0,1.4L20,5.9L17.1,4z"/>
|
||||
</vector>
|
||||
|
|
@ -1,7 +1,32 @@
|
|||
<!-- Ported from ByteDance IconPark "magnet" (@icon-park/svg 1.4.2, Apache-2.0).
|
||||
48-grid outline theme, stroke-width 4, converted path-for-path to VectorDrawable. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#FF000000" android:pathData="M4,5h16v1H4zM4,11h16v1H4zM4,17h16v1H4zM16.2,3.1l4.7,4.7 -6.5,6.5 -3.2,0.9 0.9,-3.2zM13,13.2l1.8,-0.5 4.7,-4.7 -1.3,-1.3 -4.7,4.7z" />
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M6 10V26C6 35.9411 14.0589 44 24 44C33.9411 44 42 35.9411 42 26V10M14 10V26C14 31.5228 18.4772 36 24 36C29.5228 36 34 31.5228 34 26V10"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M14 4H6V10H14V4Z"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M34 4H42V10H34V4Z"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
</vector>
|
||||
|
|
|
|||
22
patches/moannotes/res/drawable/icp_tools.xml
Normal file
22
patches/moannotes/res/drawable/icp_tools.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<!-- Ported from ByteDance IconPark "setting-two" (@icon-park/svg 1.4.2, Apache-2.0).
|
||||
48-grid outline theme, stroke-width 4, converted path-for-path to VectorDrawable. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M18.2838 43.1713C14.9327 42.1736 11.9498 40.3213 9.58787 37.867C10.469 36.8227 11 35.4734 11 34.0001C11 30.6864 8.31371 28.0001 5 28.0001C4.79955 28.0001 4.60139 28.01 4.40599 28.0292C4.13979 26.7277 4 25.3803 4 24.0001C4 21.9095 4.32077 19.8938 4.91579 17.9995C4.94381 17.9999 4.97188 18.0001 5 18.0001C8.31371 18.0001 11 15.3138 11 12.0001C11 11.0488 10.7786 10.1493 10.3846 9.35011C12.6975 7.1995 15.5205 5.59002 18.6521 4.72314C19.6444 6.66819 21.6667 8.00013 24 8.00013C26.3333 8.00013 28.3556 6.66819 29.3479 4.72314C32.4795 5.59002 35.3025 7.1995 37.6154 9.35011C37.2214 10.1493 37 11.0488 37 12.0001C37 15.3138 39.6863 18.0001 43 18.0001C43.0281 18.0001 43.0562 17.9999 43.0842 17.9995C43.6792 19.8938 44 21.9095 44 24.0001C44 25.3803 43.8602 26.7277 43.594 28.0292C43.3986 28.01 43.2005 28.0001 43 28.0001C39.6863 28.0001 37 30.6864 37 34.0001C37 35.4734 37.531 36.8227 38.4121 37.867C36.0502 40.3213 33.0673 42.1736 29.7162 43.1713C28.9428 40.752 26.676 39.0001 24 39.0001C21.324 39.0001 19.0572 40.752 18.2838 43.1713Z"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
<path
|
||||
android:pathData="M24 31C27.866 31 31 27.866 31 24C31 20.134 27.866 17 24 17C20.134 17 17 20.134 17 24C17 27.866 20.134 31 24 31Z"
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:strokeColor="#FF1A1A1A"
|
||||
android:strokeWidth="4"
|
||||
android:strokeLineJoin="round"
|
||||
/>
|
||||
</vector>
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
<resources>
|
||||
<item name="moannotes_snap_line_button" type="id" />
|
||||
<item name="moannotes_layers_button" type="id" />
|
||||
<item name="moannotes_kra_export_button" type="id" />
|
||||
<item name="moannotes_tools_fab" type="id" />
|
||||
</resources>
|
||||
|
|
|
|||
4
patches/moannotes/res/values/moannotes_strings.xml
Normal file
4
patches/moannotes/res/values/moannotes_strings.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="college_ruled">College ruled</string>
|
||||
</resources>
|
||||
|
|
@ -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;-><init>()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
|
||||
|
|
|
|||
|
|
@ -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;-><init>(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;-><init>(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;-><init>(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;-><init>(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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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 <adb-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"}.`,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<string, string> {
|
||||
|
|
@ -32,7 +35,10 @@ function readCredentials(): Record<string, string> {
|
|||
}
|
||||
|
||||
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<string, string> {
|
|||
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}`);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
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<String, Void, Bitmap> {
|
||||
private final WeakReference<ImageView> imageViewReference;
|
||||
|
|
|
|||
|
|
@ -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,22 +25,22 @@ 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;
|
||||
|
||||
|
|
@ -77,15 +76,13 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
Collections.sort(resultPath, new SortIgnoreCase());
|
||||
|
||||
totalPage = getTotalPage();
|
||||
if (currentPage == 0)
|
||||
currentPage = 1;
|
||||
if (currentPage == 0) currentPage = 1;
|
||||
|
||||
//add into items for showing
|
||||
// add into items for showing
|
||||
updateItems();
|
||||
|
||||
fileGrid = (GridView) findViewById(R.id.filepicker_file_grid);
|
||||
|
||||
|
||||
if (display_mode == Configuration.ORIENTATION_PORTRAIT) {
|
||||
|
||||
} else {
|
||||
|
|
@ -100,9 +97,13 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
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}) {
|
||||
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) {
|
||||
|
|
@ -115,8 +116,18 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
|
||||
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() : "");
|
||||
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);
|
||||
|
|
@ -124,7 +135,8 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
coverView.setImageBitmap(null);
|
||||
}
|
||||
|
||||
if (asyncTask[position] != null && asyncTask[position].getStatus().equals(Status.RUNNING)) {
|
||||
if (asyncTask[position] != null
|
||||
&& asyncTask[position].getStatus().equals(Status.RUNNING)) {
|
||||
asyncTask[position].cancel(true);
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +149,8 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
fileGrid.setOnItemClickListener(this);
|
||||
|
||||
if (resultPath.isEmpty()) {
|
||||
Toast.makeText(ImagePickerActivity.this, R.string.image_pick_no_image, Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(ImagePickerActivity.this, R.string.image_pick_no_image, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
|
||||
setTitle(R.string.image_pick_title);
|
||||
|
|
@ -148,18 +161,17 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
|
||||
imgv_pre_page.setOnClickListener(instantKeyListener);
|
||||
imgv_next_page.setOnClickListener(instantKeyListener);
|
||||
|
||||
}
|
||||
|
||||
private OnClickListener instantKeyListener = new OnClickListener() {
|
||||
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;
|
||||
if (currentPage <= 0) currentPage = totalPage;
|
||||
|
||||
txtv_pageinfo.setText(currentPage + " of " + totalPage);
|
||||
updateItems();
|
||||
|
|
@ -167,8 +179,7 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
break;
|
||||
case R.id.imgb_next_page:
|
||||
currentPage++;
|
||||
if (currentPage > totalPage)
|
||||
currentPage = 1;
|
||||
if (currentPage > totalPage) currentPage = 1;
|
||||
|
||||
txtv_pageinfo.setText(currentPage + " of " + totalPage);
|
||||
updateItems();
|
||||
|
|
@ -189,11 +200,9 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
String path = resultPath.get(realPos);
|
||||
final File f = new File(path);
|
||||
|
||||
if (f.isFile())
|
||||
pickFile(f);
|
||||
if (f.isFile()) pickFile(f);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
|
@ -207,26 +216,25 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
finish();
|
||||
}
|
||||
|
||||
//scan all the specific type files. ex:*.note
|
||||
// scan all the specific type files. ex:*.note
|
||||
public void searchFiles(File file) {
|
||||
File[] the_Files = file.listFiles();
|
||||
|
||||
if (the_Files == null)
|
||||
return;
|
||||
if (the_Files == null) return;
|
||||
|
||||
//search all dirs and files
|
||||
// search all dirs and files
|
||||
for (File tempF : the_Files) {
|
||||
if (tempF.isDirectory()) {
|
||||
if (!tempF.isHidden())
|
||||
searchFiles(tempF);
|
||||
if (!tempF.isHidden()) searchFiles(tempF);
|
||||
} else {
|
||||
try {
|
||||
boolean isImage = false;
|
||||
//compare file. if the key is matched, return value > -1.
|
||||
// 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() : "");
|
||||
final String myExtension =
|
||||
((index > 0) ? tempF.getPath().substring(index).toLowerCase().intern() : "");
|
||||
|
||||
if (myExtension.equals(type) && tempF.getName().substring(0, 1).equals(".") == false) {
|
||||
isImage = true;
|
||||
|
|
@ -234,15 +242,16 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
}
|
||||
}
|
||||
if (isImage) {
|
||||
//add matched path into array
|
||||
// add matched path into array
|
||||
resultPath.add(tempF.getPath());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(ImagePickerActivity.this, "error - searching image files.", Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(
|
||||
ImagePickerActivity.this, "error - searching image files.", Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -252,11 +261,10 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
|
||||
// update page info
|
||||
totalPage = getTotalPage();
|
||||
if (currentPage == 0)
|
||||
currentPage = 1;
|
||||
if (currentPage == 0) currentPage = 1;
|
||||
txtv_pageinfo.setText(currentPage + " of " + totalPage);
|
||||
|
||||
if (totalPage == 1) { //only one page
|
||||
if (totalPage == 1) { // only one page
|
||||
txtv_pageinfo.setVisibility(View.INVISIBLE);
|
||||
imgv_pre_page.setVisibility(View.INVISIBLE);
|
||||
imgv_next_page.setVisibility(View.INVISIBLE);
|
||||
|
|
@ -277,19 +285,16 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
private int getTotalPage() {
|
||||
int totalpage = resultPath.size() / (numPerPage);
|
||||
|
||||
if (resultPath.size() % (numPerPage) != 0 | resultPath.size() / (numPerPage) == 0)
|
||||
totalpage++;
|
||||
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();
|
||||
if (items.size() != 0) items.clear();
|
||||
|
||||
for (int i = numPerPage * (currentPage - 1); i < numPerPage * currentPage; i++) {
|
||||
if (i >= resultPath.size())
|
||||
break;
|
||||
if (i >= resultPath.size()) break;
|
||||
|
||||
Map<String, Object> item = new HashMap<String, Object>();
|
||||
item.put("image", resultPath.get(i));
|
||||
|
|
@ -304,10 +309,12 @@ public class ImagePickerActivity extends Activity implements OnItemClickListener
|
|||
String s2 = (String) o2;
|
||||
|
||||
final int indexNameS1 = s1.lastIndexOf('/');
|
||||
final String filenameS1 = ((indexNameS1 > 1) ? s1.substring(indexNameS1).toLowerCase().intern() : "");
|
||||
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() : "");
|
||||
final String filenameS2 =
|
||||
((indexNameS2 > 1) ? s2.substring(indexNameS2).toLowerCase().intern() : "");
|
||||
|
||||
return filenameS1.toLowerCase().compareTo(filenameS2.toLowerCase());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,26 +30,20 @@ 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;
|
||||
|
|
@ -69,13 +59,18 @@ public class MainActivity extends Activity {
|
|||
protected ReselectSpinner thicknessSpinner;
|
||||
protected ReselectSpinner mPenTypeSpinner;
|
||||
private ImageButton refreshAButton, refreshButton;
|
||||
private ImageButton thicknessButton, calibrationButton, cleanButton, rotateButton, backgroundButton, saveButton, infoButton;
|
||||
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;
|
||||
|
|
@ -85,14 +80,11 @@ public class MainActivity extends Activity {
|
|||
AlertDialog dialog;
|
||||
|
||||
private int calHorizontalPosition, calVerticalPosition;
|
||||
|
||||
private int initHorizontalPosition, initVerticalPosition;
|
||||
private int isSavedInteger=0;
|
||||
|
||||
private int isSavedInteger = 0;
|
||||
|
||||
static int pen_offset_x, pen_offset_y;
|
||||
|
||||
|
||||
private int[] thicknessChoices;
|
||||
private String[] mPentypeChoices;
|
||||
|
||||
|
|
@ -161,38 +153,32 @@ public class MainActivity extends Activity {
|
|||
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]);
|
||||
if (i == thicknessChoices.length - 1) thickness_spinner_string_table[i] = (String) "Pressure";
|
||||
else thickness_spinner_string_table[i] = (String) String.valueOf(thicknessChoices[i]);
|
||||
}
|
||||
ArrayAdapter<String> mAdapter = new ArrayAdapter<String>(
|
||||
mContext,
|
||||
R.layout.toolbox_spinner_item,
|
||||
thickness_spinner_string_table);
|
||||
ArrayAdapter<String> mAdapter =
|
||||
new ArrayAdapter<String>(
|
||||
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());
|
||||
|
||||
//pen type
|
||||
// pen type
|
||||
mPentypeChoices = getResources().getStringArray(R.array.pen_type_values);
|
||||
|
||||
mAdapter = new ArrayAdapter<String>(
|
||||
mContext,
|
||||
R.layout.toolbox_spinner_item,
|
||||
mPentypeChoices);
|
||||
mAdapter = new ArrayAdapter<String>(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<String>(
|
||||
mContext,
|
||||
R.layout.toolbox_spinner_item,
|
||||
refresh_mode_spinner_string_table);
|
||||
String[] refresh_mode_spinner_string_table =
|
||||
getResources().getStringArray(R.array.toolbox_refresh_mode);
|
||||
mAdapter =
|
||||
new ArrayAdapter<String>(
|
||||
mContext, R.layout.toolbox_spinner_item, refresh_mode_spinner_string_table);
|
||||
mAdapter.setDropDownViewResource(R.layout.toolbox_spinner_item);
|
||||
refreshModeSpinner.setAdapter(mAdapter);
|
||||
isRefreshModeSpinnerInitRun = true;
|
||||
|
|
@ -204,19 +190,19 @@ public class MainActivity extends Activity {
|
|||
backgroundBlackButton.setOnClickListener(mOnClickListener);
|
||||
autoDrawButton.setOnClickListener(mOnClickListener);
|
||||
|
||||
chkbox_finger_touch.setOnClickListener(new CheckBox.OnClickListener() {
|
||||
chkbox_finger_touch.setOnClickListener(
|
||||
new CheckBox.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
myNewView.setFingerTouch(chkbox_finger_touch.isChecked());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
refreshAButton.setOnTouchListener(new View.OnTouchListener(){
|
||||
refreshAButton.setOnTouchListener(
|
||||
new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if(event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
{
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
refreshAButton.setImageResource(R.color.black);
|
||||
}
|
||||
|
||||
|
|
@ -224,23 +210,24 @@ public class MainActivity extends Activity {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
mBuilder=new AlertDialog.Builder(MainActivity.this);
|
||||
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<String> adapterHorizontal = new ArrayAdapter<String>(MainActivity.this,
|
||||
ArrayAdapter<String> adapterHorizontal =
|
||||
new ArrayAdapter<String>(
|
||||
MainActivity.this,
|
||||
android.R.layout.simple_spinner_item,
|
||||
getResources().getStringArray(R.array.horizontal_offset_list));
|
||||
ArrayAdapter<String> adapterVertical = new ArrayAdapter<String>(MainActivity.this,
|
||||
ArrayAdapter<String> adapterVertical =
|
||||
new ArrayAdapter<String>(
|
||||
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);
|
||||
|
||||
|
|
@ -250,89 +237,70 @@ public class MainActivity extends Activity {
|
|||
mHorizontalSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
|
||||
mVerticalSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
|
||||
|
||||
initHorizontalPosition = (adapterHorizontal.getCount() - 1) / 2;
|
||||
initVerticalPosition = (adapterHorizontal.getCount() - 1) / 2;
|
||||
|
||||
isSavedInteger = Settings.System.getInt(getContentResolver(), "isSavedInteger", 999);
|
||||
|
||||
initHorizontalPosition=(adapterHorizontal.getCount()-1)/2;
|
||||
initVerticalPosition=(adapterHorizontal.getCount()-1)/2;
|
||||
if (isSavedInteger == 1) {
|
||||
|
||||
isSavedInteger=Settings.System.getInt(getContentResolver(),"isSavedInteger",999);
|
||||
calHorizontalPosition =
|
||||
Settings.System.getInt(getContentResolver(), "calHorizontalPosition", 0);
|
||||
calVerticalPosition = Settings.System.getInt(getContentResolver(), "calVerticalPosition", 0);
|
||||
|
||||
if(isSavedInteger==1){
|
||||
|
||||
calHorizontalPosition=Settings.System.getInt(getContentResolver(),"calHorizontalPosition",0);
|
||||
calVerticalPosition=Settings.System.getInt(getContentResolver(),"calVerticalPosition",0);
|
||||
|
||||
|
||||
}else{
|
||||
|
||||
calHorizontalPosition=initHorizontalPosition;
|
||||
calVerticalPosition=initVerticalPosition;
|
||||
} 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);
|
||||
|
||||
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];
|
||||
|
||||
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() {
|
||||
mBuilder.setPositiveButton(
|
||||
"OK",
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
|
||||
calHorizontalPosition = mHorizontalSpinner.getSelectedItemPosition();
|
||||
calVerticalPosition = mVerticalSpinner.getSelectedItemPosition();
|
||||
|
||||
|
||||
calHorizontalPosition=mHorizontalSpinner.getSelectedItemPosition();
|
||||
calVerticalPosition=mVerticalSpinner.getSelectedItemPosition();
|
||||
|
||||
pen_offset_x=horizontal_offset_int_table[calHorizontalPosition];
|
||||
pen_offset_y=vertical_offset_int_table[calVerticalPosition];
|
||||
|
||||
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);
|
||||
|
||||
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() {
|
||||
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();
|
||||
|
|
@ -342,17 +310,15 @@ public class MainActivity extends Activity {
|
|||
mDisplay = mWindowManager.getDefaultDisplay();
|
||||
|
||||
nDrawHelper.NDrawInit(this);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
changeEinkControlPermission(false);
|
||||
Log.d(TAG,"onBackPressed");
|
||||
Log.d(TAG, "onBackPressed");
|
||||
nDrawHelper.NDrawSkia2FBUnInit();
|
||||
//myNewView.onBackPressed();
|
||||
// myNewView.onBackPressed();
|
||||
int pid = android.os.Process.myPid();
|
||||
android.os.Process.killProcess(pid);
|
||||
}
|
||||
|
|
@ -364,32 +330,31 @@ public class MainActivity extends Activity {
|
|||
myNewView.onResume();
|
||||
|
||||
mTimer = new Timer();
|
||||
mTimer.schedule(new TimerTask() {
|
||||
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
|
||||
// RefreshHandler.sendEmptyMessage(1);//Bugfix: show white region when resume from
|
||||
// suspend
|
||||
}
|
||||
}, 0, fixed_full_refresh_time);
|
||||
},
|
||||
0,
|
||||
fixed_full_refresh_time);
|
||||
PowerEnhanceSet(1);
|
||||
|
||||
|
||||
|
||||
if (refresh_flag){
|
||||
if (refresh_flag) {
|
||||
|
||||
changeToA2DitheringMode();
|
||||
|
||||
mHandler.postDelayed(runInvalidate,2000);
|
||||
mHandler.postDelayed(runInvalidate, 2000);
|
||||
|
||||
}else{
|
||||
} else {
|
||||
|
||||
changeToGlobalReset();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void changeEinkControlPermission(boolean isForNtxAppsOnly) {
|
||||
|
|
@ -398,21 +363,21 @@ public class MainActivity extends Activity {
|
|||
sendBroadcast(changePermissionIntent);
|
||||
}
|
||||
|
||||
private Handler RefreshHandler = new Handler() {
|
||||
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();
|
||||
if (myNewView.isEinkHardwareType()) myNewView.fullRefresh(mainLayout);
|
||||
else mainLayout.invalidate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private Spinner.OnTouchListener mSpinnerOnTouchListener = new Spinner.OnTouchListener() {
|
||||
private Spinner.OnTouchListener mSpinnerOnTouchListener =
|
||||
new Spinner.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
|
||||
|
|
@ -423,23 +388,22 @@ public class MainActivity extends Activity {
|
|||
}
|
||||
};
|
||||
|
||||
private Button.OnClickListener mOnClickListener = new Button.OnClickListener() {
|
||||
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
|
||||
// trigger thickness spinner
|
||||
((ReselectSpinner) findViewById(R.id.toolbox_thickness_spinner)).performClick();
|
||||
break;
|
||||
case R.id.pen_type_button:
|
||||
//trigger thickness spinner
|
||||
// trigger thickness spinner
|
||||
((ReselectSpinner) findViewById(R.id.pen_type_spinner)).performClick();
|
||||
break;
|
||||
case R.id.toolbox_calibration:
|
||||
|
||||
|
||||
dialog.show();
|
||||
|
||||
break;
|
||||
|
|
@ -452,26 +416,31 @@ public class MainActivity extends Activity {
|
|||
|
||||
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);
|
||||
}
|
||||
}).start();
|
||||
})
|
||||
.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");
|
||||
strSaveImgPath =
|
||||
Environment.getExternalStorageDirectory().getPath()
|
||||
+ "/painter"
|
||||
+ now.format("_%Y%m%d-%H%M%S.png");
|
||||
if (myNewView.FileOutput(strSaveImgPath) == 0) {
|
||||
mDialog = createDialog(
|
||||
mDialog =
|
||||
createDialog(
|
||||
MainActivity.this,
|
||||
String.format(getString(R.string.toolbox_save_msg_success), strSaveImgPath),
|
||||
new View.OnClickListener() {
|
||||
|
|
@ -482,7 +451,8 @@ public class MainActivity extends Activity {
|
|||
});
|
||||
mDialog.show();
|
||||
} else {
|
||||
mDialog = createDialog(
|
||||
mDialog =
|
||||
createDialog(
|
||||
MainActivity.this,
|
||||
String.format(getString(R.string.toolbox_save_msg_fail), strSaveImgPath),
|
||||
new View.OnClickListener() {
|
||||
|
|
@ -495,7 +465,8 @@ public class MainActivity extends Activity {
|
|||
}
|
||||
break;
|
||||
case R.id.toolbox_info:
|
||||
mDialog = createDialog(
|
||||
mDialog =
|
||||
createDialog(
|
||||
MainActivity.this,
|
||||
getString(R.string.msg_info),
|
||||
new View.OnClickListener() {
|
||||
|
|
@ -518,18 +489,16 @@ public class MainActivity extends Activity {
|
|||
break;
|
||||
case R.id.toolbox_full_refreshA:
|
||||
// refreshButton.setImageResource(R.drawable.ic_menu_reset);
|
||||
if (refresh_flag == false){
|
||||
if (refresh_flag == false) {
|
||||
|
||||
refresh_flag = true;
|
||||
|
||||
}else if (refresh_flag == true){
|
||||
} else if (refresh_flag == true) {
|
||||
|
||||
refresh_flag = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (refresh_flag){
|
||||
if (refresh_flag) {
|
||||
|
||||
refreshAButton.setImageResource(R.drawable.ic_menu_admode);
|
||||
|
||||
|
|
@ -537,72 +506,59 @@ public class MainActivity extends Activity {
|
|||
|
||||
myNewView.invalidate();
|
||||
|
||||
}else{
|
||||
} else {
|
||||
|
||||
refreshAButton.setImageResource(R.drawable.ic_menu_reset);
|
||||
|
||||
changeToGlobalReset();
|
||||
|
||||
myNewView.invalidate();
|
||||
|
||||
}
|
||||
|
||||
|
||||
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("updatemode", NtxView.UPDATE_MODE_FULL_GC16);
|
||||
ac.putExtra("commandFromNtxApp", true);
|
||||
sendBroadcast(ac);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public void changeToA2DitheringMode(){
|
||||
public void changeToA2DitheringMode() {
|
||||
|
||||
doDuRefresh();
|
||||
|
||||
Intent a = new Intent("ntx.eink_control.GLOBAL_REFRESH");
|
||||
a.putExtra("updatemode",NtxView.UPDATE_MODE_GLOBAL_FULL_A2_WITH_DITHER);
|
||||
a.putExtra("updatemode", NtxView.UPDATE_MODE_GLOBAL_FULL_A2_WITH_DITHER);
|
||||
a.putExtra("commandFromNtxApp", true);
|
||||
sendBroadcast(a);
|
||||
|
||||
}
|
||||
|
||||
private void doDuRefresh(){
|
||||
private void doDuRefresh() {
|
||||
|
||||
Intent quickDuRefreshIntent = new Intent("ntx.eink_control.QUICK_REFRESH");
|
||||
quickDuRefreshIntent.putExtra("updatemode",NtxView.UPDATE_MODE_FULL_DU_WITH_DITHER);
|
||||
quickDuRefreshIntent.putExtra("updatemode", NtxView.UPDATE_MODE_FULL_DU_WITH_DITHER);
|
||||
quickDuRefreshIntent.putExtra("commandFromNtxApp", true);
|
||||
sendBroadcast(quickDuRefreshIntent);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void changeToGlobalReset(){
|
||||
public void changeToGlobalReset() {
|
||||
|
||||
Intent al = new Intent("ntx.eink_control.GLOBAL_REFRESH");
|
||||
al.putExtra("updatemode",NtxView.UPDATE_MODE_GLOBAL_RESET);
|
||||
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);
|
||||
|
|
@ -625,7 +581,8 @@ public class MainActivity extends Activity {
|
|||
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) {
|
||||
|
|
@ -639,10 +596,10 @@ public class MainActivity extends Activity {
|
|||
setToolboxThicknessValue();
|
||||
} else {
|
||||
int thickness = thicknessChoices[position];
|
||||
//save current setting to the customized
|
||||
// save current setting to the customized
|
||||
thicknessChoices[thicknessChoices.length - 1] = thickness;
|
||||
if (thickness == myNewView.getPenThickness() && (!myNewView.getPenThicknessWithPressure()))
|
||||
return;
|
||||
if (thickness == myNewView.getPenThickness()
|
||||
&& (!myNewView.getPenThicknessWithPressure())) return;
|
||||
myNewView.setPenThickness(thickness);
|
||||
setToolboxThicknessValue(thickness);
|
||||
}
|
||||
|
|
@ -668,14 +625,12 @@ public class MainActivity extends Activity {
|
|||
}
|
||||
|
||||
@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() ) {
|
||||
// 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()) {
|
||||
|
|
@ -694,29 +649,24 @@ public class MainActivity extends Activity {
|
|||
mTimer.cancel();
|
||||
PowerEnhanceSet(0);
|
||||
|
||||
|
||||
changeToGlobalReset();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void startRotate() {
|
||||
if(getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
|
||||
if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
} else if(getRequestedOrientation() == 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){
|
||||
} else if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
|
||||
} else if(getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE){
|
||||
} else if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
} else{
|
||||
//default
|
||||
} else {
|
||||
// default
|
||||
// setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
myNewView.setCurrentRotation(getRequestedOrientation());
|
||||
myNewView.setBackgroundPath("");
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void setToolBoxRefreshModeCurrentValue() {
|
||||
|
|
@ -724,26 +674,23 @@ public class MainActivity extends Activity {
|
|||
mHandler.postDelayed(runbSetRefreshMode, 500);
|
||||
}
|
||||
|
||||
private Runnable runbSetRefreshMode = new Runnable() {
|
||||
private Runnable runbSetRefreshMode =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setRefreshModeValue(myNewView.getRefreshMode());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private Runnable runInvalidate = new Runnable() {
|
||||
private Runnable runInvalidate =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
|
||||
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]) {
|
||||
|
|
@ -770,7 +717,7 @@ public class MainActivity extends Activity {
|
|||
return;
|
||||
}
|
||||
|
||||
//update thickness button
|
||||
// update thickness button
|
||||
if ((thickness >= 9) && (thickness <= 14)) {
|
||||
thicknessButton.setImageResource(R.drawable.ic_menu_thickness_medium);
|
||||
} else if (thickness >= 15) {
|
||||
|
|
@ -779,32 +726,28 @@ public class MainActivity extends Activity {
|
|||
thicknessButton.setImageResource(R.drawable.ic_menu_thickness_small);
|
||||
}
|
||||
|
||||
//update thickness number
|
||||
// update thickness number
|
||||
setThicknessNumText(String.valueOf(thickness));
|
||||
|
||||
//sync to customized thickness
|
||||
// sync to customized thickness
|
||||
thicknessChoices[thicknessChoices.length - 1] = thickness;
|
||||
|
||||
}
|
||||
|
||||
public void setThicknessNumText(String mString) {
|
||||
txtvThicknessNum.setText(mString);
|
||||
}
|
||||
|
||||
private final static int REQUEST_CODE_PICK_IMAGE = 1;
|
||||
private static final int REQUEST_CODE_PICK_IMAGE = 1;
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode,
|
||||
Intent intent) {
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
|
||||
if (mDialog.isShowing())
|
||||
mDialog.dismiss();
|
||||
if (mDialog.isShowing()) mDialog.dismiss();
|
||||
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_PICK_IMAGE:
|
||||
if (resultCode != RESULT_OK)
|
||||
break;
|
||||
if (resultCode != RESULT_OK) break;
|
||||
Uri selectedImage = intent.getData();
|
||||
if (selectedImage != null) {
|
||||
File mFile = new File(selectedImage.getPath());
|
||||
|
|
@ -816,13 +759,10 @@ public class MainActivity extends Activity {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Control the 2-Step-Suspend for Netronix eInk devices
|
||||
*
|
||||
* @param state
|
||||
* 1 is enable.
|
||||
* 0 is disable.
|
||||
* @param state 1 is enable. 0 is disable.
|
||||
*/
|
||||
private void PowerEnhanceSet(int state) {
|
||||
Log.d(TAG, "PowerEnhanceSet = " + state);
|
||||
|
|
@ -839,5 +779,4 @@ public class MainActivity extends Activity {
|
|||
super.onConfigurationChanged(newConfig);
|
||||
Log.d(TAG, "Rotation = " + mDisplay.getRotation());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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,29 +9,23 @@ 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;
|
||||
|
|
@ -79,13 +72,12 @@ public class NtxView extends View {
|
|||
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;
|
||||
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
|
||||
|
|
@ -99,8 +91,7 @@ public class NtxView extends View {
|
|||
| 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;
|
||||
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
|
||||
|
|
@ -114,8 +105,7 @@ public class NtxView extends View {
|
|||
| 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;
|
||||
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
|
||||
|
|
@ -129,16 +119,13 @@ public class NtxView extends View {
|
|||
| 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;
|
||||
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;
|
||||
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;
|
||||
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
|
||||
|
|
@ -200,9 +187,7 @@ public class NtxView extends View {
|
|||
}
|
||||
|
||||
public static int[] pen_type_table = {
|
||||
PenType.PENCIL.ordinal(),
|
||||
PenType.PEN.ordinal(),
|
||||
PenType.CHINESE_BRUSH.ordinal(),
|
||||
PenType.PENCIL.ordinal(), PenType.PEN.ordinal(), PenType.CHINESE_BRUSH.ordinal(),
|
||||
};
|
||||
|
||||
public static boolean isEinkHardwareType() {
|
||||
|
|
@ -239,8 +224,6 @@ public class NtxView extends View {
|
|||
|
||||
this.mCurrentRotation = mCurrent_Rotation;
|
||||
nDrawHelper.NDrawSetInputRotation(mCurrentRotation);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public NtxView(Context context) {
|
||||
|
|
@ -292,29 +275,24 @@ public class NtxView extends View {
|
|||
}
|
||||
|
||||
private int getRelativeTop(View myView) {
|
||||
if (myView.getParent() == myView.getRootView())
|
||||
return myView.getTop();
|
||||
else
|
||||
return myView.getTop() + getRelativeTop((View) myView.getParent());
|
||||
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 + ")");
|
||||
// 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;
|
||||
if (curW < w) curW = w;
|
||||
if (curH < h) curH = h;
|
||||
|
||||
intCanvasHeight = curH;
|
||||
intCanvasWidth = curW;
|
||||
|
|
@ -339,16 +317,16 @@ public class NtxView extends View {
|
|||
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();
|
||||
// Log.d(TAG, "packet1: " + Arrays.toString(packet1));
|
||||
// nDrawHelper.NDrawInit();
|
||||
nDrawHelper.NDrawSetDrawRegion(packet1);
|
||||
|
||||
//test_setNDrawMultiActiveRegion(w, h);
|
||||
// test_setNDrawMultiActiveRegion(w, h);
|
||||
|
||||
}
|
||||
|
||||
private void test_NDrawDropFrames() {
|
||||
nDrawHelper.NDrawDropFrames(400000000); //400 ms
|
||||
nDrawHelper.NDrawDropFrames(400000000); // 400 ms
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -376,8 +354,7 @@ public class NtxView extends View {
|
|||
* 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}
|
||||
* @return a string describing the call stack. {@hide}
|
||||
*/
|
||||
public static String getCallers(final int depth) {
|
||||
final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
|
||||
|
|
@ -392,8 +369,7 @@ public class NtxView extends View {
|
|||
* 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}
|
||||
* @return a string describing the call stack. {@hide}
|
||||
*/
|
||||
public static String getCallers(final int start, int depth) {
|
||||
final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
|
||||
|
|
@ -437,16 +413,19 @@ public class NtxView extends View {
|
|||
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
|
||||
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
|
||||
} 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)) {
|
||||
|
|
@ -456,7 +435,7 @@ public class NtxView extends View {
|
|||
isEraser = false;
|
||||
nDrawHelper.NDrawSwitch(true);
|
||||
|
||||
//nDrawHelper.NDrawSetInputOffset(intCanvasOffsetX,intCanvasOffsetY);
|
||||
// nDrawHelper.NDrawSetInputOffset(intCanvasOffsetX,intCanvasOffsetY);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -467,23 +446,24 @@ public class NtxView extends View {
|
|||
|
||||
int historySize = event.getHistorySize();
|
||||
for (int i = 0; i < historySize; i++) {
|
||||
drawPointNtx(event.getHistoricalX(i) + intCanvasOffsetX,
|
||||
drawPointNtx(
|
||||
event.getHistoricalX(i) + intCanvasOffsetX,
|
||||
event.getHistoricalY(i) + intCanvasOffsetY + 1,
|
||||
event.getHistoricalPressure(i),
|
||||
event.getHistoricalSize(i));
|
||||
}
|
||||
|
||||
drawPointNtx(event.getX() + intCanvasOffsetX,
|
||||
drawPointNtx(
|
||||
event.getX() + intCanvasOffsetX,
|
||||
event.getY() + intCanvasOffsetY + 1,
|
||||
event.getPressure(),
|
||||
event.getSize());
|
||||
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
stopUpdating();
|
||||
//nDrawHelper.NDrawSwitch(false);
|
||||
// nDrawHelper.NDrawSwitch(false);
|
||||
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
|
||||
if (isEraser)
|
||||
eraseDraw();
|
||||
if (isEraser) eraseDraw();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -494,7 +474,6 @@ public class NtxView extends View {
|
|||
mHandler.removeCallbacks(runPostInvalidate);
|
||||
mStartFlag = true;
|
||||
mCurrDrawRect = new Rect(0, 0, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
void stopUpdating() {
|
||||
|
|
@ -573,17 +552,33 @@ public class NtxView extends View {
|
|||
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));
|
||||
|
||||
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 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{
|
||||
} else {
|
||||
// PenType.PENCIL.ordinal()
|
||||
tPath.add(new TouchPath(fLinewidth, mPaint.getColor(), mOldX, mOldY, mCurX, mCurY, mCurrDrawRect));
|
||||
|
||||
tPath.add(
|
||||
new TouchPath(
|
||||
fLinewidth, mPaint.getColor(), mOldX, mOldY, mCurX, mCurY, mCurrDrawRect));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -608,14 +603,16 @@ public class NtxView extends View {
|
|||
}
|
||||
}
|
||||
|
||||
private Runnable runPostInvalidate = new Runnable() {
|
||||
private Runnable runPostInvalidate =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
postInvalidate();
|
||||
}
|
||||
};
|
||||
|
||||
private Runnable runClear = new Runnable() {
|
||||
private Runnable runClear =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
|
|
@ -629,13 +626,17 @@ public class NtxView extends View {
|
|||
Bitmap bmpBackground = null;
|
||||
|
||||
if (strBackgroundPath.equals("")) {
|
||||
if (mBitmap == null)
|
||||
return -1; // retrun -1 ==> fail; return 0 ==> success;
|
||||
if (mBitmap == null) return -1; // retrun -1 ==> fail; return 0 ==> success;
|
||||
|
||||
bmpBackground = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
bmpBackground =
|
||||
Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
} else {
|
||||
bmpBackground = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(strBackgroundPath), mBitmap.getWidth(),
|
||||
mBitmap.getHeight(), true);
|
||||
bmpBackground =
|
||||
Bitmap.createScaledBitmap(
|
||||
BitmapFactory.decodeFile(strBackgroundPath),
|
||||
mBitmap.getWidth(),
|
||||
mBitmap.getHeight(),
|
||||
true);
|
||||
}
|
||||
|
||||
if (mBitmap != null) {
|
||||
|
|
@ -652,7 +653,6 @@ public class NtxView extends View {
|
|||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -671,8 +671,7 @@ public class NtxView extends View {
|
|||
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.drawBitmap(foreground, (bgWidth - fgWidth) / 2, (bgHeight - fgHeight) / 2, null);
|
||||
canvas.save(Canvas.ALL_SAVE_FLAG);
|
||||
canvas.restore();
|
||||
return newmap;
|
||||
|
|
@ -691,27 +690,24 @@ public class NtxView extends View {
|
|||
return;
|
||||
}
|
||||
|
||||
private void updatePenStroke( float pressure ) {
|
||||
private void updatePenStroke(float pressure) {
|
||||
|
||||
if ( mPenType == PenType.PENCIL.ordinal() ) {
|
||||
if (mPenType == PenType.PENCIL.ordinal()) {
|
||||
mPaint.setStrokeWidth(fLinewidth);
|
||||
nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(NOTSET);
|
||||
nDrawHelper.NDrawSetStrokeWidth(fLinewidth);
|
||||
} else if ( mPenType == PenType.PEN.ordinal() ) {
|
||||
} else if (mPenType == PenType.PEN.ordinal()) {
|
||||
isEnablePenPressure = true;
|
||||
mPaint.setStrokeWidth(fLinewidth * pressure);
|
||||
nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(fLinewidth);
|
||||
|
||||
} else if ( mPenType == PenType.CHINESE_BRUSH.ordinal() ) {
|
||||
} else if (mPenType == PenType.CHINESE_BRUSH.ordinal()) {
|
||||
isEnablePenPressure = true;
|
||||
mPaint.setStrokeWidth((float)(max_pressure * Math.pow(pressure,3)));
|
||||
mPaint.setStrokeWidth((float) (max_pressure * Math.pow(pressure, 3)));
|
||||
nDrawHelper.NDrawSetMaxStrokeWidthWhenUsingPressure(max_pressure);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean getPenThicknessWithPressure() {
|
||||
return isEnablePenPressure;
|
||||
}
|
||||
|
|
@ -767,7 +763,7 @@ public class NtxView extends View {
|
|||
}
|
||||
|
||||
public int getPenType() {
|
||||
//Log.d(TAG, "getPenType: " + mPenType);
|
||||
// Log.d(TAG, "getPenType: " + mPenType);
|
||||
return mPenType;
|
||||
}
|
||||
|
||||
|
|
@ -803,7 +799,9 @@ public class NtxView extends View {
|
|||
}
|
||||
|
||||
private int getToolType(MotionEvent event) {
|
||||
return hasToolStylus(event) ? MotionEvent.TOOL_TYPE_STYLUS : event.getToolType(event.getActionIndex());
|
||||
return hasToolStylus(event)
|
||||
? MotionEvent.TOOL_TYPE_STYLUS
|
||||
: event.getToolType(event.getActionIndex());
|
||||
}
|
||||
|
||||
private boolean hasToolStylus(MotionEvent event) {
|
||||
|
|
@ -848,7 +846,12 @@ public class NtxView extends View {
|
|||
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);
|
||||
mCanvas.drawLine(
|
||||
(float) tp.mOldX,
|
||||
(float) tp.mOldY,
|
||||
(float) tp.mCurX,
|
||||
(float) tp.mCurY,
|
||||
mCurrDrawPaint);
|
||||
}
|
||||
nDrawHelper.NDrawSetStrokeWidth(currStrokeWidth);
|
||||
nDrawHelper.NDrawSetPaintColor(currColor);
|
||||
|
|
@ -901,16 +904,13 @@ public class NtxView extends 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;
|
||||
|
|
@ -920,7 +920,14 @@ public class NtxView extends View {
|
|||
public Rect mRect;
|
||||
public int mStrokeWidth;
|
||||
|
||||
public TouchPath(int strokeWidth, int currColor, int currOldX, int currOldY, int currCurX, int currCurY, Rect currmRect) {
|
||||
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;
|
||||
|
|
@ -934,29 +941,36 @@ public class NtxView extends View {
|
|||
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_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_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};
|
||||
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.NDrawInit(multi_active_region);
|
||||
nDrawHelper.NDrawSetDrawRegion(multi_active_region);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,36 +4,41 @@ import android.content.Context;
|
|||
import android.util.AttributeSet;
|
||||
import android.widget.Spinner;
|
||||
|
||||
public class ReselectSpinner extends Spinner{
|
||||
public class ReselectSpinner extends Spinner {
|
||||
|
||||
public ReselectSpinner(Context context)
|
||||
{ super(context); }
|
||||
public ReselectSpinner(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ReselectSpinner(Context context, AttributeSet attrs)
|
||||
{ super(context, attrs); }
|
||||
public ReselectSpinner(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public ReselectSpinner(Context context, AttributeSet attrs, int defStyle)
|
||||
{ super(context, attrs, defStyle); }
|
||||
public ReselectSpinner(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override public void
|
||||
setSelection(int position, boolean animate)
|
||||
{
|
||||
@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());
|
||||
// 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)
|
||||
{
|
||||
@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());
|
||||
// Spinner does not call the OnItemSelectedListener if the same item is selected, so do it
|
||||
// manually now
|
||||
getOnItemSelectedListener()
|
||||
.onItemSelected(this, getSelectedView(), position, getSelectedItemId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package ntx.demo;
|
||||
|
||||
import android.app.Activity;
|
||||
|
|
@ -20,16 +19,12 @@ 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
|
||||
|
|
@ -195,9 +190,9 @@ public class MainActivity extends Activity
|
|||
/**
|
||||
* 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.
|
||||
* <p>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
|
||||
*/
|
||||
|
|
@ -294,23 +289,26 @@ public class MainActivity extends Activity
|
|||
*/
|
||||
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 =
|
||||
// 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
|
||||
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_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
|
||||
public static final int UI_MONOCHROME_MODE =
|
||||
EINK_MONOCHROME_MODE_MONOCHROME
|
||||
| EINK_CONVERT_MODE_NOCONVERT
|
||||
| EINK_INVERT_MODE_NOINVERT
|
||||
| EINK_DITHER_MODE_NODITHER
|
||||
|
|
@ -323,7 +321,6 @@ public class MainActivity extends Activity
|
|||
private Spinner spinner1, spinner2, spinner3, spinner4, spinner5;
|
||||
private Button btnApply, btnFullRefresh;
|
||||
|
||||
|
||||
private static final String TAG = "MainActivity";
|
||||
|
||||
private Activity mActivity = null;
|
||||
|
|
@ -338,7 +335,6 @@ public class MainActivity extends Activity
|
|||
|
||||
private int einkMode;
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle paramBundle) {
|
||||
super.onCreate(paramBundle);
|
||||
|
|
@ -346,8 +342,9 @@ public class MainActivity extends Activity
|
|||
mContext = getApplicationContext();
|
||||
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
getWindow()
|
||||
.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
|
||||
setContentView(R.layout.main);
|
||||
|
||||
|
|
@ -358,21 +355,17 @@ public class MainActivity extends Activity
|
|||
mPM = (PowerManager) mActivity.getSystemService(Context.POWER_SERVICE);
|
||||
|
||||
setResImages();
|
||||
iv.setOnClickListener(new OnClickListener() {
|
||||
iv.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setResImages();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
initUI();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void initUI() {
|
||||
|
||||
spinner1 = (Spinner) findViewById(R.id.spinner1);
|
||||
|
|
@ -380,9 +373,8 @@ public class MainActivity extends Activity
|
|||
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);
|
||||
|
||||
btnApply = (Button) findViewById(R.id.btnApply);
|
||||
btnFullRefresh = (Button) findViewById(R.id.btnFullRefresh);
|
||||
|
||||
// set spinner default value
|
||||
String defaultitem1 = "GC16";
|
||||
|
|
@ -395,53 +387,42 @@ public class MainActivity extends Activity
|
|||
spinnerPosition = myAdap.getPosition(defaultitem2);
|
||||
spinner2.setSelection(spinnerPosition);
|
||||
|
||||
|
||||
btnApply.setOnClickListener(new OnClickListener() {
|
||||
btnApply.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
|
||||
applyUpdateParams();
|
||||
|
||||
Toast toast = Toast.makeText(getApplicationContext(), "Applied Successfully", Toast.LENGTH_SHORT);
|
||||
Toast toast =
|
||||
Toast.makeText(getApplicationContext(), "Applied Successfully", Toast.LENGTH_SHORT);
|
||||
toast.setGravity(Gravity.TOP, 0, 0);
|
||||
toast.show();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
btnFullRefresh.setOnClickListener(new OnClickListener() {
|
||||
btnFullRefresh.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View arg0) {
|
||||
|
||||
doGC16FullRefresh();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
applyUpdateParams();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void 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());
|
||||
|
||||
|
||||
int waveform = 0;
|
||||
if (waveformStr.equalsIgnoreCase("DU")) {
|
||||
waveform = EINK_WAVEFORM_MODE_DU;
|
||||
|
|
@ -481,34 +462,25 @@ public class MainActivity extends Activity
|
|||
}
|
||||
|
||||
int hardware_dither = 0;
|
||||
if(hardwareDitherStr.equals("NONE")){
|
||||
if (hardwareDitherStr.equals("NONE")) {
|
||||
hardware_dither = EINK_DITHER_MODE_NODITHER;
|
||||
} else if(hardwareDitherStr.equals("DITHER")){
|
||||
} 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(){
|
||||
public void doGC16FullRefresh() {
|
||||
|
||||
Intent fullRefreshIntent = new Intent("ntx.eink_control.QUICK_REFRESH");
|
||||
fullRefreshIntent.putExtra("updatemode",NtxImageViewClass.UPDATE_MODE_FULL_GC16);
|
||||
fullRefreshIntent.putExtra("updatemode", NtxImageViewClass.UPDATE_MODE_FULL_GC16);
|
||||
|
||||
sendBroadcast(fullRefreshIntent);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
|
@ -518,22 +490,17 @@ public class MainActivity extends Activity
|
|||
|
||||
private static List<Integer> imageValues = getImageValues();
|
||||
|
||||
public static List<Integer> getImageValues()
|
||||
{
|
||||
try
|
||||
{
|
||||
public static List<Integer> getImageValues() {
|
||||
try {
|
||||
Field[] drawableFields = R.drawable.class.getFields();
|
||||
List<Integer> resourceValues = new ArrayList<Integer>();
|
||||
for (Field field : drawableFields)
|
||||
{
|
||||
if (field.getName().indexOf("a_") != -1)
|
||||
{
|
||||
for (Field field : drawableFields) {
|
||||
if (field.getName().indexOf("a_") != -1) {
|
||||
resourceValues.add(field.getInt(R.drawable.class));
|
||||
}
|
||||
}
|
||||
return resourceValues;
|
||||
} catch (Exception e)
|
||||
{
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -542,16 +509,13 @@ public class MainActivity extends Activity
|
|||
|
||||
private void setResImages() {
|
||||
if (imageValues != null) {
|
||||
if (imageValues.size() != 0)
|
||||
{
|
||||
if (++page_index >= imageValues.size())
|
||||
{
|
||||
if (imageValues.size() != 0) {
|
||||
if (++page_index >= imageValues.size()) {
|
||||
page_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (page_index > -1)
|
||||
{
|
||||
if (page_index > -1) {
|
||||
iv.setImageDrawable(getResources().getDrawable(imageValues.get(page_index)));
|
||||
} else {
|
||||
iv.setImageDrawable(getResources().getDrawable(R.drawable.default_photo));
|
||||
|
|
@ -564,9 +528,7 @@ public class MainActivity extends Activity
|
|||
if ((mWakeLock != null) && (mWakeLock.isHeld())) {
|
||||
return;
|
||||
}
|
||||
mWakeLock = mPM.newWakeLock(
|
||||
PowerManager.FULL_WAKE_LOCK
|
||||
, this.getClass().getCanonicalName());
|
||||
mWakeLock = mPM.newWakeLock(PowerManager.FULL_WAKE_LOCK, this.getClass().getCanonicalName());
|
||||
mWakeLock.setReferenceCounted(false);
|
||||
mWakeLock.acquire();
|
||||
}
|
||||
|
|
@ -574,16 +536,19 @@ public class MainActivity extends Activity
|
|||
public void cancelWakeLock() {
|
||||
final Handler handler = new Handler();
|
||||
if ((mWakeLock != null) && (mWakeLock.isHeld())) {
|
||||
new Thread(new Runnable() {
|
||||
new Thread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handler.post(runnable);
|
||||
}
|
||||
}).start();
|
||||
})
|
||||
.start();
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable runnable = new Runnable() {
|
||||
private Runnable runnable =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
package ntx.demo;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
|
|
@ -7,7 +8,7 @@ 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;
|
||||
|
|
@ -15,19 +16,15 @@ import android.widget.ImageView;
|
|||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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
|
||||
|
|
@ -63,16 +60,13 @@ import android.widget.ImageView;
|
|||
| 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;
|
||||
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;
|
||||
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;
|
||||
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
|
||||
|
|
@ -97,8 +91,8 @@ import android.widget.ImageView;
|
|||
}
|
||||
|
||||
public Bitmap loadBitmap(Drawable sprite, Bitmap.Config bitmapConfig) {
|
||||
Display d = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE))
|
||||
.getDefaultDisplay();
|
||||
Display d =
|
||||
((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
int width = d.getWidth();
|
||||
int height = d.getHeight();
|
||||
|
||||
|
|
@ -116,8 +110,7 @@ import android.widget.ImageView;
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setImageBitmap(Bitmap bm)
|
||||
{
|
||||
public void setImageBitmap(Bitmap bm) {
|
||||
invalidate(UPDATE_MODE_SCREEN);
|
||||
super.setImageBitmap(bm);
|
||||
}
|
||||
|
|
@ -132,16 +125,8 @@ import android.widget.ImageView;
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setUPDATE_MODE_SCREEN(int updateMode) {
|
||||
|
||||
|
||||
UPDATE_MODE_SCREEN=updateMode;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
UPDATE_MODE_SCREEN = updateMode;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
Loading…
Reference in a new issue