prepare for multiple apps
@@ -0,0 +1,121 @@
|
||||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
id "org.jetbrains.kotlin.plugin.compose" version "2.3.10"
|
||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
}
|
||||
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file("local.properties")
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader("UTF-8") { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = "1"
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty("flutter.versionName")
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = "1.0"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "eu.weblibre.gecko"
|
||||
compileSdk = 36
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '17'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "eu.weblibre.gecko"
|
||||
// https://api.flutter.dev/flutter/widgets/AndroidView-class.html
|
||||
minSdkVersion = 26
|
||||
targetSdk = 36
|
||||
versionCode = flutterVersionCode.toInteger()
|
||||
versionName = flutterVersionName
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.glance:glance-appwidget:1.1.1'
|
||||
implementation 'com.google.android.material:material:1.13.0'
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose true
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias System.getenv("KEY_ALIAS")
|
||||
keyPassword System.getenv("KEY_PASSWORD")
|
||||
storeFile System.getenv("KEY_PATH") ? file(System.getenv("KEY_PATH")) : null
|
||||
storePassword System.getenv("KEY_PASSWORD")
|
||||
}
|
||||
}
|
||||
|
||||
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.release
|
||||
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
matchingFallbacks = ['release'] // Use on the "release" build type in dependencies (AARs)
|
||||
}
|
||||
}
|
||||
|
||||
bundle {
|
||||
// Profiler issues require us to temporarily package native code compressed to
|
||||
// match the previous APK packaging.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1865634
|
||||
packagingOptions {
|
||||
jniLibs {
|
||||
it.useLegacyPackaging = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
jniLibs {
|
||||
useLegacyPackaging true
|
||||
}
|
||||
}
|
||||
|
||||
//Exclude dependenciesInfo for f-droid
|
||||
dependenciesInfo {
|
||||
// Disables dependency metadata when building APKs.
|
||||
includeInApk = false
|
||||
// Disables dependency metadata when building Android App Bundles.
|
||||
includeInBundle = false
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
||||
|
||||
//Override version code for f-droid
|
||||
ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3]
|
||||
import com.android.build.OutputFile
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
|
||||
if (abiVersionCode != null) {
|
||||
output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
-dontobfuscate
|
||||
|
||||
####################################################################################################
|
||||
# Android and GeckoView built-ins
|
||||
####################################################################################################
|
||||
|
||||
-dontwarn android.**
|
||||
-dontwarn androidx.**
|
||||
-dontwarn com.google.**
|
||||
-dontwarn org.mozilla.geckoview.**
|
||||
|
||||
# Raptor now writes a *-config.yaml file to specify Gecko runtime settings (e.g. the profile dir). This
|
||||
# file gets deserialized into a DebugConfig object, which is why we need to keep this class
|
||||
# and its members.
|
||||
-keep class org.mozilla.gecko.util.DebugConfig { *; }
|
||||
|
||||
####################################################################################################
|
||||
# kotlinx.coroutines: use the fast service loader to init MainDispatcherLoader by including a rule
|
||||
# to rewrite this property to return true:
|
||||
# https://github.com/Kotlin/kotlinx.coroutines/blob/8c98180f177bbe4b26f1ed9685a9280fea648b9c/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt#L19
|
||||
#
|
||||
# R8 is expected to optimize the default implementation to avoid a performance issue but a bug in R8
|
||||
# as bundled with AGP v7.0.0 causes this optimization to fail so we use the fast service loader instead. See:
|
||||
# https://github.com/mozilla-mobile/focus-android/issues/5102#issuecomment-897854121
|
||||
#
|
||||
# The fast service loader appears to be as performant as the R8 optimization so it's not worth the
|
||||
# churn to later remove this workaround. If needed, the upstream fix is being handled in
|
||||
# https://issuetracker.google.com/issues/196302685
|
||||
####################################################################################################
|
||||
-assumenosideeffects class kotlinx.coroutines.internal.MainDispatcherLoader {
|
||||
boolean FAST_SERVICE_LOADER_ENABLED return true;
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
# REMOVE all Log messages except warnings and errors
|
||||
####################################################################################################
|
||||
-assumenosideeffects class android.util.Log {
|
||||
public static boolean isLoggable(java.lang.String, int);
|
||||
public static int v(...);
|
||||
public static int i(...);
|
||||
public static int d(...);
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
# Mozilla Application Services
|
||||
####################################################################################################
|
||||
|
||||
-keep class mozilla.appservices.** { *; }
|
||||
|
||||
####################################################################################################
|
||||
# ViewModels
|
||||
####################################################################################################
|
||||
|
||||
-keep class org.mozilla.fenix.**ViewModel { *; }
|
||||
|
||||
# Keep Android Lifecycle methods
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1596302
|
||||
-keep class androidx.lifecycle.** { *; }
|
||||
|
||||
-dontwarn java.beans.BeanInfo
|
||||
-dontwarn java.beans.FeatureDescriptor
|
||||
-dontwarn java.beans.IntrospectionException
|
||||
-dontwarn java.beans.Introspector
|
||||
-dontwarn java.beans.PropertyDescriptor
|
||||
|
||||
####################################################################################################
|
||||
# Checker Framework
|
||||
####################################################################################################
|
||||
|
||||
-dontwarn org.checkerframework.checker.nullness.qual.EnsuresNonNull
|
||||
-dontwarn org.checkerframework.checker.nullness.qual.EnsuresNonNullIf
|
||||
-dontwarn org.checkerframework.checker.nullness.qual.RequiresNonNull
|
||||
@@ -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>
|
||||
@@ -0,0 +1,289 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<!-- This is needed because the android.permission.CAMERA above automatically
|
||||
adds a requirements for camera hardware and we don't want add those restrictions -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera.autofocus"
|
||||
android:required="false" />
|
||||
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
|
||||
<!-- Fenix only uses this in debug manifest -->
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<!-- Needed for uploading media files on devices with Android 13 and later. -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
|
||||
|
||||
<!-- Needed to interact with all apps installed on a device -->
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
|
||||
<!-- Needed to support Credential Manager with Android 14 and later. -->
|
||||
<uses-permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ORIGIN" />
|
||||
<uses-permission android:name="android.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS" />
|
||||
|
||||
|
||||
<application
|
||||
android:label="WebLibre"
|
||||
android:name=".MyApplication"
|
||||
android:icon="@mipmap/launcher_icon"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.activities.NotificationActivity"
|
||||
android:theme="@style/Theme.AppCompat.Translucent"
|
||||
android:exported="false"
|
||||
android:launchMode="singleTop" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="WebLibre"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity="eu.weblibre.gecko.task"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- We do want to ahndle "open with" by ourselfs. Prevents automatically pushing routes. -->
|
||||
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<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"/>
|
||||
<category android:name="android.intent.category.APP_BROWSER"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="es.antonborri.home_widget.action.LAUNCH" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:mimeType="text/*" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.WEB_SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Intent router: receives all VIEW intents and routes Custom Tab / PWA to
|
||||
ExternalAppBrowserActivity, regular VIEW intents to MainActivity -->
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.activities.IntentReceiverActivity"
|
||||
android:theme="@style/Theme.AppCompat.Translucent"
|
||||
android:exported="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:relinquishTaskIdentity="true"
|
||||
android:taskAffinity="">
|
||||
|
||||
<!-- Required exactly like this to appear in both default app choosers and when explicitly browsing -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="mozilla.components.pwa.category.SHORTCUT" />
|
||||
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
</intent-filter>
|
||||
<!-- like above but specifying additional schemes. Not qualified for app chooser. -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:mimeType="text/html" />
|
||||
<data android:mimeType="text/plain" />
|
||||
<data android:mimeType="application/xhtml+xml" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:mimeType="application/pdf" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- PWA launch from home screen shortcut -->
|
||||
<intent-filter>
|
||||
<action android:name="mozilla.components.feature.pwa.VIEW_PWA" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="https" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Native activity for Custom Tabs and PWA browsing (non-Flutter) -->
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.activities.ExternalAppBrowserActivity"
|
||||
android:exported="false"
|
||||
android:taskAffinity=""
|
||||
android:documentLaunchMode="always"
|
||||
android:autoRemoveFromRecents="false"
|
||||
android:theme="@style/ExternalAppBrowserTheme"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:resizeableActivity="true"
|
||||
android:persistableMode="persistNever"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.activities.AuthCustomTabActivity"
|
||||
android:exported="false"
|
||||
android:taskAffinity=""
|
||||
android:autoRemoveFromRecents="false"
|
||||
android:theme="@style/ExternalAppBrowserTheme"
|
||||
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|layoutDirection|smallestScreenSize|screenLayout"
|
||||
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.activities.AuthIntentReceiverActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:theme="@style/AddonsActivityTheme"
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.AddonsActivity"
|
||||
android:label="@string/mozac_feature_addons_addons"
|
||||
android:exported="false"
|
||||
android:parentActivityName=".MainActivity" />
|
||||
|
||||
<activity
|
||||
android:theme="@style/AppTheme"
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.AddonDetailsActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/mozac_feature_addons_addons" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.InstalledAddonDetailsActivity"
|
||||
android:label="@string/mozac_feature_addons_addons"
|
||||
android:parentActivityName="eu.weblibre.flutter_mozilla_components.addons.AddonsActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.PermissionsDetailsActivity"
|
||||
android:label="@string/mozac_feature_addons_addons"
|
||||
android:exported="false"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.AddonInternalSettingsActivity"
|
||||
android:label="@string/mozac_feature_addons_addons"
|
||||
android:exported="false"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.WebExtensionActionPopupActivity"
|
||||
android:label="@string/mozac_feature_addons_addons"
|
||||
android:theme="@style/AppTheme"/>
|
||||
|
||||
<receiver
|
||||
android:name=".SearchBarWidgetReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/search_bar_widget_info" />
|
||||
</receiver>
|
||||
|
||||
|
||||
<service
|
||||
android:name="eu.weblibre.flutter_mozilla_components.services.CustomTabsService"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.support.customtabs.action.CustomTabsService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name="eu.weblibre.flutter_mozilla_components.services.DownloadService"
|
||||
android:foregroundServiceType="dataSync"
|
||||
android:exported="false" />
|
||||
|
||||
<service android:name="eu.weblibre.flutter_mozilla_components.services.MediaSessionService"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:exported="false" />
|
||||
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||
android:value="false" />
|
||||
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:scheme="*" />
|
||||
</intent>
|
||||
</queries>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||
|
||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||
<!-- This will show a lot of app actions in context menu when selecting text
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent>
|
||||
</queries> -->
|
||||
</manifest>
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2025 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.weblibre.gecko
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import io.flutter.embedding.android.FlutterFragmentActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.embedding.engine.FlutterEngineCache
|
||||
import io.flutter.embedding.engine.FlutterJNI
|
||||
import io.flutter.embedding.engine.dart.DartExecutor
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
class MainActivity: FlutterFragmentActivity() {
|
||||
companion object {
|
||||
private const val TAG = "MainActivity"
|
||||
}
|
||||
|
||||
private val TRIM_MEMORY_CHANNEL = "eu.weblibre.flutter_mozilla_components/trim_memory"
|
||||
private val ACTIVITY_CHANNEL = "eu.weblibre.gecko/activity"
|
||||
private val ENGINE_ID = "engine_id"
|
||||
private var trimMemoryChannel: MethodChannel? = null
|
||||
|
||||
private fun engineTag(engine: FlutterEngine?): String {
|
||||
return engine?.let { "0x${System.identityHashCode(it).toString(16)}" } ?: "null"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Log.d(TAG, "onCreate: savedInstanceState=${savedInstanceState != null}, " +
|
||||
"cachedEngine=${engineTag(FlutterEngineCache.getInstance().get(ENGINE_ID))}")
|
||||
|
||||
super.onCreate(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the FlutterEngine's native JNI layer is still attached.
|
||||
* Note: binaryMessenger.send() does NOT throw when JNI is detached — it just
|
||||
* logs a warning. We must use reflection to access FlutterJNI.isAttachedToJni().
|
||||
*/
|
||||
private fun isEngineNativeAlive(engine: FlutterEngine): Boolean {
|
||||
return try {
|
||||
val field = FlutterEngine::class.java.getDeclaredField("flutterJNI")
|
||||
field.isAccessible = true
|
||||
val jni = field.get(engine) as FlutterJNI
|
||||
jni.isAttached
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Could not check JNI attachment state: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
|
||||
trimMemoryChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, TRIM_MEMORY_CHANNEL)
|
||||
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, ACTIVITY_CHANNEL).setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"moveTaskToBack" -> {
|
||||
moveTaskToBack(true)
|
||||
result.success(null)
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
Log.d(TAG, "onPause")
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.d(TAG, "onDestroy: isFinishing=$isFinishing, " +
|
||||
"cachedEngine=${engineTag(FlutterEngineCache.getInstance().get(ENGINE_ID))}")
|
||||
super.onDestroy()
|
||||
|
||||
if (!isFinishing) {
|
||||
val cache = FlutterEngineCache.getInstance()
|
||||
val engine = cache.get(ENGINE_ID)
|
||||
if (engine != null) {
|
||||
Log.d(TAG, "onDestroy: system-initiated destroy, clearing stale engine")
|
||||
cache.remove(ENGINE_ID)
|
||||
try {
|
||||
engine.destroy()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Error destroying engine in onDestroy", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTrimMemory(level: Int) {
|
||||
super.onTrimMemory(level)
|
||||
trimMemoryChannel?.invokeMethod("onTrimMemory", level)
|
||||
}
|
||||
|
||||
override fun provideFlutterEngine(context: Context): FlutterEngine {
|
||||
val cache = FlutterEngineCache.getInstance()
|
||||
val cachedEngine = cache.get(ENGINE_ID)
|
||||
if (cachedEngine != null) {
|
||||
val isHealthy = try {
|
||||
cachedEngine.dartExecutor.isExecutingDart && isEngineNativeAlive(cachedEngine)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Cached engine health check failed", e)
|
||||
false
|
||||
}
|
||||
|
||||
if (isHealthy) {
|
||||
Log.d(TAG, "provideFlutterEngine: reusing cached engine ${engineTag(cachedEngine)}")
|
||||
return cachedEngine
|
||||
}
|
||||
|
||||
Log.w(TAG, "provideFlutterEngine: cached engine ${engineTag(cachedEngine)} is stale, creating fresh")
|
||||
cache.remove(ENGINE_ID)
|
||||
try {
|
||||
cachedEngine.destroy()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Error destroying stale engine", e)
|
||||
}
|
||||
}
|
||||
|
||||
val flutterEngine = FlutterEngine(context.applicationContext)
|
||||
flutterEngine.navigationChannel.setInitialRoute("/")
|
||||
flutterEngine.dartExecutor.executeDartEntrypoint(
|
||||
DartExecutor.DartEntrypoint.createDefault()
|
||||
)
|
||||
cache.put(ENGINE_ID, flutterEngine)
|
||||
|
||||
Log.d(TAG, "provideFlutterEngine: created new engine ${engineTag(flutterEngine)}")
|
||||
return flutterEngine
|
||||
}
|
||||
|
||||
override fun shouldDestroyEngineWithHost(): Boolean {
|
||||
return false // Keep engine alive when activity is destroyed
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2025 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.weblibre.gecko
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import eu.weblibre.flutter_mozilla_components.ActiveProfile
|
||||
import eu.weblibre.flutter_mozilla_components.MegazordSetup
|
||||
|
||||
class MyApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
MegazordSetup.setupEarlyMainProcess()
|
||||
|
||||
// Resolve active profile EARLY so cold-start WorkManager workers
|
||||
// get profile-prefixed SharedPreferences
|
||||
ActiveProfile.resolveFromDisk(this)
|
||||
}
|
||||
|
||||
override fun getSharedPreferences(name: String, mode: Int): SharedPreferences {
|
||||
val pfx = ActiveProfile.prefix
|
||||
if (pfx != null && name in ActiveProfile.FXA_SHARED_PREFERENCE_NAMES) {
|
||||
return super.getSharedPreferences("${pfx}_$name", mode)
|
||||
}
|
||||
return super.getSharedPreferences(name, mode)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package eu.weblibre.gecko
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.glance.GlanceId
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.Image
|
||||
import androidx.glance.ImageProvider
|
||||
import androidx.glance.action.clickable
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import androidx.glance.appwidget.GlanceAppWidgetReceiver
|
||||
import androidx.glance.appwidget.provideContent
|
||||
import androidx.glance.background
|
||||
import androidx.glance.layout.Alignment
|
||||
import androidx.glance.layout.Row
|
||||
import androidx.glance.layout.fillMaxWidth
|
||||
import androidx.glance.layout.padding
|
||||
import androidx.glance.layout.wrapContentHeight
|
||||
import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextStyle
|
||||
import androidx.glance.unit.ColorProvider
|
||||
import es.antonborri.home_widget.HomeWidgetLaunchIntent
|
||||
import android.net.Uri
|
||||
import es.antonborri.home_widget.actionStartActivity
|
||||
import androidx.core.net.toUri
|
||||
|
||||
class SearchBarGlanceWidget : GlanceAppWidget() {
|
||||
|
||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
||||
provideContent {
|
||||
SearchBarContent(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchBarContent(context: Context) {
|
||||
Row(
|
||||
modifier = GlanceModifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.background(ImageProvider(R.drawable.search_text_field))
|
||||
.padding(12.dp)
|
||||
.clickable(onClick = actionStartActivity<MainActivity>(context,
|
||||
"widget://search".toUri())),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Start icon
|
||||
Image(
|
||||
provider = ImageProvider(R.drawable.icon_with_size),
|
||||
contentDescription = "Search icon",
|
||||
modifier = GlanceModifier.padding(end = 8.dp)
|
||||
)
|
||||
|
||||
// Search text
|
||||
Text(
|
||||
text = "Search with WebLibre...",
|
||||
style = TextStyle(
|
||||
color = ColorProvider(Color(0xFF848388)),
|
||||
fontSize = 16.sp
|
||||
),
|
||||
modifier = GlanceModifier.defaultWeight()
|
||||
)
|
||||
|
||||
// End icon (microphone)
|
||||
Image(
|
||||
provider = ImageProvider(R.drawable.mdi_icon_microphone_tint),
|
||||
contentDescription = "Microphone icon",
|
||||
modifier = GlanceModifier.padding(start = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class SearchBarWidgetReceiver : GlanceAppWidgetReceiver() {
|
||||
override val glanceAppWidget: GlanceAppWidget = SearchBarGlanceWidget()
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 661 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
@@ -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>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#FFFFFF"/> <!-- Background color -->
|
||||
<corners android:radius="18dp"/> <!-- Corner radius -->
|
||||
</shape>
|
||||
@@ -0,0 +1,10 @@
|
||||
<!-- res/drawable/icon_with_size.xml -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:width="24dp"
|
||||
android:height="24dp">
|
||||
<bitmap
|
||||
android:src="@mipmap/launcher_icon"
|
||||
android:width="24dp"
|
||||
android:height="24dp"/>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -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>
|
||||
@@ -0,0 +1 @@
|
||||
<!-- drawable/brain.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M21.33,12.91C21.42,14.46 20.71,15.95 19.44,16.86L20.21,18.35C20.44,18.8 20.47,19.33 20.27,19.8C20.08,20.27 19.69,20.64 19.21,20.8L18.42,21.05C18.25,21.11 18.06,21.14 17.88,21.14C17.37,21.14 16.89,20.91 16.56,20.5L14.44,18C13.55,17.85 12.71,17.47 12,16.9C11.5,17.05 11,17.13 10.5,17.13C9.62,17.13 8.74,16.86 8,16.34C7.47,16.5 6.93,16.57 6.38,16.56C5.59,16.57 4.81,16.41 4.08,16.11C2.65,15.47 1.7,14.07 1.65,12.5C1.57,11.78 1.69,11.05 2,10.39C1.71,9.64 1.68,8.82 1.93,8.06C2.3,7.11 3,6.32 3.87,5.82C4.45,4.13 6.08,3 7.87,3.12C9.47,1.62 11.92,1.46 13.7,2.75C14.12,2.64 14.56,2.58 15,2.58C16.36,2.55 17.65,3.15 18.5,4.22C20.54,4.75 22,6.57 22.08,8.69C22.13,9.8 21.83,10.89 21.22,11.82C21.29,12.18 21.33,12.54 21.33,12.91M16.33,11.5C16.9,11.57 17.35,12 17.35,12.57A1,1 0 0,1 16.35,13.57H15.72C15.4,14.47 14.84,15.26 14.1,15.86C14.35,15.95 14.61,16 14.87,16.07C20,16 19.4,12.87 19.4,12.82C19.34,11.39 18.14,10.27 16.71,10.33A1,1 0 0,1 15.71,9.33A1,1 0 0,1 16.71,8.33C17.94,8.36 19.12,8.82 20.04,9.63C20.09,9.34 20.12,9.04 20.12,8.74C20.06,7.5 19.5,6.42 17.25,6.21C16,3.25 12.85,4.89 12.85,5.81V5.81C12.82,6.04 13.06,6.53 13.1,6.56A1,1 0 0,1 14.1,7.56C14.1,8.11 13.65,8.56 13.1,8.56V8.56C12.57,8.54 12.07,8.34 11.67,8C11.19,8.31 10.64,8.5 10.07,8.56V8.56C9.5,8.61 9.03,8.21 9,7.66C8.92,7.1 9.33,6.61 9.88,6.56C10.04,6.54 10.82,6.42 10.82,5.79V5.79C10.82,5.13 11.07,4.5 11.5,4C10.58,3.75 9.59,4.08 8.59,5.29C6.75,5 6,5.25 5.45,7.2C4.5,7.67 4,8 3.78,9C4.86,8.78 5.97,8.87 7,9.25C7.5,9.44 7.78,10 7.59,10.54C7.4,11.06 6.82,11.32 6.3,11.13C5.57,10.81 4.75,10.79 4,11.07C3.68,11.34 3.68,11.9 3.68,12.34C3.68,13.08 4.05,13.77 4.68,14.17C5.21,14.44 5.8,14.58 6.39,14.57C6.24,14.31 6.11,14.04 6,13.76C5.81,13.22 6.1,12.63 6.64,12.44C7.18,12.25 7.77,12.54 7.96,13.08C8.36,14.22 9.38,15 10.58,15.13C11.95,15.06 13.17,14.25 13.77,13C14,11.62 15.11,11.5 16.33,11.5M18.33,18.97L17.71,17.67L17,17.83L18,19.08L18.33,18.97M13.68,10.36C13.7,9.83 13.3,9.38 12.77,9.33C12.06,9.29 11.37,9.53 10.84,10C10.27,10.58 9.97,11.38 10,12.19A1,1 0 0,0 11,13.19C11.57,13.19 12,12.74 12,12.19C12,11.92 12.07,11.65 12.23,11.43C12.35,11.33 12.5,11.28 12.66,11.28C13.21,11.31 13.68,10.9 13.68,10.36Z" /></vector>
|
||||
@@ -0,0 +1 @@
|
||||
<!-- drawable/microphone.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#848388" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M12,2A3,3 0 0,1 15,5V11A3,3 0 0,1 12,14A3,3 0 0,1 9,11V5A3,3 0 0,1 12,2M19,11C19,14.53 16.39,17.44 13,17.93V21H11V17.93C7.61,17.44 5,14.53 5,11H7A5,5 0 0,0 12,16A5,5 0 0,0 17,11H19Z" /></vector>
|
||||
@@ -0,0 +1 @@
|
||||
<!-- drawable/search_web.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M15.5,14L20.5,19L19,20.5L14,15.5V14.71L13.73,14.43C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.43,13.73L14.71,14H15.5M9.5,4.5L8.95,4.53C8.71,5.05 8.34,5.93 8.07,7H10.93C10.66,5.93 10.29,5.05 10.05,4.53C9.87,4.5 9.69,4.5 9.5,4.5M13.83,7C13.24,5.97 12.29,5.17 11.15,4.78C11.39,5.31 11.7,6.08 11.93,7H13.83M5.17,7H7.07C7.3,6.08 7.61,5.31 7.85,4.78C6.71,5.17 5.76,5.97 5.17,7M4.5,9.5C4.5,10 4.58,10.53 4.73,11H6.87L6.75,9.5L6.87,8H4.73C4.58,8.47 4.5,9 4.5,9.5M14.27,11C14.42,10.53 14.5,10 14.5,9.5C14.5,9 14.42,8.47 14.27,8H12.13C12.21,8.5 12.25,9 12.25,9.5C12.25,10 12.21,10.5 12.13,11H14.27M7.87,8L7.75,9.5L7.87,11H11.13C11.21,10.5 11.25,10 11.25,9.5C11.25,9 11.21,8.5 11.13,8H7.87M9.5,14.5C9.68,14.5 9.86,14.5 10.03,14.47C10.28,13.95 10.66,13.07 10.93,12H8.07C8.34,13.07 8.72,13.95 8.97,14.47L9.5,14.5M13.83,12H11.93C11.7,12.92 11.39,13.69 11.15,14.22C12.29,13.83 13.24,13.03 13.83,12M5.17,12C5.76,13.03 6.71,13.83 7.85,14.22C7.61,13.69 7.3,12.92 7.07,12H5.17Z" /></vector>
|
||||
@@ -0,0 +1 @@
|
||||
<!-- drawable/text.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M21,6V8H3V6H21M3,18H12V16H3V18M3,13H21V11H3V13Z" /></vector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#333237"/> <!-- Background color -->
|
||||
<corners android:radius="32dp"/> <!-- Rounded corners -->
|
||||
<stroke android:width="1dp" android:color="#BDBDBD"/> <!-- Border -->
|
||||
</shape>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?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="25%" />
|
||||
</foreground>
|
||||
<monochrome>
|
||||
<inset
|
||||
android:drawable="@drawable/ic_launcher_monochrome"
|
||||
android:inset="25%" />
|
||||
</monochrome>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
@@ -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="Theme.Material3.DynamicColors.DayNight.NoActionBar">
|
||||
<!-- 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="Theme.Material3.DynamicColors.DayNight.NoActionBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<resources>
|
||||
<declare-styleable name="AppWidgetAttrs">
|
||||
<attr name="appWidgetPadding" format="dimension" />
|
||||
<attr name="appWidgetInnerRadius" format="dimension" />
|
||||
<attr name="appWidgetRadius" format="dimension" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="light_blue_50">#FFE1F5FE</color>
|
||||
<color name="light_blue_200">#FF81D4FA</color>
|
||||
<color name="light_blue_600">#FF039BE5</color>
|
||||
<color name="light_blue_900">#FF01579B</color>
|
||||
<color name="ic_launcher_background">#FF000000</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!--
|
||||
Refer to App Widget Documentation for margin information
|
||||
http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout
|
||||
-->
|
||||
<dimen name="widget_margin">0dp</dimen>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?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="Theme.Material3.DynamicColors.DayNight.NoActionBar">
|
||||
<!-- 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="Theme.Material3.DynamicColors.DayNight.NoActionBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.WebLibreWidget.AppWidget.Container" parent="android:Widget">
|
||||
<item name="android:id">@android:id/background</item>
|
||||
<item name="android:background">?android:attr/colorBackground</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.WebLibreWidget.AppWidget.InnerView" parent="android:Widget">
|
||||
<item name="android:background">?android:attr/colorBackground</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme" parent="Theme.Material3.DynamicColors.DayNight">
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
|
||||
</style>
|
||||
|
||||
<style name="ExternalAppBrowserTheme" parent="Theme.Material3.DynamicColors.DayNight.NoActionBar">
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="AddonsActivityTheme" parent="AppTheme">
|
||||
<item name="mozac_primary_text_color">@color/photonDarkGrey90</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,31 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.WebLibreWidget" parent="@style/Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
<style name="Theme.WebLibreWidget.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault">
|
||||
<!-- Radius of the outer bound of widgets to make the rounded corners -->
|
||||
<item name="appWidgetRadius">16dp</item>
|
||||
<!--
|
||||
Radius of the inner view's bound of widgets to make the rounded corners.
|
||||
It needs to be 8dp or less than the value of appWidgetRadius
|
||||
-->
|
||||
<item name="appWidgetInnerRadius">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.WebLibreWidget.AppWidgetContainer" parent="Theme.WebLibreWidget.AppWidgetContainerParent">
|
||||
<!-- Apply padding to avoid the content of the widget colliding with the rounded corners -->
|
||||
<item name="appWidgetPadding">16dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialLayout="@layout/glance_default_loading_layout"
|
||||
android:resizeMode="none"
|
||||
android:targetCellWidth="5"
|
||||
android:targetCellHeight="1"
|
||||
android:widgetCategory="home_screen" />
|
||||
@@ -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>
|
||||