From 6caaa1cc2dd048215916610a519a452300fb778d Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Fri, 7 Aug 2026 23:14:02 +0000 Subject: [PATCH 01/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index 3aefe53..1178bb5 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity....MainActivity....MainActivity.....bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity....MainActivity....MainActivity.....bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From b484cf3175b31465c44184782929261fc0fe23ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 04:46:27 +0530 Subject: [PATCH 02/29] Notereplyreceiver added --- .../github/ronynn/karui/MyWebViewClient.java | 25 ------------- .../ronynn/karui/NoteReplyReceiver.java | 37 +++++++++++++++++++ 2 files changed, 37 insertions(+), 25 deletions(-) delete mode 100644 app/src/main/java/io/github/ronynn/karui/MyWebViewClient.java create mode 100644 app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java diff --git a/app/src/main/java/io/github/ronynn/karui/MyWebViewClient.java b/app/src/main/java/io/github/ronynn/karui/MyWebViewClient.java deleted file mode 100644 index e1ff130..0000000 --- a/app/src/main/java/io/github/ronynn/karui/MyWebViewClient.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.ronynn.karui; - -import android.content.Intent; -import android.net.Uri; -import android.webkit.WebView; -import android.webkit.WebViewClient; - -class MyWebViewClient extends WebViewClient { - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - String hostname; - - // YOUR HOSTNAME - hostname = "example.com"; - - Uri uri = Uri.parse(url); - if (url.startsWith("file:") || uri.getHost() != null && uri.getHost().endsWith(hostname)) { - return false; - } - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - view.getContext().startActivity(intent); - return true; - } -} diff --git a/app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java b/app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java new file mode 100644 index 0000000..f587cb8 --- /dev/null +++ b/app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java @@ -0,0 +1,37 @@ +package io.github.ronynn.karui; + +import android.app.RemoteInput; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; + +public class NoteReplyReceiver extends BroadcastReceiver { + public static final String KEY_TEXT_REPLY = "key_text_reply"; + + @Override + public void onReceive(Context context, Intent intent) { + Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); + if (remoteInput != null) { + CharSequence replyText = remoteInput.getCharSequence(KEY_TEXT_REPLY); + if (replyText != null && replyText.length() > 0) { + SharedPreferences prefs = context.getSharedPreferences("note_queue", Context.MODE_PRIVATE); + String existingNotes = prefs.getString("pending_notes", ""); + String existingTabs = prefs.getString("pending_notes_tabs", ""); + + // Read the current inbox tab name (default "Inbox") + String inboxTab = prefs.getString("inbox_tab_name", "Inbox"); + + String newNote = replyText.toString(); + String updatedNotes = existingNotes.isEmpty() ? newNote : existingNotes + "\n" + newNote; + String updatedTabs = existingTabs.isEmpty() ? inboxTab : existingTabs + "\n" + inboxTab; + + prefs.edit() + .putString("pending_notes", updatedNotes) + .putString("pending_notes_tabs", updatedTabs) + .apply(); + } + } + } +} From 9d40083218892d7a0365f46f68bc1d31feb49bf9 Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Fri, 7 Aug 2026 23:16:35 +0000 Subject: [PATCH 03/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index 1178bb5..d75a9d9 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity....MainActivity....MainActivity.....bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity....MainActivity....MainActivity.....bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From d97c4f7727bbad97510310349965a9cbbcb5f9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 04:46:55 +0530 Subject: [PATCH 04/29] Statusbarnotes support --- .../io/github/ronynn/karui/MainActivity.java | 184 +++++++++++++++++- 1 file changed, 181 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/github/ronynn/karui/MainActivity.java b/app/src/main/java/io/github/ronynn/karui/MainActivity.java index 0c066c3..81cc7f9 100644 --- a/app/src/main/java/io/github/ronynn/karui/MainActivity.java +++ b/app/src/main/java/io/github/ronynn/karui/MainActivity.java @@ -3,9 +3,18 @@ import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.app.Activity; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.RemoteInput; import android.content.ActivityNotFoundException; +import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.view.View; import android.view.animation.DecelerateInterpolator; @@ -31,6 +40,9 @@ public class MainActivity extends Activity { private static final int CREATE_FILE_REQUEST_CODE = 1; private static final int IMPORT_FILE_REQUEST_CODE = 2; private static final int FILECHOOSER_RESULTCODE = 3; + private static final int NOTIFICATION_PERMISSION_REQUEST = 100; + private static final String CHANNEL_ID = "note_reply_channel"; + private static final int NOTIFICATION_ID = 1; private WebView mWebView; private View splashScreen; @@ -38,9 +50,12 @@ public class MainActivity extends Activity { private String pendingFileName; private String pendingFileData; private String pendingFileType; - + private ValueCallback mFilePathCallback; + private boolean isNotificationActive = false; + private String inboxTabName = "Inbox"; + @Override @SuppressLint({"SetJavaScriptEnabled", "AllowFileAccess"}) protected void onCreate(Bundle savedInstanceState) { @@ -107,12 +122,124 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC }); mWebView.loadUrl("file:///android_asset/index.html"); + + // Load persisted inbox tab name + SharedPreferences prefs = getSharedPreferences("note_queue", MODE_PRIVATE); + inboxTabName = prefs.getString("inbox_tab_name", "Inbox"); + + createNotificationChannel(); + } + + // ---------- NOTIFICATION METHODS (NO AndroidX) ---------- + + private void createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, "Quick Note", NotificationManager.IMPORTANCE_LOW); + channel.setDescription("Allows adding notes from the status bar"); + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager != null) manager.createNotificationChannel(channel); + } + } + + @SuppressLint("NewApi") // RemoteInput is API 20, we check + private void showNotification() { + // Build open-app pending intent + Intent openAppIntent = new Intent(this, MainActivity.class); + PendingIntent openPendingIntent = PendingIntent.getActivity( + this, 0, openAppIntent, + PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0)); + + // Build the direct reply action + RemoteInput remoteInput = new RemoteInput.Builder(NoteReplyReceiver.KEY_TEXT_REPLY) + .setLabel("Add Note") + .build(); + + Intent replyIntent = new Intent(this, NoteReplyReceiver.class); + PendingIntent replyPendingIntent = PendingIntent.getBroadcast( + this, 1, replyIntent, + PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0)); + + Notification.Action replyAction = new Notification.Action.Builder( + android.R.drawable.ic_menu_send, + "Add Note", + replyPendingIntent) + .addRemoteInput(remoteInput) + .build(); + + Notification notification; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notification = new Notification.Builder(this, CHANNEL_ID) + .setSmallIcon(android.R.drawable.ic_dialog_info) + .setContentTitle("Quick Note") + .setContentText("Swipe down to add a note") + .setContentIntent(openPendingIntent) + .addAction(replyAction) + .setOngoing(true) + .build(); + } else { + // Fallback for older APIs (unlikely you target below 21, but just in case) + notification = new Notification.Builder(this) + .setSmallIcon(android.R.drawable.ic_dialog_info) + .setContentTitle("Quick Note") + .setContentText("Swipe down to add a note") + .setContentIntent(openPendingIntent) + .addAction(replyAction.getIcon(), replyAction.title, replyAction.actionIntent) + .setOngoing(true) + .build(); + } + + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager != null) manager.notify(NOTIFICATION_ID, notification); + isNotificationActive = true; + } + + private void cancelNotification() { + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager != null) manager.cancel(NOTIFICATION_ID); + isNotificationActive = false; + } + + // ---------- PENDING NOTES INJECTION ---------- + + private void injectPendingNotes() { + SharedPreferences prefs = getSharedPreferences("note_queue", MODE_PRIVATE); + String pendingNotes = prefs.getString("pending_notes", ""); + String pendingTabs = prefs.getString("pending_notes_tabs", ""); + if (pendingNotes.isEmpty()) return; + + prefs.edit().remove("pending_notes").remove("pending_notes_tabs").apply(); + + String[] notes = pendingNotes.split("\n"); + String[] tabs = pendingTabs.split("\n"); + + for (int i = 0; i < notes.length; i++) { + if (notes[i].trim().isEmpty()) continue; + String tabName = (i < tabs.length) ? tabs[i] : inboxTabName; + String escapedNote = notes[i] + .replace("\\", "\\\\") + .replace("'", "\\'") + .replace("\n", "\\n"); + String escapedTab = tabName + .replace("\\", "\\\\") + .replace("'", "\\'"); + String js = String.format("syncNoteFromAndroid('%s', '%s')", escapedNote, escapedTab); + mWebView.evaluateJavascript(js, null); + } } + @Override + protected void onResume() { + super.onResume(); + injectPendingNotes(); + } + + // ---------- ACTIVITY RESULT ---------- + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - + if (requestCode == CREATE_FILE_REQUEST_CODE && resultCode == RESULT_OK) { if (data != null && data.getData() != null && pendingFileData != null) { try { @@ -137,7 +264,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } reader.close(); String jsonContent = sb.toString(); - + String jsCode = "if(window.setAndroidNotes) window.setAndroidNotes(" + JSONObject.quote(jsonContent) + ");"; mWebView.evaluateJavascript(jsCode, null); } catch (IOException e) { @@ -173,7 +300,16 @@ public void onBackPressed() { } } + @Override + protected void onDestroy() { + super.onDestroy(); + if (mWebView != null) mWebView.destroy(); + } + + // ---------- JAVASCRIPT INTERFACE (no AndroidX) ---------- + public class WebAppInterface { + @JavascriptInterface public void saveFile(String fileName, String fileData, String fileType) { pendingFileName = fileName; @@ -194,5 +330,47 @@ public void importJsonFile() { intent.setType("application/json"); startActivityForResult(intent, IMPORT_FILE_REQUEST_CODE); } + + @JavascriptInterface + public void toggleNotification(boolean enable) { + runOnUiThread(() -> { + if (enable) { + // Android 13+ requires runtime permission, but only if target SDK >= 33 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // Check if the permission is already granted + if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) + != PackageManager.PERMISSION_GRANTED) { + // Request the permission (this will show system dialog) + requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, + NOTIFICATION_PERMISSION_REQUEST); + return; // wait for the permission callback + } + } + showNotification(); + } else { + cancelNotification(); + } + }); + } + + @JavascriptInterface + public void setInboxTabName(String tabName) { + if (tabName == null || tabName.trim().isEmpty()) tabName = "Inbox"; + inboxTabName = tabName.trim(); + getSharedPreferences("note_queue", MODE_PRIVATE) + .edit().putString("inbox_tab_name", inboxTabName).apply(); + } + } + + // Permission result callback (no AndroidX) + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + if (requestCode == NOTIFICATION_PERMISSION_REQUEST) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + showNotification(); + } else { + Toast.makeText(this, "Notification permission denied", Toast.LENGTH_SHORT).show(); + } + } } } From fe4403b1861987f6cc83283a8a18a0032f320a79 Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Fri, 7 Aug 2026 23:17:03 +0000 Subject: [PATCH 05/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index d75a9d9..33584b0 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity....MainActivity....MainActivity.....bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From 6a32aec6d43d8366dc781d1f016d53724b7a7120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 04:49:04 +0530 Subject: [PATCH 06/29] Notf permissions --- app/src/main/AndroidManifest.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 956d12c..129123a 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> + + - \ No newline at end of file + From 814fdb303e5a98ffda55224f4b3b665c2d306d46 Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Fri, 7 Aug 2026 23:19:13 +0000 Subject: [PATCH 07/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index 33584b0..f875407 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From 52c79efc803da55ccb45dbd050d9f9af67e16594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 04:53:30 +0530 Subject: [PATCH 08/29] Notf bar settings --- app/src/main/assets/index.html | 71 +++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/app/src/main/assets/index.html b/app/src/main/assets/index.html index 7cc5bc4..944f4d8 100644 --- a/app/src/main/assets/index.html +++ b/app/src/main/assets/index.html @@ -133,6 +133,20 @@

Fonts


Add Notes To Bottom + + +
+ Enable Quick Note + +
+
+ + +
+ + + +
@@ -550,7 +564,62 @@

Recycle Bin

try { mergeNotes(JSON.parse(jsonString)); } catch(err) {} }; +// Status bar notes integration +function loadNotificationSettings() { + let notifEnabled = localStorage.getItem("notificationEnabled") === "true"; + document.getElementById('notification-toggle').checked = notifEnabled; + + let savedTab = localStorage.getItem("inboxTabName") || "Inbox"; + document.getElementById('inbox-tab-name').value = savedTab; + // Ensure the tab exists without switching to it + ensureTabExists(savedTab); + // Sync to Android + if (window.Android) { + window.Android.setInboxTabName(savedTab); + if (notifEnabled) window.Android.toggleNotification(true); + } +} + +function toggleNotification(enable) { + localStorage.setItem("notificationEnabled", enable); + if (window.Android) { + window.Android.toggleNotification(enable); + } +} + +function updateInboxTab(name) { + name = name.trim() || "Inbox"; + localStorage.setItem("inboxTabName", name); + ensureTabExists(name); + if (window.Android) { + window.Android.setInboxTabName(name); + } +} + +// Helper: add a tab if it doesn't exist, without changing active category +function ensureTabExists(name) { + if (!noteCategories.includes(name)) { + noteCategories.push(name); + saveData(); + renderTabs(); + } +} + +// Modified note receiver: adds note to the specified tab +window.syncNoteFromAndroid = function(text, tabName) { + tabName = tabName || 'Inbox'; + ensureTabExists(tabName); + let note = { id: Date.now(), text: text, completed: false, category: tabName }; + notes.unshift(note); + saveData(); + if (activeCategory === tabName) { + renderNotes(); + } + // Also update stats if needed (but it's okay) +}; + init(); +loadNotificationSettings(); - \ No newline at end of file + From 3b7910af69c392a9db6ba31e7779eb39cfef39b5 Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Fri, 7 Aug 2026 23:23:38 +0000 Subject: [PATCH 09/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index f875407..5b89eff 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From ecd8fbbc66175926fdaf2805b16d465a07a8242b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 05:00:21 +0530 Subject: [PATCH 10/29] Fix for build error --- .../io/github/ronynn/karui/MainActivity.java | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/io/github/ronynn/karui/MainActivity.java b/app/src/main/java/io/github/ronynn/karui/MainActivity.java index 81cc7f9..eaa5f6e 100644 --- a/app/src/main/java/io/github/ronynn/karui/MainActivity.java +++ b/app/src/main/java/io/github/ronynn/karui/MainActivity.java @@ -1,5 +1,6 @@ package io.github.ronynn.karui; +import android.Manifest; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.app.Activity; @@ -130,35 +131,35 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC createNotificationChannel(); } - // ---------- NOTIFICATION METHODS (NO AndroidX) ---------- + // ---------- NOTIFICATION METHODS (no AndroidX, works API 21+) ---------- private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "Quick Note", NotificationManager.IMPORTANCE_LOW); - channel.setDescription("Allows adding notes from the status bar"); + channel.setDescription("Add notes from status bar"); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (manager != null) manager.createNotificationChannel(channel); } } - @SuppressLint("NewApi") // RemoteInput is API 20, we check + @SuppressLint("NewApi") private void showNotification() { - // Build open-app pending intent + // Open app pending intent Intent openAppIntent = new Intent(this, MainActivity.class); - PendingIntent openPendingIntent = PendingIntent.getActivity( - this, 0, openAppIntent, - PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0)); + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent openPendingIntent = PendingIntent.getActivity(this, 0, openAppIntent, flags); - // Build the direct reply action + // Remote input and action RemoteInput remoteInput = new RemoteInput.Builder(NoteReplyReceiver.KEY_TEXT_REPLY) .setLabel("Add Note") .build(); Intent replyIntent = new Intent(this, NoteReplyReceiver.class); - PendingIntent replyPendingIntent = PendingIntent.getBroadcast( - this, 1, replyIntent, - PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0)); + PendingIntent replyPendingIntent = PendingIntent.getBroadcast(this, 1, replyIntent, flags); Notification.Action replyAction = new Notification.Action.Builder( android.R.drawable.ic_menu_send, @@ -167,6 +168,7 @@ private void showNotification() { .addRemoteInput(remoteInput) .build(); + // Build notification using correct constructor based on API level Notification notification; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notification = new Notification.Builder(this, CHANNEL_ID) @@ -174,17 +176,17 @@ private void showNotification() { .setContentTitle("Quick Note") .setContentText("Swipe down to add a note") .setContentIntent(openPendingIntent) - .addAction(replyAction) + .addAction(replyAction) // works API 20+ .setOngoing(true) .build(); } else { - // Fallback for older APIs (unlikely you target below 21, but just in case) + // API 21–25 fallback (no channel, but supports action and remote input) notification = new Notification.Builder(this) .setSmallIcon(android.R.drawable.ic_dialog_info) .setContentTitle("Quick Note") .setContentText("Swipe down to add a note") .setContentIntent(openPendingIntent) - .addAction(replyAction.getIcon(), replyAction.title, replyAction.actionIntent) + .addAction(replyAction) .setOngoing(true) .build(); } @@ -306,7 +308,7 @@ protected void onDestroy() { if (mWebView != null) mWebView.destroy(); } - // ---------- JAVASCRIPT INTERFACE (no AndroidX) ---------- + // ---------- JAVASCRIPT INTERFACE ---------- public class WebAppInterface { @@ -335,15 +337,14 @@ public void importJsonFile() { public void toggleNotification(boolean enable) { runOnUiThread(() -> { if (enable) { - // Android 13+ requires runtime permission, but only if target SDK >= 33 + // Android 13+ (API 33) requires runtime permission for notifications if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - // Check if the permission is already granted if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { - // Request the permission (this will show system dialog) - requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, + requestPermissions( + new String[]{Manifest.permission.POST_NOTIFICATIONS}, NOTIFICATION_PERMISSION_REQUEST); - return; // wait for the permission callback + return; // callback will handle showNotification() } } showNotification(); @@ -362,7 +363,7 @@ public void setInboxTabName(String tabName) { } } - // Permission result callback (no AndroidX) + // Permission result (for notification on API 33+) @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == NOTIFICATION_PERMISSION_REQUEST) { From 061852cd8b41f40d9dbcb59de23e41ed22427f8d Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Fri, 7 Aug 2026 23:30:36 +0000 Subject: [PATCH 11/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index 5b89eff..b68698f 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From 7c6a1bb2c45c21f1b921f270b46c21dc55d18702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 05:13:16 +0530 Subject: [PATCH 12/29] Try catch crash bug --- .../io/github/ronynn/karui/MainActivity.java | 128 ++++++++++-------- 1 file changed, 73 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/io/github/ronynn/karui/MainActivity.java b/app/src/main/java/io/github/ronynn/karui/MainActivity.java index eaa5f6e..62a24d9 100644 --- a/app/src/main/java/io/github/ronynn/karui/MainActivity.java +++ b/app/src/main/java/io/github/ronynn/karui/MainActivity.java @@ -131,75 +131,93 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC createNotificationChannel(); } - // ---------- NOTIFICATION METHODS (no AndroidX, works API 21+) ---------- + // ---------- NOTIFICATION METHODS (safe, no AndroidX) ---------- private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationChannel channel = new NotificationChannel( - CHANNEL_ID, "Quick Note", NotificationManager.IMPORTANCE_LOW); - channel.setDescription("Add notes from status bar"); - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (manager != null) manager.createNotificationChannel(channel); + try { + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, "Quick Note", NotificationManager.IMPORTANCE_LOW); + channel.setDescription("Add notes from status bar"); + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager != null) manager.createNotificationChannel(channel); + } catch (Exception e) { + e.printStackTrace(); + } } } @SuppressLint("NewApi") private void showNotification() { - // Open app pending intent - Intent openAppIntent = new Intent(this, MainActivity.class); - int flags = PendingIntent.FLAG_UPDATE_CURRENT; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - flags |= PendingIntent.FLAG_IMMUTABLE; - } - PendingIntent openPendingIntent = PendingIntent.getActivity(this, 0, openAppIntent, flags); - - // Remote input and action - RemoteInput remoteInput = new RemoteInput.Builder(NoteReplyReceiver.KEY_TEXT_REPLY) - .setLabel("Add Note") - .build(); + try { + // Use the app's own launcher icon (guaranteed to exist) + int iconRes = R.mipmap.ic_launcher; + + // Open app pending intent + Intent openAppIntent = new Intent(this, MainActivity.class); + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent openPendingIntent = PendingIntent.getActivity(this, 0, openAppIntent, flags); - Intent replyIntent = new Intent(this, NoteReplyReceiver.class); - PendingIntent replyPendingIntent = PendingIntent.getBroadcast(this, 1, replyIntent, flags); + // Remote input and reply action + RemoteInput remoteInput = new RemoteInput.Builder(NoteReplyReceiver.KEY_TEXT_REPLY) + .setLabel("Add Note") + .build(); - Notification.Action replyAction = new Notification.Action.Builder( - android.R.drawable.ic_menu_send, - "Add Note", - replyPendingIntent) - .addRemoteInput(remoteInput) - .build(); + Intent replyIntent = new Intent(this, NoteReplyReceiver.class); + PendingIntent replyPendingIntent = PendingIntent.getBroadcast(this, 1, replyIntent, flags); - // Build notification using correct constructor based on API level - Notification notification; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - notification = new Notification.Builder(this, CHANNEL_ID) - .setSmallIcon(android.R.drawable.ic_dialog_info) - .setContentTitle("Quick Note") - .setContentText("Swipe down to add a note") - .setContentIntent(openPendingIntent) - .addAction(replyAction) // works API 20+ - .setOngoing(true) - .build(); - } else { - // API 21–25 fallback (no channel, but supports action and remote input) - notification = new Notification.Builder(this) - .setSmallIcon(android.R.drawable.ic_dialog_info) - .setContentTitle("Quick Note") - .setContentText("Swipe down to add a note") - .setContentIntent(openPendingIntent) - .addAction(replyAction) - .setOngoing(true) + Notification.Action replyAction = new Notification.Action.Builder( + iconRes, // safe icon + "Add Note", + replyPendingIntent) + .addRemoteInput(remoteInput) .build(); - } - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (manager != null) manager.notify(NOTIFICATION_ID, notification); - isNotificationActive = true; + // Build notification + Notification notification; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notification = new Notification.Builder(this, CHANNEL_ID) + .setSmallIcon(iconRes) + .setContentTitle("Quick Note") + .setContentText("Swipe down to add a note") + .setContentIntent(openPendingIntent) + .addAction(replyAction) + .setOngoing(true) + .build(); + } else { + notification = new Notification.Builder(this) + .setSmallIcon(iconRes) + .setContentTitle("Quick Note") + .setContentText("Swipe down to add a note") + .setContentIntent(openPendingIntent) + .addAction(replyAction) + .setOngoing(true) + .build(); + } + + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager != null) { + manager.notify(NOTIFICATION_ID, notification); + isNotificationActive = true; + } + } catch (Exception e) { + // Prevent crash – show error and move on + Toast.makeText(this, "Couldn't show notification: " + e.getMessage(), Toast.LENGTH_LONG).show(); + e.printStackTrace(); + } } private void cancelNotification() { - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (manager != null) manager.cancel(NOTIFICATION_ID); - isNotificationActive = false; + try { + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager != null) manager.cancel(NOTIFICATION_ID); + isNotificationActive = false; + } catch (Exception e) { + e.printStackTrace(); + } } // ---------- PENDING NOTES INJECTION ---------- @@ -337,14 +355,14 @@ public void importJsonFile() { public void toggleNotification(boolean enable) { runOnUiThread(() -> { if (enable) { - // Android 13+ (API 33) requires runtime permission for notifications + // Android 13+ (API 33) runtime permission for notifications if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { requestPermissions( new String[]{Manifest.permission.POST_NOTIFICATIONS}, NOTIFICATION_PERMISSION_REQUEST); - return; // callback will handle showNotification() + return; } } showNotification(); From da0f3eb82a6d1f4a6fe5d5a76ec1c24f184e093c Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Fri, 7 Aug 2026 23:43:33 +0000 Subject: [PATCH 13/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index b68698f..17d764a 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From deec4b1a912c409811811f827903a83a403f9432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 05:22:38 +0530 Subject: [PATCH 14/29] Potential fix --- .../io/github/ronynn/karui/MainActivity.java | 74 +++++++++++-------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/io/github/ronynn/karui/MainActivity.java b/app/src/main/java/io/github/ronynn/karui/MainActivity.java index 62a24d9..1dd8691 100644 --- a/app/src/main/java/io/github/ronynn/karui/MainActivity.java +++ b/app/src/main/java/io/github/ronynn/karui/MainActivity.java @@ -135,25 +135,51 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - try { - NotificationChannel channel = new NotificationChannel( - CHANNEL_ID, "Quick Note", NotificationManager.IMPORTANCE_LOW); - channel.setDescription("Add notes from status bar"); - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (manager != null) manager.createNotificationChannel(channel); - } catch (Exception e) { - e.printStackTrace(); - } + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, "Quick Note", NotificationManager.IMPORTANCE_LOW); + channel.setDescription("Add notes from status bar"); + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager != null) manager.createNotificationChannel(channel); } } @SuppressLint("NewApi") private void showNotification() { + // 1. First, ensure the channel exists (idempotent) + createNotificationChannel(); + + // 2. Check if notifications are enabled (API 24+) + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager == null) { + Toast.makeText(this, "Notification service unavailable", Toast.LENGTH_SHORT).show(); + return; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (!manager.areNotificationsEnabled()) { + Toast.makeText(this, "Notifications are disabled. Please enable them in system settings.", Toast.LENGTH_LONG).show(); + // Optionally open the app's notification settings + try { + Intent settingsIntent = new Intent(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + settingsIntent.setAction(android.provider.Settings.ACTION_APP_NOTIFICATION_SETTINGS); + settingsIntent.putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, getPackageName()); + } else { + settingsIntent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + settingsIntent.setData(Uri.parse("package:" + getPackageName())); + } + startActivity(settingsIntent); + } catch (Exception e) { + // ignore + } + return; + } + } + + // 3. Build and post notification try { - // Use the app's own launcher icon (guaranteed to exist) - int iconRes = R.mipmap.ic_launcher; + int iconRes = R.mipmap.ic_launcher; // guaranteed to exist - // Open app pending intent Intent openAppIntent = new Intent(this, MainActivity.class); int flags = PendingIntent.FLAG_UPDATE_CURRENT; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -161,7 +187,6 @@ private void showNotification() { } PendingIntent openPendingIntent = PendingIntent.getActivity(this, 0, openAppIntent, flags); - // Remote input and reply action RemoteInput remoteInput = new RemoteInput.Builder(NoteReplyReceiver.KEY_TEXT_REPLY) .setLabel("Add Note") .build(); @@ -170,13 +195,10 @@ private void showNotification() { PendingIntent replyPendingIntent = PendingIntent.getBroadcast(this, 1, replyIntent, flags); Notification.Action replyAction = new Notification.Action.Builder( - iconRes, // safe icon - "Add Note", - replyPendingIntent) + iconRes, "Add Note", replyPendingIntent) .addRemoteInput(remoteInput) .build(); - // Build notification Notification notification; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notification = new Notification.Builder(this, CHANNEL_ID) @@ -198,26 +220,18 @@ private void showNotification() { .build(); } - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (manager != null) { - manager.notify(NOTIFICATION_ID, notification); - isNotificationActive = true; - } + manager.notify(NOTIFICATION_ID, notification); + isNotificationActive = true; } catch (Exception e) { - // Prevent crash – show error and move on Toast.makeText(this, "Couldn't show notification: " + e.getMessage(), Toast.LENGTH_LONG).show(); e.printStackTrace(); } } private void cancelNotification() { - try { - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (manager != null) manager.cancel(NOTIFICATION_ID); - isNotificationActive = false; - } catch (Exception e) { - e.printStackTrace(); - } + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager != null) manager.cancel(NOTIFICATION_ID); + isNotificationActive = false; } // ---------- PENDING NOTES INJECTION ---------- From 2eb01f27d1495169d1d06284bf6b932222ee8bb7 Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Fri, 7 Aug 2026 23:52:47 +0000 Subject: [PATCH 15/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index 17d764a..20121f4 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From c7f682aab3d26d0be7043391b3459f6d83233981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 05:28:44 +0530 Subject: [PATCH 16/29] Update MainActivity.java --- .../io/github/ronynn/karui/MainActivity.java | 130 +++++++++++++----- 1 file changed, 92 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/io/github/ronynn/karui/MainActivity.java b/app/src/main/java/io/github/ronynn/karui/MainActivity.java index 1dd8691..d1527cc 100644 --- a/app/src/main/java/io/github/ronynn/karui/MainActivity.java +++ b/app/src/main/java/io/github/ronynn/karui/MainActivity.java @@ -14,6 +14,13 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -131,7 +138,33 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC createNotificationChannel(); } - // ---------- NOTIFICATION METHODS (safe, no AndroidX) ---------- + // ---------- NOTIFICATION ICON GENERATOR (no file required) ---------- + + /** + * Creates a simple white circle bitmap that works as a valid notification small icon. + */ + private Bitmap createNotificationIconBitmap() { + int size = 96; // 24dp * 4 for crisp edges + Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(Color.WHITE); + canvas.drawCircle(size / 2f, size / 2f, size / 2f * 0.8f, paint); + return bitmap; + } + + /** + * Returns an Icon object (API 23+) or null for older APIs. + */ + @SuppressLint("NewApi") + private Icon getNotificationIcon() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return Icon.createWithBitmap(createNotificationIconBitmap()); + } + return null; + } + + // ---------- NOTIFICATION CHANNEL ---------- private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -143,32 +176,34 @@ private void createNotificationChannel() { } } + // ---------- SHOW / CANCEL NOTIFICATION ---------- + @SuppressLint("NewApi") private void showNotification() { - // 1. First, ensure the channel exists (idempotent) + // Ensure channel exists createNotificationChannel(); - // 2. Check if notifications are enabled (API 24+) NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (manager == null) { Toast.makeText(this, "Notification service unavailable", Toast.LENGTH_SHORT).show(); return; } + // Check global notification enablement (API 24+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (!manager.areNotificationsEnabled()) { - Toast.makeText(this, "Notifications are disabled. Please enable them in system settings.", Toast.LENGTH_LONG).show(); - // Optionally open the app's notification settings + Toast.makeText(this, "Notifications are disabled in system settings", Toast.LENGTH_LONG).show(); + // Open notification settings for the app try { - Intent settingsIntent = new Intent(); + Intent intent = new Intent(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - settingsIntent.setAction(android.provider.Settings.ACTION_APP_NOTIFICATION_SETTINGS); - settingsIntent.putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, getPackageName()); + intent.setAction(android.provider.Settings.ACTION_APP_NOTIFICATION_SETTINGS); + intent.putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, getPackageName()); } else { - settingsIntent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - settingsIntent.setData(Uri.parse("package:" + getPackageName())); + intent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.parse("package:" + getPackageName())); } - startActivity(settingsIntent); + startActivity(intent); } catch (Exception e) { // ignore } @@ -176,10 +211,9 @@ private void showNotification() { } } - // 3. Build and post notification + // Build notification try { - int iconRes = R.mipmap.ic_launcher; // guaranteed to exist - + // Open app intent Intent openAppIntent = new Intent(this, MainActivity.class); int flags = PendingIntent.FLAG_UPDATE_CURRENT; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -187,6 +221,7 @@ private void showNotification() { } PendingIntent openPendingIntent = PendingIntent.getActivity(this, 0, openAppIntent, flags); + // Remote input for direct reply RemoteInput remoteInput = new RemoteInput.Builder(NoteReplyReceiver.KEY_TEXT_REPLY) .setLabel("Add Note") .build(); @@ -194,36 +229,55 @@ private void showNotification() { Intent replyIntent = new Intent(this, NoteReplyReceiver.class); PendingIntent replyPendingIntent = PendingIntent.getBroadcast(this, 1, replyIntent, flags); - Notification.Action replyAction = new Notification.Action.Builder( - iconRes, "Add Note", replyPendingIntent) - .addRemoteInput(remoteInput) - .build(); - - Notification notification; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - notification = new Notification.Builder(this, CHANNEL_ID) - .setSmallIcon(iconRes) - .setContentTitle("Quick Note") - .setContentText("Swipe down to add a note") - .setContentIntent(openPendingIntent) - .addAction(replyAction) - .setOngoing(true) + // Notification action with the generated icon + Notification.Action replyAction; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + replyAction = new Notification.Action.Builder( + getNotificationIcon(), // Icon object (safe) + "Add Note", + replyPendingIntent) + .addRemoteInput(remoteInput) .build(); } else { - notification = new Notification.Builder(this) - .setSmallIcon(iconRes) - .setContentTitle("Quick Note") - .setContentText("Swipe down to add a note") - .setContentIntent(openPendingIntent) - .addAction(replyAction) - .setOngoing(true) + // Fallback for API 21–22: use a built-in resource (no Icon class) + replyAction = new Notification.Action.Builder( + android.R.drawable.ic_menu_send, + "Add Note", + replyPendingIntent) + .addRemoteInput(remoteInput) .build(); } + // Build notification itself + Notification.Builder builder; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + builder = new Notification.Builder(this, CHANNEL_ID); + } else { + builder = new Notification.Builder(this); + } + + // Set small icon depending on API + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + builder.setSmallIcon(getNotificationIcon()); // Icon object + } else { + builder.setSmallIcon(android.R.drawable.ic_dialog_info); // built-in + } + + builder.setContentTitle("Quick Note") + .setContentText("Swipe down to add a note") + .setContentIntent(openPendingIntent) + .addAction(replyAction) + .setOngoing(true); + + Notification notification = builder.build(); manager.notify(NOTIFICATION_ID, notification); isNotificationActive = true; + } catch (Exception e) { - Toast.makeText(this, "Couldn't show notification: " + e.getMessage(), Toast.LENGTH_LONG).show(); + // Detailed error for debugging + String msg = e.getClass().getSimpleName() + ": " + e.getMessage(); + if (e.getCause() != null) msg += "\nCause: " + e.getCause().toString(); + Toast.makeText(this, "Couldn't show notification:\n" + msg, Toast.LENGTH_LONG).show(); e.printStackTrace(); } } @@ -369,7 +423,7 @@ public void importJsonFile() { public void toggleNotification(boolean enable) { runOnUiThread(() -> { if (enable) { - // Android 13+ (API 33) runtime permission for notifications + // Android 13+ runtime permission if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { @@ -395,7 +449,7 @@ public void setInboxTabName(String tabName) { } } - // Permission result (for notification on API 33+) + // Permission result @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == NOTIFICATION_PERMISSION_REQUEST) { From 1033de988d7d26a3d45678253f34b138fd7b6abb Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Fri, 7 Aug 2026 23:59:49 +0000 Subject: [PATCH 17/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index 20121f4..9860506 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From f6d31c6c9b199041db7381022da20697ca143d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 05:35:48 +0530 Subject: [PATCH 18/29] Create ic_note.xml --- app/src/main/res/drawable/ic_note.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 app/src/main/res/drawable/ic_note.xml diff --git a/app/src/main/res/drawable/ic_note.xml b/app/src/main/res/drawable/ic_note.xml new file mode 100644 index 0000000..6dc065f --- /dev/null +++ b/app/src/main/res/drawable/ic_note.xml @@ -0,0 +1,10 @@ + + + + From 695e8e61da9158631c738cd25de6d250cdd0da3b Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Sat, 8 Aug 2026 00:05:57 +0000 Subject: [PATCH 19/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index 9860506..7497bb0 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrapper...gradle-wrapper...gradle-wrapper...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrappe...gradle-wrappe...gradle-wrappe...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...drawable/i...drawable/i...drawable/i...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From 34a1dac09c5afb56401b4cda0e8fed38bcbffc0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 05:37:49 +0530 Subject: [PATCH 20/29] Icon vector added --- .../io/github/ronynn/karui/MainActivity.java | 86 +++++-------------- 1 file changed, 23 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/io/github/ronynn/karui/MainActivity.java b/app/src/main/java/io/github/ronynn/karui/MainActivity.java index d1527cc..36ad7b1 100644 --- a/app/src/main/java/io/github/ronynn/karui/MainActivity.java +++ b/app/src/main/java/io/github/ronynn/karui/MainActivity.java @@ -14,12 +14,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Build; @@ -138,32 +132,6 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC createNotificationChannel(); } - // ---------- NOTIFICATION ICON GENERATOR (no file required) ---------- - - /** - * Creates a simple white circle bitmap that works as a valid notification small icon. - */ - private Bitmap createNotificationIconBitmap() { - int size = 96; // 24dp * 4 for crisp edges - Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - paint.setColor(Color.WHITE); - canvas.drawCircle(size / 2f, size / 2f, size / 2f * 0.8f, paint); - return bitmap; - } - - /** - * Returns an Icon object (API 23+) or null for older APIs. - */ - @SuppressLint("NewApi") - private Icon getNotificationIcon() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - return Icon.createWithBitmap(createNotificationIconBitmap()); - } - return null; - } - // ---------- NOTIFICATION CHANNEL ---------- private void createNotificationChannel() { @@ -176,9 +144,8 @@ private void createNotificationChannel() { } } - // ---------- SHOW / CANCEL NOTIFICATION ---------- + // ---------- SHOW NOTIFICATION (safe, uses R.drawable.ic_note) ---------- - @SuppressLint("NewApi") private void showNotification() { // Ensure channel exists createNotificationChannel(); @@ -189,29 +156,23 @@ private void showNotification() { return; } - // Check global notification enablement (API 24+) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (!manager.areNotificationsEnabled()) { - Toast.makeText(this, "Notifications are disabled in system settings", Toast.LENGTH_LONG).show(); - // Open notification settings for the app - try { - Intent intent = new Intent(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - intent.setAction(android.provider.Settings.ACTION_APP_NOTIFICATION_SETTINGS); - intent.putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, getPackageName()); - } else { - intent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - intent.setData(Uri.parse("package:" + getPackageName())); - } - startActivity(intent); - } catch (Exception e) { - // ignore + // Check if notifications are enabled globally + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !manager.areNotificationsEnabled()) { + Toast.makeText(this, "Notifications are disabled. Please enable them in system settings.", Toast.LENGTH_LONG).show(); + try { + Intent intent = new Intent(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.setAction(android.provider.Settings.ACTION_APP_NOTIFICATION_SETTINGS); + intent.putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, getPackageName()); + } else { + intent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.parse("package:" + getPackageName())); } - return; - } + startActivity(intent); + } catch (Exception ignored) {} + return; } - // Build notification try { // Open app intent Intent openAppIntent = new Intent(this, MainActivity.class); @@ -229,26 +190,26 @@ private void showNotification() { Intent replyIntent = new Intent(this, NoteReplyReceiver.class); PendingIntent replyPendingIntent = PendingIntent.getBroadcast(this, 1, replyIntent, flags); - // Notification action with the generated icon + // Notification action with the drawable icon Notification.Action replyAction; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { replyAction = new Notification.Action.Builder( - getNotificationIcon(), // Icon object (safe) + Icon.createWithResource(this, R.drawable.ic_note), "Add Note", replyPendingIntent) .addRemoteInput(remoteInput) .build(); } else { - // Fallback for API 21–22: use a built-in resource (no Icon class) + // Pre-Marshmallow fallback replyAction = new Notification.Action.Builder( - android.R.drawable.ic_menu_send, + R.drawable.ic_note, "Add Note", replyPendingIntent) .addRemoteInput(remoteInput) .build(); } - // Build notification itself + // Build notification Notification.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder = new Notification.Builder(this, CHANNEL_ID); @@ -256,11 +217,11 @@ private void showNotification() { builder = new Notification.Builder(this); } - // Set small icon depending on API + // Set the small icon if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - builder.setSmallIcon(getNotificationIcon()); // Icon object + builder.setSmallIcon(Icon.createWithResource(this, R.drawable.ic_note)); } else { - builder.setSmallIcon(android.R.drawable.ic_dialog_info); // built-in + builder.setSmallIcon(R.drawable.ic_note); } builder.setContentTitle("Quick Note") @@ -274,7 +235,6 @@ private void showNotification() { isNotificationActive = true; } catch (Exception e) { - // Detailed error for debugging String msg = e.getClass().getSimpleName() + ": " + e.getMessage(); if (e.getCause() != null) msg += "\nCause: " + e.getCause().toString(); Toast.makeText(this, "Couldn't show notification:\n" + msg, Toast.LENGTH_LONG).show(); From 036403df330452bc1a7814e1d59ff2162705c138 Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Sat, 8 Aug 2026 00:07:59 +0000 Subject: [PATCH 21/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index 7497bb0..a68849f 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrappe...gradle-wrappe...gradle-wrappe...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...drawable/i...drawable/i...drawable/i...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrappe...gradle-wrappe...gradle-wrappe...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...drawable/i...drawable/i...drawable/i...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From 1dee63755359c7221b60a4b8d1fb856bd5ecd812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 05:43:17 +0530 Subject: [PATCH 22/29] Android 12+ requirements --- .../io/github/ronynn/karui/MainActivity.java | 817 ++++++++++-------- 1 file changed, 459 insertions(+), 358 deletions(-) diff --git a/app/src/main/java/io/github/ronynn/karui/MainActivity.java b/app/src/main/java/io/github/ronynn/karui/MainActivity.java index 36ad7b1..d0fc65e 100644 --- a/app/src/main/java/io/github/ronynn/karui/MainActivity.java +++ b/app/src/main/java/io/github/ronynn/karui/MainActivity.java @@ -37,387 +37,488 @@ import java.io.InputStreamReader; import java.io.OutputStream; -public class MainActivity extends Activity { - - private static final int CREATE_FILE_REQUEST_CODE = 1; - private static final int IMPORT_FILE_REQUEST_CODE = 2; - private static final int FILECHOOSER_RESULTCODE = 3; - private static final int NOTIFICATION_PERMISSION_REQUEST = 100; - private static final String CHANNEL_ID = "note_reply_channel"; - private static final int NOTIFICATION_ID = 1; - - private WebView mWebView; - private View splashScreen; - - private String pendingFileName; - private String pendingFileData; - private String pendingFileType; - - private ValueCallback mFilePathCallback; - - private boolean isNotificationActive = false; - private String inboxTabName = "Inbox"; - - @Override - @SuppressLint({"SetJavaScriptEnabled", "AllowFileAccess"}) - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - mWebView = findViewById(R.id.activity_main_webview); - splashScreen = findViewById(R.id.splash_screen); - - WebSettings webSettings = mWebView.getSettings(); - webSettings.setJavaScriptEnabled(true); - webSettings.setDomStorageEnabled(true); - webSettings.setAllowFileAccess(true); - webSettings.setAllowContentAccess(true); - webSettings.setAllowFileAccessFromFileURLs(true); - webSettings.setAllowUniversalAccessFromFileURLs(true); - - CookieManager cookieManager = CookieManager.getInstance(); - cookieManager.setAcceptCookie(true); - cookieManager.setAcceptThirdPartyCookies(mWebView, true); - - mWebView.addJavascriptInterface(new WebAppInterface(), "Android"); - - mWebView.setWebViewClient(new WebViewClient() { - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - try { - view.getContext().startActivity(intent); - } catch (ActivityNotFoundException e) { - Toast.makeText(view.getContext(), R.string.no_app_to_open_link, Toast.LENGTH_SHORT).show(); - } - return true; - } - - @Override - public void onPageFinished(WebView view, String url) { - ObjectAnimator fadeOut = ObjectAnimator.ofFloat(splashScreen, "alpha", 1f, 0f); - fadeOut.setInterpolator(new DecelerateInterpolator()); - fadeOut.setDuration(500); - fadeOut.start(); - - splashScreen.setVisibility(View.GONE); - mWebView.setVisibility(View.VISIBLE); - } - }); - - mWebView.setWebChromeClient(new WebChromeClient() { - @Override - public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { - if (mFilePathCallback != null) { - mFilePathCallback.onReceiveValue(null); - } - mFilePathCallback = filePathCallback; - Intent intent = fileChooserParams.createIntent(); - try { - startActivityForResult(intent, FILECHOOSER_RESULTCODE); - } catch (Exception e) { - mFilePathCallback = null; - return false; - } - return true; - } - }); - - mWebView.loadUrl("file:///android_asset/index.html"); - - // Load persisted inbox tab name - SharedPreferences prefs = getSharedPreferences("note_queue", MODE_PRIVATE); - inboxTabName = prefs.getString("inbox_tab_name", "Inbox"); - - createNotificationChannel(); - } - - // ---------- NOTIFICATION CHANNEL ---------- - - private void createNotificationChannel() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationChannel channel = new NotificationChannel( - CHANNEL_ID, "Quick Note", NotificationManager.IMPORTANCE_LOW); - channel.setDescription("Add notes from status bar"); - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (manager != null) manager.createNotificationChannel(channel); +public class MainActivity extends Activity +{ + private static final int CREATE_FILE_REQUEST_CODE = 1; + private static final int IMPORT_FILE_REQUEST_CODE = 2; + private static final int FILECHOOSER_RESULTCODE = 3; + private static final int NOTIFICATION_PERMISSION_REQUEST = 100; + private static final String CHANNEL_ID = "note_reply_channel"; + private static final int NOTIFICATION_ID = 1; + + private WebView mWebView; + private View splashScreen; + + private String pendingFileName; + private String pendingFileData; + private String pendingFileType; + + private ValueCallback mFilePathCallback; + + private boolean isNotificationActive = false; + private String inboxTabName = "Inbox"; + + @Override + @SuppressLint({"SetJavaScriptEnabled", "AllowFileAccess"}) + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + mWebView = findViewById(R.id.activity_main_webview); + splashScreen = findViewById(R.id.splash_screen); + + WebSettings webSettings = mWebView.getSettings(); + webSettings.setJavaScriptEnabled(true); + webSettings.setDomStorageEnabled(true); + webSettings.setAllowFileAccess(true); + webSettings.setAllowContentAccess(true); + webSettings.setAllowFileAccessFromFileURLs(true); + webSettings.setAllowUniversalAccessFromFileURLs(true); + + CookieManager cookieManager = CookieManager.getInstance(); + cookieManager.setAcceptCookie(true); + cookieManager.setAcceptThirdPartyCookies(mWebView, true); + + mWebView.addJavascriptInterface(new WebAppInterface(), "Android"); + + mWebView.setWebViewClient(new WebViewClient() + { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) + { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + try + { + view.getContext().startActivity(intent); } - } - - // ---------- SHOW NOTIFICATION (safe, uses R.drawable.ic_note) ---------- - - private void showNotification() { - // Ensure channel exists - createNotificationChannel(); - - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (manager == null) { - Toast.makeText(this, "Notification service unavailable", Toast.LENGTH_SHORT).show(); - return; + catch (ActivityNotFoundException e) + { + Toast.makeText(view.getContext(), R.string.no_app_to_open_link, Toast.LENGTH_SHORT).show(); } - - // Check if notifications are enabled globally - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !manager.areNotificationsEnabled()) { - Toast.makeText(this, "Notifications are disabled. Please enable them in system settings.", Toast.LENGTH_LONG).show(); - try { - Intent intent = new Intent(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - intent.setAction(android.provider.Settings.ACTION_APP_NOTIFICATION_SETTINGS); - intent.putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, getPackageName()); - } else { - intent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - intent.setData(Uri.parse("package:" + getPackageName())); - } - startActivity(intent); - } catch (Exception ignored) {} - return; + return true; + } + + @Override + public void onPageFinished(WebView view, String url) + { + ObjectAnimator fadeOut = ObjectAnimator.ofFloat(splashScreen, "alpha", 1f, 0f); + fadeOut.setInterpolator(new DecelerateInterpolator()); + fadeOut.setDuration(500); + fadeOut.start(); + + splashScreen.setVisibility(View.GONE); + mWebView.setVisibility(View.VISIBLE); + } + }); + + mWebView.setWebChromeClient(new WebChromeClient() + { + @Override + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) + { + if (mFilePathCallback != null) + { + mFilePathCallback.onReceiveValue(null); } - - try { - // Open app intent - Intent openAppIntent = new Intent(this, MainActivity.class); - int flags = PendingIntent.FLAG_UPDATE_CURRENT; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - flags |= PendingIntent.FLAG_IMMUTABLE; - } - PendingIntent openPendingIntent = PendingIntent.getActivity(this, 0, openAppIntent, flags); - - // Remote input for direct reply - RemoteInput remoteInput = new RemoteInput.Builder(NoteReplyReceiver.KEY_TEXT_REPLY) - .setLabel("Add Note") - .build(); - - Intent replyIntent = new Intent(this, NoteReplyReceiver.class); - PendingIntent replyPendingIntent = PendingIntent.getBroadcast(this, 1, replyIntent, flags); - - // Notification action with the drawable icon - Notification.Action replyAction; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - replyAction = new Notification.Action.Builder( - Icon.createWithResource(this, R.drawable.ic_note), - "Add Note", - replyPendingIntent) - .addRemoteInput(remoteInput) - .build(); - } else { - // Pre-Marshmallow fallback - replyAction = new Notification.Action.Builder( - R.drawable.ic_note, - "Add Note", - replyPendingIntent) - .addRemoteInput(remoteInput) - .build(); - } - - // Build notification - Notification.Builder builder; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - builder = new Notification.Builder(this, CHANNEL_ID); - } else { - builder = new Notification.Builder(this); - } - - // Set the small icon - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - builder.setSmallIcon(Icon.createWithResource(this, R.drawable.ic_note)); - } else { - builder.setSmallIcon(R.drawable.ic_note); - } - - builder.setContentTitle("Quick Note") - .setContentText("Swipe down to add a note") - .setContentIntent(openPendingIntent) - .addAction(replyAction) - .setOngoing(true); - - Notification notification = builder.build(); - manager.notify(NOTIFICATION_ID, notification); - isNotificationActive = true; - - } catch (Exception e) { - String msg = e.getClass().getSimpleName() + ": " + e.getMessage(); - if (e.getCause() != null) msg += "\nCause: " + e.getCause().toString(); - Toast.makeText(this, "Couldn't show notification:\n" + msg, Toast.LENGTH_LONG).show(); - e.printStackTrace(); + mFilePathCallback = filePathCallback; + Intent intent = fileChooserParams.createIntent(); + try + { + startActivityForResult(intent, FILECHOOSER_RESULTCODE); + } + catch (Exception e) + { + mFilePathCallback = null; + return false; } + return true; + } + }); + + mWebView.loadUrl("file:///android_asset/index.html"); + + SharedPreferences prefs = getSharedPreferences("note_queue", MODE_PRIVATE); + inboxTabName = prefs.getString("inbox_tab_name", "Inbox"); + + createNotificationChannel(); + } + + // ---------- NOTIFICATION HANDLING ---------- + + private void createNotificationChannel() + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + { + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, "Quick Note", NotificationManager.IMPORTANCE_LOW); + channel.setDescription("Add notes from status bar"); + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager != null) + { + manager.createNotificationChannel(channel); + } } + } - private void cancelNotification() { - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (manager != null) manager.cancel(NOTIFICATION_ID); - isNotificationActive = false; + private void showNotification() + { + createNotificationChannel(); + + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager == null) + { + Toast.makeText(this, "Notification service unavailable", Toast.LENGTH_SHORT).show(); + return; } - // ---------- PENDING NOTES INJECTION ---------- - - private void injectPendingNotes() { - SharedPreferences prefs = getSharedPreferences("note_queue", MODE_PRIVATE); - String pendingNotes = prefs.getString("pending_notes", ""); - String pendingTabs = prefs.getString("pending_notes_tabs", ""); - if (pendingNotes.isEmpty()) return; - - prefs.edit().remove("pending_notes").remove("pending_notes_tabs").apply(); - - String[] notes = pendingNotes.split("\n"); - String[] tabs = pendingTabs.split("\n"); - - for (int i = 0; i < notes.length; i++) { - if (notes[i].trim().isEmpty()) continue; - String tabName = (i < tabs.length) ? tabs[i] : inboxTabName; - String escapedNote = notes[i] - .replace("\\", "\\\\") - .replace("'", "\\'") - .replace("\n", "\\n"); - String escapedTab = tabName - .replace("\\", "\\\\") - .replace("'", "\\'"); - String js = String.format("syncNoteFromAndroid('%s', '%s')", escapedNote, escapedTab); - mWebView.evaluateJavascript(js, null); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !manager.areNotificationsEnabled()) + { + Toast.makeText(this, "Notifications are disabled. Please enable them in system settings.", Toast.LENGTH_LONG).show(); + try + { + Intent intent = new Intent(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + { + intent.setAction(android.provider.Settings.ACTION_APP_NOTIFICATION_SETTINGS); + intent.putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, getPackageName()); + } + else + { + intent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.parse("package:" + getPackageName())); } + startActivity(intent); + } + catch (Exception ignored) + { + } + return; } - @Override - protected void onResume() { - super.onResume(); - injectPendingNotes(); + try + { + Intent openAppIntent = new Intent(this, MainActivity.class); + int openFlags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + { + openFlags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent openPendingIntent = PendingIntent.getActivity(this, 0, openAppIntent, openFlags); + + RemoteInput remoteInput = new RemoteInput.Builder(NoteReplyReceiver.KEY_TEXT_REPLY) + .setLabel("Add Note") + .build(); + + Intent replyIntent = new Intent(this, NoteReplyReceiver.class); + int replyFlags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + { + replyFlags |= PendingIntent.FLAG_MUTABLE; + } + else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + { + replyFlags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent replyPendingIntent = PendingIntent.getBroadcast(this, 1, replyIntent, replyFlags); + + Notification.Action replyAction; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + { + replyAction = new Notification.Action.Builder( + Icon.createWithResource(this, R.drawable.ic_note), + "Add Note", + replyPendingIntent) + .addRemoteInput(remoteInput) + .build(); + } + else + { + replyAction = new Notification.Action.Builder( + R.drawable.ic_note, + "Add Note", + replyPendingIntent) + .addRemoteInput(remoteInput) + .build(); + } + + Notification.Builder builder; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + { + builder = new Notification.Builder(this, CHANNEL_ID); + } + else + { + builder = new Notification.Builder(this); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + { + builder.setSmallIcon(Icon.createWithResource(this, R.drawable.ic_note)); + } + else + { + builder.setSmallIcon(R.drawable.ic_note); + } + + builder.setContentTitle("Quick Note") + .setContentText("Swipe down to add a note") + .setContentIntent(openPendingIntent) + .addAction(replyAction) + .setOngoing(true); + + Notification notification = builder.build(); + manager.notify(NOTIFICATION_ID, notification); + isNotificationActive = true; } - - // ---------- ACTIVITY RESULT ---------- - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (requestCode == CREATE_FILE_REQUEST_CODE && resultCode == RESULT_OK) { - if (data != null && data.getData() != null && pendingFileData != null) { - try { - OutputStream outputStream = getContentResolver().openOutputStream(data.getData()); - if (outputStream != null) { - outputStream.write(pendingFileData.getBytes()); - outputStream.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } else if (requestCode == IMPORT_FILE_REQUEST_CODE && resultCode == RESULT_OK) { - if (data != null && data.getData() != null) { - try { - InputStream inputStream = getContentResolver().openInputStream(data.getData()); - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); - StringBuilder sb = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - sb.append(line); - } - reader.close(); - String jsonContent = sb.toString(); - - String jsCode = "if(window.setAndroidNotes) window.setAndroidNotes(" + JSONObject.quote(jsonContent) + ");"; - mWebView.evaluateJavascript(jsCode, null); - } catch (IOException e) { - e.printStackTrace(); - } - } - } else if (requestCode == FILECHOOSER_RESULTCODE) { - if (mFilePathCallback == null) return; - Uri[] results = null; - if (resultCode == Activity.RESULT_OK && data != null) { - String dataString = data.getDataString(); - if (dataString != null) { - results = new Uri[]{Uri.parse(dataString)}; - } else if (data.getClipData() != null) { - int count = data.getClipData().getItemCount(); - results = new Uri[count]; - for (int i = 0; i < count; i++) { - results[i] = data.getClipData().getItemAt(i).getUri(); - } - } - } - mFilePathCallback.onReceiveValue(results); - mFilePathCallback = null; - } + catch (Exception e) + { + String msg = e.getClass().getSimpleName() + ": " + e.getMessage(); + if (e.getCause() != null) + { + msg += "\nCause: " + e.getCause().toString(); + } + Toast.makeText(this, "Couldn't show notification:\n" + msg, Toast.LENGTH_LONG).show(); + e.printStackTrace(); } - - @Override - public void onBackPressed() { - if (mWebView.canGoBack()) { - mWebView.goBack(); - } else { - super.onBackPressed(); - } + } + + private void cancelNotification() + { + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (manager != null) + { + manager.cancel(NOTIFICATION_ID); } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (mWebView != null) mWebView.destroy(); + isNotificationActive = false; + } + + // ---------- DATA INJECTION ---------- + + private void injectPendingNotes() + { + SharedPreferences prefs = getSharedPreferences("note_queue", MODE_PRIVATE); + String pendingNotes = prefs.getString("pending_notes", ""); + String pendingTabs = prefs.getString("pending_notes_tabs", ""); + if (pendingNotes.isEmpty()) + { + return; } - // ---------- JAVASCRIPT INTERFACE ---------- - - public class WebAppInterface { - - @JavascriptInterface - public void saveFile(String fileName, String fileData, String fileType) { - pendingFileName = fileName; - pendingFileData = fileData; - pendingFileType = fileType; - - Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType(fileType); - intent.putExtra(Intent.EXTRA_TITLE, fileName); - startActivityForResult(intent, CREATE_FILE_REQUEST_CODE); + prefs.edit().remove("pending_notes").remove("pending_notes_tabs").apply(); + + String[] notes = pendingNotes.split("\n"); + String[] tabs = pendingTabs.split("\n"); + + for (int i = 0; i < notes.length; i++) + { + if (notes[i].trim().isEmpty()) + { + continue; + } + String tabName = (i < tabs.length) ? tabs[i] : inboxTabName; + String escapedNote = notes[i] + .replace("\\", "\\\\") + .replace("'", "\\'") + .replace("\n", "\\n"); + String escapedTab = tabName + .replace("\\", "\\\\") + .replace("'", "\\'"); + String js = String.format("syncNoteFromAndroid('%s', '%s')", escapedNote, escapedTab); + mWebView.evaluateJavascript(js, null); + } + } + + @Override + protected void onResume() + { + super.onResume(); + injectPendingNotes(); + } + + // ---------- ACTIVITY RESULTS & LIFECYCLE ---------- + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) + { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == CREATE_FILE_REQUEST_CODE && resultCode == RESULT_OK) + { + if (data != null && data.getData() != null && pendingFileData != null) + { + try + { + OutputStream outputStream = getContentResolver().openOutputStream(data.getData()); + if (outputStream != null) + { + outputStream.write(pendingFileData.getBytes()); + outputStream.close(); + } } - - @JavascriptInterface - public void importJsonFile() { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("application/json"); - startActivityForResult(intent, IMPORT_FILE_REQUEST_CODE); + catch (IOException e) + { + e.printStackTrace(); } - - @JavascriptInterface - public void toggleNotification(boolean enable) { - runOnUiThread(() -> { - if (enable) { - // Android 13+ runtime permission - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) - != PackageManager.PERMISSION_GRANTED) { - requestPermissions( - new String[]{Manifest.permission.POST_NOTIFICATIONS}, - NOTIFICATION_PERMISSION_REQUEST); - return; - } - } - showNotification(); - } else { - cancelNotification(); - } - }); + } + } + else if (requestCode == IMPORT_FILE_REQUEST_CODE && resultCode == RESULT_OK) + { + if (data != null && data.getData() != null) + { + try + { + InputStream inputStream = getContentResolver().openInputStream(data.getData()); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + reader.close(); + String jsonContent = sb.toString(); + + String jsCode = "if(window.setAndroidNotes) window.setAndroidNotes(" + JSONObject.quote(jsonContent) + ");"; + mWebView.evaluateJavascript(jsCode, null); } - - @JavascriptInterface - public void setInboxTabName(String tabName) { - if (tabName == null || tabName.trim().isEmpty()) tabName = "Inbox"; - inboxTabName = tabName.trim(); - getSharedPreferences("note_queue", MODE_PRIVATE) - .edit().putString("inbox_tab_name", inboxTabName).apply(); + catch (IOException e) + { + e.printStackTrace(); + } + } + } + else if (requestCode == FILECHOOSER_RESULTCODE) + { + if (mFilePathCallback == null) + { + return; + } + Uri[] results = null; + if (resultCode == Activity.RESULT_OK && data != null) + { + String dataString = data.getDataString(); + if (dataString != null) + { + results = new Uri[]{Uri.parse(dataString)}; + } + else if (data.getClipData() != null) + { + int count = data.getClipData().getItemCount(); + results = new Uri[count]; + for (int i = 0; i < count; i++) + { + results[i] = data.getClipData().getItemAt(i).getUri(); + } } + } + mFilePathCallback.onReceiveValue(results); + mFilePathCallback = null; + } + } + + @Override + public void onBackPressed() + { + if (mWebView.canGoBack()) + { + mWebView.goBack(); + } + else + { + super.onBackPressed(); + } + } + + @Override + protected void onDestroy() + { + super.onDestroy(); + if (mWebView != null) + { + mWebView.destroy(); + } + } + + // ---------- JAVASCRIPT INTERFACE ---------- + + public class WebAppInterface + { + @JavascriptInterface + public void saveFile(String fileName, String fileData, String fileType) + { + pendingFileName = fileName; + pendingFileData = fileData; + pendingFileType = fileType; + + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType(fileType); + intent.putExtra(Intent.EXTRA_TITLE, fileName); + startActivityForResult(intent, CREATE_FILE_REQUEST_CODE); + } + + @JavascriptInterface + public void importJsonFile() + { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("application/json"); + startActivityForResult(intent, IMPORT_FILE_REQUEST_CODE); } - // Permission result - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - if (requestCode == NOTIFICATION_PERMISSION_REQUEST) { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - showNotification(); - } else { - Toast.makeText(this, "Notification permission denied", Toast.LENGTH_SHORT).show(); + @JavascriptInterface + public void toggleNotification(boolean enable) + { + runOnUiThread(() -> { + if (enable) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + { + if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) + != PackageManager.PERMISSION_GRANTED) + { + requestPermissions( + new String[]{Manifest.permission.POST_NOTIFICATIONS}, + NOTIFICATION_PERMISSION_REQUEST); + return; } + } + showNotification(); + } + else + { + cancelNotification(); } + }); + } + + @JavascriptInterface + public void setInboxTabName(String tabName) + { + if (tabName == null || tabName.trim().isEmpty()) + { + tabName = "Inbox"; + } + inboxTabName = tabName.trim(); + getSharedPreferences("note_queue", MODE_PRIVATE) + .edit().putString("inbox_tab_name", inboxTabName).apply(); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) + { + if (requestCode == NOTIFICATION_PERMISSION_REQUEST) + { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) + { + showNotification(); + } + else + { + Toast.makeText(this, "Notification permission denied", Toast.LENGTH_SHORT).show(); + } } + } } From 4b1e82a310e85d6236999a619cf5e2f1e752564d Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Sat, 8 Aug 2026 00:13:26 +0000 Subject: [PATCH 23/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index a68849f..47603b6 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrappe...gradle-wrappe...gradle-wrappe...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...drawable/i...drawable/i...drawable/i...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrappe...gradle-wrappe...gradle-wrappe...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...drawable/i...drawable/i...drawable/i...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From 3b61840ed882ca21b1d063a7c61ca2b45ba4954c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 05:59:57 +0530 Subject: [PATCH 24/29] Update NoteReplyReceiver.java --- .../ronynn/karui/NoteReplyReceiver.java | 158 +++++++++++++++--- 1 file changed, 133 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java b/app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java index f587cb8..c4b507e 100644 --- a/app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java +++ b/app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java @@ -1,37 +1,145 @@ package io.github.ronynn.karui; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.app.RemoteInput; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.graphics.drawable.Icon; +import android.os.Build; import android.os.Bundle; -public class NoteReplyReceiver extends BroadcastReceiver { - public static final String KEY_TEXT_REPLY = "key_text_reply"; - - @Override - public void onReceive(Context context, Intent intent) { - Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); - if (remoteInput != null) { - CharSequence replyText = remoteInput.getCharSequence(KEY_TEXT_REPLY); - if (replyText != null && replyText.length() > 0) { - SharedPreferences prefs = context.getSharedPreferences("note_queue", Context.MODE_PRIVATE); - String existingNotes = prefs.getString("pending_notes", ""); - String existingTabs = prefs.getString("pending_notes_tabs", ""); - - // Read the current inbox tab name (default "Inbox") - String inboxTab = prefs.getString("inbox_tab_name", "Inbox"); - - String newNote = replyText.toString(); - String updatedNotes = existingNotes.isEmpty() ? newNote : existingNotes + "\n" + newNote; - String updatedTabs = existingTabs.isEmpty() ? inboxTab : existingTabs + "\n" + inboxTab; - - prefs.edit() - .putString("pending_notes", updatedNotes) - .putString("pending_notes_tabs", updatedTabs) - .apply(); - } +public class NoteReplyReceiver extends BroadcastReceiver +{ + public static final String KEY_TEXT_REPLY = "key_text_reply"; + private static final String CHANNEL_ID = "note_reply_channel"; + private static final int NOTIFICATION_ID = 1; + + @Override + public void onReceive(Context context, Intent intent) + { + Bundle remoteInputResult = RemoteInput.getResultsFromIntent(intent); + if (remoteInputResult != null) + { + CharSequence input = remoteInputResult.getCharSequence(KEY_TEXT_REPLY); + if (input != null && input.length() > 0) + { + String noteText = input.toString().trim(); + if (!noteText.isEmpty()) + { + saveNoteToPreferences(context, noteText); } + } + } + + // Re-publishing notification stops the spinning loader in status bar + updateNotification(context); + } + + private void saveNoteToPreferences(Context context, String noteText) + { + SharedPreferences prefs = context.getSharedPreferences("note_queue", Context.MODE_PRIVATE); + String existingNotes = prefs.getString("pending_notes", ""); + String existingTabs = prefs.getString("pending_notes_tabs", ""); + String inboxTab = prefs.getString("inbox_tab_name", "Inbox"); + + if (existingNotes.isEmpty()) + { + existingNotes = noteText; + existingTabs = inboxTab; + } + else + { + existingNotes += "\n" + noteText; + existingTabs += "\n" + inboxTab; + } + + prefs.edit() + .putString("pending_notes", existingNotes) + .putString("pending_notes_tabs", existingTabs) + .apply(); + } + + private void updateNotification(Context context) + { + NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + if (manager == null) + { + return; + } + + Intent openAppIntent = new Intent(context, MainActivity.class); + int openFlags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + { + openFlags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent openPendingIntent = PendingIntent.getActivity(context, 0, openAppIntent, openFlags); + + RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY) + .setLabel("Add Note") + .build(); + + Intent replyIntent = new Intent(context, NoteReplyReceiver.class); + int replyFlags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + { + replyFlags |= PendingIntent.FLAG_MUTABLE; + } + else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + { + replyFlags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent replyPendingIntent = PendingIntent.getBroadcast(context, 1, replyIntent, replyFlags); + + Notification.Action replyAction; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + { + replyAction = new Notification.Action.Builder( + Icon.createWithResource(context, R.drawable.ic_note), + "Add Note", + replyPendingIntent) + .addRemoteInput(remoteInput) + .build(); + } + else + { + replyAction = new Notification.Action.Builder( + R.drawable.ic_note, + "Add Note", + replyPendingIntent) + .addRemoteInput(remoteInput) + .build(); + } + + Notification.Builder builder; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + { + builder = new Notification.Builder(context, CHANNEL_ID); } + else + { + builder = new Notification.Builder(context); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + { + builder.setSmallIcon(Icon.createWithResource(context, R.drawable.ic_note)); + } + else + { + builder.setSmallIcon(R.drawable.ic_note); + } + + builder.setContentTitle("Quick Note") + .setContentText("Note saved! Swipe down to add another") + .setContentIntent(openPendingIntent) + .addAction(replyAction) + .setOngoing(true); + + manager.notify(NOTIFICATION_ID, builder.build()); + } } From 94e38bb5d101dd20891a6db889834349d589e207 Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Sat, 8 Aug 2026 00:30:05 +0000 Subject: [PATCH 25/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index 47603b6..8d12281 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrappe...gradle-wrappe...gradle-wrappe...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewgradlew.batgradlew.batgradlew.batLaterData...LaterData...LaterData...MainScree...MainScree...MainScree...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...drawable/i...drawable/i...drawable/i...MainActivity.javaMainActivity.javaMainActivity.java.bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrappe...gradle-wrappe...gradle-wrappe...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewLaterData...LaterData...LaterData...MainScre...MainScre...MainScre...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...drawable/i...drawable/i...drawable/i...MainActivity.javaMainActivity.javaMainActivity.javaNoteReplyR...NoteReplyR...NoteReplyR....bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From dbe547fe8b56d666f3e6f406fa8d6f2e364b4f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 06:13:09 +0530 Subject: [PATCH 26/29] Update NoteReplyReceiver.java --- .../main/java/io/github/ronynn/karui/NoteReplyReceiver.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java b/app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java index c4b507e..f805673 100644 --- a/app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java +++ b/app/src/main/java/io/github/ronynn/karui/NoteReplyReceiver.java @@ -31,11 +31,14 @@ public void onReceive(Context context, Intent intent) if (!noteText.isEmpty()) { saveNoteToPreferences(context, noteText); + + Intent updateIntent = new Intent(MainActivity.ACTION_NOTE_ADDED); + updateIntent.setPackage(context.getPackageName()); + context.sendBroadcast(updateIntent); } } } - // Re-publishing notification stops the spinning loader in status bar updateNotification(context); } From ec5d4872eacf5adca1d5b509820c0de236e72c43 Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Sat, 8 Aug 2026 00:43:19 +0000 Subject: [PATCH 27/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index 8d12281..8e31c6f 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrappe...gradle-wrappe...gradle-wrappe...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewLaterData...LaterData...LaterData...MainScre...MainScre...MainScre...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...drawable/i...drawable/i...drawable/i...MainActivity.javaMainActivity.javaMainActivity.javaNoteReplyR...NoteReplyR...NoteReplyR....bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrappe...gradle-wrappe...gradle-wrappe...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewLaterData...LaterData...LaterData...MainScre...MainScre...MainScre...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...drawable/i...drawable/i...drawable/i...MainActivity.javaMainActivity.javaMainActivity.javaNoteReplyR...NoteReplyR...NoteReplyR....bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file From 295355dad463b4cfe1e1bba5691399523b6660d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ronynn=20=CA=95=20=E2=80=A2=E1=B4=A5=E2=80=A2=CA=94?= <82273354+ronynn@users.noreply.github.com> Date: Sat, 8 Aug 2026 06:13:44 +0530 Subject: [PATCH 28/29] Instant note updates --- .../io/github/ronynn/karui/MainActivity.java | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/ronynn/karui/MainActivity.java b/app/src/main/java/io/github/ronynn/karui/MainActivity.java index d0fc65e..af2b341 100644 --- a/app/src/main/java/io/github/ronynn/karui/MainActivity.java +++ b/app/src/main/java/io/github/ronynn/karui/MainActivity.java @@ -10,8 +10,10 @@ import android.app.PendingIntent; import android.app.RemoteInput; import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.drawable.Icon; @@ -39,6 +41,8 @@ public class MainActivity extends Activity { + public static final String ACTION_NOTE_ADDED = "io.github.ronynn.karui.ACTION_NOTE_ADDED"; + private static final int CREATE_FILE_REQUEST_CODE = 1; private static final int IMPORT_FILE_REQUEST_CODE = 2; private static final int FILECHOOSER_RESULTCODE = 3; @@ -56,8 +60,21 @@ public class MainActivity extends Activity private ValueCallback mFilePathCallback; private boolean isNotificationActive = false; + private boolean isPageLoaded = false; private String inboxTabName = "Inbox"; + private final BroadcastReceiver noteReceiver = new BroadcastReceiver() + { + @Override + public void onReceive(Context context, Intent intent) + { + if (isPageLoaded) + { + injectPendingNotes(); + } + } + }; + @Override @SuppressLint({"SetJavaScriptEnabled", "AllowFileAccess"}) protected void onCreate(Bundle savedInstanceState) @@ -109,6 +126,9 @@ public void onPageFinished(WebView view, String url) splashScreen.setVisibility(View.GONE); mWebView.setVisibility(View.VISIBLE); + + isPageLoaded = true; + injectPendingNotes(); } }); @@ -142,6 +162,15 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC inboxTabName = prefs.getString("inbox_tab_name", "Inbox"); createNotificationChannel(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + { + registerReceiver(noteReceiver, new IntentFilter(ACTION_NOTE_ADDED), Context.RECEIVER_NOT_EXPORTED); + } + else + { + registerReceiver(noteReceiver, new IntentFilter(ACTION_NOTE_ADDED)); + } } // ---------- NOTIFICATION HANDLING ---------- @@ -297,6 +326,11 @@ private void cancelNotification() private void injectPendingNotes() { + if (!isPageLoaded) + { + return; + } + SharedPreferences prefs = getSharedPreferences("note_queue", MODE_PRIVATE); String pendingNotes = prefs.getString("pending_notes", ""); String pendingTabs = prefs.getString("pending_notes_tabs", ""); @@ -333,7 +367,10 @@ private void injectPendingNotes() protected void onResume() { super.onResume(); - injectPendingNotes(); + if (isPageLoaded) + { + injectPendingNotes(); + } } // ---------- ACTIVITY RESULTS & LIFECYCLE ---------- @@ -434,6 +471,14 @@ public void onBackPressed() protected void onDestroy() { super.onDestroy(); + try + { + unregisterReceiver(noteReceiver); + } + catch (Exception ignored) + { + } + if (mWebView != null) { mWebView.destroy(); From 73ebe690a06fe84523e6194fbb303eb60eb85064 Mon Sep 17 00:00:00 2001 From: repo-visualizer Date: Sat, 8 Aug 2026 00:43:53 +0000 Subject: [PATCH 29/29] Repo visualizer: update diagram --- diagram.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagram.svg b/diagram.svg index 8e31c6f..99120cb 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1 +1 @@ -srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrappe...gradle-wrappe...gradle-wrappe...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewLaterData...LaterData...LaterData...MainScre...MainScre...MainScre...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...drawable/i...drawable/i...drawable/i...MainActivity.javaMainActivity.javaMainActivity.javaNoteReplyR...NoteReplyR...NoteReplyR....bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file +srcsrcgradle/wrappergradle/wrapperfastlane/metadata/android/en-USfastlane/metadata/android/en-USappappcomponentscomponentsassets_archiveassets_archiveassetsassetsimagesimageschangelogschangelogssrc/mainsrc/mainresresjava/io/github/ronynn/karuijava/io/github/ronynn/karuivaluesvaluesApp.svelteApp.svelteApp.sveltelib/Counte...lib/Counte...lib/Counte...gradle-wrappe...gradle-wrappe...gradle-wrappe...README.mdREADME.mdREADME.mdLICENSELICENSELICENSEgradlewgradlewgradlewLaterData...LaterData...LaterData...MainScre...MainScre...MainScre...alpine.cdn.min.jsalpine.cdn.min.jsalpine.cdn.min.jsmilligram.cssmilligram.cssmilligram.cssmain.jsmain.jsmain.jsindex.htmlindex.htmlindex.htmlnormalize.cssnormalize.cssnormalize.cssmilligram.cssmilligram.cssmilligram.cssnormalize.cssnormalize.cssnormalize.cssassets/ind...assets/ind...assets/ind...mipmap-any...mipmap-any...mipmap-any...layout/act...layout/act...layout/act...drawable/i...drawable/i...drawable/i...MainActivity.javaMainActivity.javaMainActivity.javaNoteReplyR...NoteReplyR...NoteReplyR....bat.css.gitignore.gradle.html.java.js.md.properties.res.svelte.svg.ts.txt.xmleach dot sized by file size \ No newline at end of file