diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-08 00:05:39 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-08 00:05:39 +0300 |
| commit | 3e4bacb7b939b1de98a2fb07115f638750637357 (patch) | |
| tree | 0897a6854d1c3ca3b1d0252657e992abbba061ba /android | |
| parent | 665b150727ee7259e398d38d1f6bc51b4c2d4a1c (diff) | |
Rewrite as Flutter app and rename to Quicklogmain
Drop the Go/Fyne implementation and replace it with a native Flutter app
targeting Android (primary) and Linux desktop (development). Rename
quicklogger -> quicklog throughout (project, applicationId, MethodChannel,
cache filename, AppBar title, Linux binary).
Storage on Android moves to /Android/data/org.buetow.quicklog/files/ so
MANAGE_EXTERNAL_STORAGE is no longer required. Share-intent receive is
preserved via a Kotlin ShareActivity that writes shared text to the app
cache, which Dart reads through a MethodChannel on app resume; MainActivity
also accepts SEND directly so launchers that prefer the main target work.
New since the Fyne version: an entry browser screen with read-only viewer
and long-press delete.
Cross-compiling to Android ARM from amd64 Linux no longer needs Docker /
fyne-cross / NDK -- flutter build apk emits ARM binaries directly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Diffstat (limited to 'android')
28 files changed, 368 insertions, 36 deletions
diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..be3943c --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml deleted file mode 100644 index 0e6115e..0000000 --- a/android/AndroidManifest.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="org.buetow.quicklogger"> - - <application> - <!-- Also accept SEND on the main Go activity (some launchers prefer direct targets) --> - <activity - android:name="org.golang.app.GoNativeActivity" - android:exported="true" - tools:node="merge"> - <intent-filter> - <action android:name="android.intent.action.SEND" /> - <category android:name="android.intent.category.DEFAULT" /> - <data android:mimeType="text/plain" /> - <data android:mimeType="text/*" /> - </intent-filter> - </activity> - - <!-- Share receiver activity: writes text to cache and launches main app --> - <activity - android:name=".ShareActivity" - android:exported="true" - android:excludeFromRecents="true" - android:launchMode="singleTask" - android:taskAffinity="" - android:theme="@android:style/Theme.Translucent.NoTitleBar"> - <intent-filter> - <action android:name="android.intent.action.SEND" /> - <category android:name="android.intent.category.DEFAULT" /> - <data android:mimeType="text/plain" /> - <data android:mimeType="text/*" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 0000000..3333b8a --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "org.buetow.quicklog" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "org.buetow.quicklog" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- The INTERNET permission is required for development. Specifically, + the Flutter tool needs it to communicate with the running application + to allow setting breakpoints, to provide hot reload, etc. + --> + <uses-permission android:name="android.permission.INTERNET"/> +</manifest> diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6eddff0 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,56 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + <application + android:label="Quicklog" + android:name="${applicationName}" + android:icon="@mipmap/ic_launcher"> + <activity + android:name=".MainActivity" + android:exported="true" + android:launchMode="singleTop" + android:taskAffinity="" + android:theme="@style/LaunchTheme" + android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" + android:hardwareAccelerated="true" + android:windowSoftInputMode="adjustResize"> + <meta-data + android:name="io.flutter.embedding.android.NormalTheme" + android:resource="@style/NormalTheme" + /> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + <!-- Some launchers prefer routing SEND directly to the main activity. --> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/plain"/> + <data android:mimeType="text/*"/> + </intent-filter> + </activity> + <!-- Translucent share receiver: writes shared text to cache then hands off to MainActivity. --> + <activity + android:name=".ShareActivity" + android:exported="true" + android:excludeFromRecents="true" + android:launchMode="singleTask" + android:taskAffinity="" + android:theme="@android:style/Theme.Translucent.NoTitleBar"> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/plain"/> + <data android:mimeType="text/*"/> + </intent-filter> + </activity> + <meta-data + android:name="flutterEmbedding" + android:value="2" /> + </application> + <queries> + <intent> + <action android:name="android.intent.action.PROCESS_TEXT"/> + <data android:mimeType="text/plain"/> + </intent> + </queries> +</manifest> diff --git a/android/app/src/main/kotlin/org/buetow/quicklog/MainActivity.kt b/android/app/src/main/kotlin/org/buetow/quicklog/MainActivity.kt new file mode 100644 index 0000000..426589f --- /dev/null +++ b/android/app/src/main/kotlin/org/buetow/quicklog/MainActivity.kt @@ -0,0 +1,56 @@ +package org.buetow.quicklog + +import android.content.Intent +import android.os.Bundle +import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel +import java.io.File + +class MainActivity : FlutterActivity() { + private val channelName = "org.buetow.quicklog/share" + private val cacheFilename = "quicklog-shared.txt" + + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channelName) + .setMethodCallHandler { call, result -> + when (call.method) { + "readSharedTextFromCache" -> result.success(readCache()) + "clearSharedTextCache" -> { + clearCache() + result.success(null) + } + else -> result.notImplemented() + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + captureSendIntent(intent) + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + captureSendIntent(intent) + } + + private fun captureSendIntent(intent: Intent?) { + if (intent?.action == Intent.ACTION_SEND && intent.type?.startsWith("text/") == true) { + intent.getStringExtra(Intent.EXTRA_TEXT)?.let { text -> + File(cacheDir, cacheFilename).writeText(text) + } + } + } + + private fun readCache(): String? { + val f = File(cacheDir, cacheFilename) + return if (f.exists()) f.readText() else null + } + + private fun clearCache() { + val f = File(cacheDir, cacheFilename) + if (f.exists()) f.delete() + } +} diff --git a/android/app/src/main/kotlin/org/buetow/quicklog/ShareActivity.kt b/android/app/src/main/kotlin/org/buetow/quicklog/ShareActivity.kt new file mode 100644 index 0000000..15261e6 --- /dev/null +++ b/android/app/src/main/kotlin/org/buetow/quicklog/ShareActivity.kt @@ -0,0 +1,54 @@ +package org.buetow.quicklog + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.util.Log +import java.io.File + +class ShareActivity : Activity() { + companion object { + private const val TAG = "QuicklogShare" + private const val CACHE_FILENAME = "quicklog-shared.txt" + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + handleShare(intent) + launchMain() + finish() + } + + private fun handleShare(intent: Intent?) { + if (intent == null) return + val type = intent.type + if (intent.action == Intent.ACTION_SEND && type != null && type.startsWith("text/")) { + val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT) + if (sharedText != null) { + try { + val f = File(cacheDir, CACHE_FILENAME) + f.writeText(sharedText) + Log.i(TAG, "Wrote shared text to ${f.absolutePath}") + } catch (e: Exception) { + Log.e(TAG, "Error writing shared text", e) + } + } + } + } + + private fun launchMain() { + try { + val launch = Intent(this, MainActivity::class.java).apply { + action = Intent.ACTION_MAIN + addCategory(Intent.CATEGORY_LAUNCHER) + flags = Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_SINGLE_TOP or + Intent.FLAG_ACTIVITY_CLEAR_TOP or + Intent.FLAG_ACTIVITY_REORDER_TO_FRONT + } + startActivity(launch) + } catch (t: Throwable) { + Log.e(TAG, "Failed to launch main activity", t) + } + } +} diff --git a/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..a454844 --- /dev/null +++ b/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png diff --git a/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..7a9bc40 --- /dev/null +++ b/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Modify this file to customize your launch splash screen --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="?android:colorBackground" /> + + <!-- You can insert your own image assets here --> + <!-- <item> + <bitmap + android:gravity="center" + android:src="@mipmap/launch_image" /> + </item> --> +</layer-list> diff --git a/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..7badce7 --- /dev/null +++ b/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..5ed2063 --- /dev/null +++ b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..089a22a --- /dev/null +++ b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Modify this file to customize your launch splash screen --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@android:color/white" /> + + <!-- You can insert your own image assets here --> + <!-- <item> + <bitmap + android:gravity="center" + android:src="@mipmap/launch_image" /> + </item> --> +</layer-list> diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..c79c58a --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/ic_launcher_background"/> + <foreground> + <inset + android:drawable="@drawable/ic_launcher_foreground" + android:inset="16%" /> + </foreground> +</adaptive-icon> diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..f75d22f --- /dev/null +++ b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..d24ba69 --- /dev/null +++ b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..b49236f --- /dev/null +++ b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..a20c849 --- /dev/null +++ b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..9a1fafa --- /dev/null +++ b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> + <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> + <!-- Show a splash screen on the activity. Automatically removed when + the Flutter engine draws its first frame --> + <item name="android:windowBackground">@drawable/launch_background</item> + </style> + <!-- Theme applied to the Android Window as soon as the process has started. + This theme determines the color of the Android Window while your + Flutter UI initializes, as well as behind your Flutter UI while its + running. + + This Theme is only used starting with V2 of Flutter's Android embedding. --> + <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> + <item name="android:windowBackground">?android:colorBackground</item> + </style> +</resources> diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..c5d5899 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="ic_launcher_background">#FFFFFF</color> +</resources>
\ No newline at end of file diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> + <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> + <!-- Show a splash screen on the activity. Automatically removed when + the Flutter engine draws its first frame --> + <item name="android:windowBackground">@drawable/launch_background</item> + </style> + <!-- Theme applied to the Android Window as soon as the process has started. + This theme determines the color of the Android Window while your + Flutter UI initializes, as well as behind your Flutter UI while its + running. + + This Theme is only used starting with V2 of Flutter's Android embedding. --> + <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> + <item name="android:windowBackground">?android:colorBackground</item> + </style> +</resources> diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- The INTERNET permission is required for development. Specifically, + the Flutter tool needs it to communicate with the running application + to allow setting breakpoints, to provide hot reload, etc. + --> + <uses-permission android:name="android.permission.INTERNET"/> +</manifest> diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..dbee657 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register<Delete>("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..fbee1d8 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e4ef43f --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 0000000..ca7fe06 --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.2.20" apply false +} + +include(":app") |
