wip
12
i18n/en.json
@@ -557,6 +557,18 @@
|
||||
"app_bar_signout_dialog_content": "Are you sure you want to sign out?",
|
||||
"app_bar_signout_dialog_title": "Sign out",
|
||||
"app_download_links": "App Download Links",
|
||||
"app_icon_blossom": "Blossom",
|
||||
"app_icon_change_failed": "Unable to change the app icon",
|
||||
"app_icon_classic": "Classic",
|
||||
"app_icon_forest": "Forest",
|
||||
"app_icon_gold": "Gold",
|
||||
"app_icon_ink": "Ink",
|
||||
"app_icon_midnight": "Midnight",
|
||||
"app_icon_neon": "Neon",
|
||||
"app_icon_ocean": "Ocean",
|
||||
"app_icon_subtitle": "Choose the icon shown on your home screen",
|
||||
"app_icon_sunset": "Sunset",
|
||||
"app_icon_title": "App Icon",
|
||||
"app_settings": "App Settings",
|
||||
"app_stores": "App Stores",
|
||||
"app_update_available": "App update is available",
|
||||
|
||||
@@ -56,11 +56,6 @@
|
||||
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" />
|
||||
</intent-filter>
|
||||
|
||||
|
||||
<!--TODO:
|
||||
Add this filter if you want to handle shared images-->
|
||||
@@ -137,6 +132,72 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Launcher aliases for the selectable app icons (see AppIconApiImpl).
|
||||
Exactly one is enabled at a time; the LAUNCHER intent-filter lives on
|
||||
these instead of MainActivity so the icon can be swapped at runtime. -->
|
||||
<activity-alias android:name=".AppIconClassic" android:targetActivity=".MainActivity"
|
||||
android:icon="@mipmap/ic_launcher" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
<activity-alias android:name=".AppIconMidnight" android:targetActivity=".MainActivity"
|
||||
android:icon="@mipmap/ic_launcher_midnight" android:exported="true" android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
<activity-alias android:name=".AppIconOcean" android:targetActivity=".MainActivity"
|
||||
android:icon="@mipmap/ic_launcher_ocean" android:exported="true" android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
<activity-alias android:name=".AppIconSunset" android:targetActivity=".MainActivity"
|
||||
android:icon="@mipmap/ic_launcher_sunset" android:exported="true" android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
<activity-alias android:name=".AppIconForest" android:targetActivity=".MainActivity"
|
||||
android:icon="@mipmap/ic_launcher_forest" android:exported="true" android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
<activity-alias android:name=".AppIconBlossom" android:targetActivity=".MainActivity"
|
||||
android:icon="@mipmap/ic_launcher_blossom" android:exported="true" android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
<activity-alias android:name=".AppIconInk" android:targetActivity=".MainActivity"
|
||||
android:icon="@mipmap/ic_launcher_ink" android:exported="true" android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
<activity-alias android:name=".AppIconNeon" android:targetActivity=".MainActivity"
|
||||
android:icon="@mipmap/ic_launcher_neon" android:exported="true" android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
<activity-alias android:name=".AppIconGold" android:targetActivity=".MainActivity"
|
||||
android:icon="@mipmap/ic_launcher_gold" android:exported="true" android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
|
||||
<activity
|
||||
android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
|
||||
|
||||
@@ -4,6 +4,8 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.ext.SdkExtensions
|
||||
import app.alextran.immich.appicon.AppIconApi
|
||||
import app.alextran.immich.appicon.AppIconApiImpl
|
||||
import app.alextran.immich.background.BackgroundEngineLock
|
||||
import app.alextran.immich.background.BackgroundWorkerApiImpl
|
||||
import app.alextran.immich.background.BackgroundWorkerFgHostApi
|
||||
@@ -61,6 +63,7 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
|
||||
BackgroundWorkerFgHostApi.setUp(messenger, BackgroundWorkerApiImpl(ctx))
|
||||
ConnectivityApi.setUp(messenger, ConnectivityApiImpl(ctx))
|
||||
AppIconApi.setUp(messenger, AppIconApiImpl(ctx))
|
||||
|
||||
flutterEngine.plugins.add(ViewIntentPlugin())
|
||||
flutterEngine.plugins.add(backgroundEngineLockImpl)
|
||||
|
||||
110
mobile/android/app/src/main/kotlin/app/alextran/immich/appicon/AppIcon.g.kt
generated
Normal file
@@ -0,0 +1,110 @@
|
||||
// Autogenerated from Pigeon (v26.3.4), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
|
||||
|
||||
package app.alextran.immich.appicon
|
||||
|
||||
import android.util.Log
|
||||
import io.flutter.plugin.common.BasicMessageChannel
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.MessageCodec
|
||||
import io.flutter.plugin.common.StandardMethodCodec
|
||||
import io.flutter.plugin.common.StandardMessageCodec
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.ByteBuffer
|
||||
private object AppIconPigeonUtils {
|
||||
|
||||
fun wrapResult(result: Any?): List<Any?> {
|
||||
return listOf(result)
|
||||
}
|
||||
|
||||
fun wrapError(exception: Throwable): List<Any?> {
|
||||
return if (exception is FlutterError) {
|
||||
listOf(
|
||||
exception.code,
|
||||
exception.message,
|
||||
exception.details
|
||||
)
|
||||
} else {
|
||||
listOf(
|
||||
exception.javaClass.simpleName,
|
||||
exception.toString(),
|
||||
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error class for passing custom error details to Flutter via a thrown PlatformException.
|
||||
* @property code The error code.
|
||||
* @property message The error message.
|
||||
* @property details The error details. Must be a datatype supported by the api codec.
|
||||
*/
|
||||
class FlutterError (
|
||||
val code: String,
|
||||
override val message: String? = null,
|
||||
val details: Any? = null
|
||||
) : RuntimeException()
|
||||
private open class AppIconPigeonCodec : StandardMessageCodec() {
|
||||
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
|
||||
return super.readValueOfType(type, buffer)
|
||||
}
|
||||
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
|
||||
super.writeValue(stream, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||
interface AppIconApi {
|
||||
fun setAppIcon(iconId: String, callback: (Result<Unit>) -> Unit)
|
||||
fun getAppIcon(): String
|
||||
|
||||
companion object {
|
||||
/** The codec used by AppIconApi. */
|
||||
val codec: MessageCodec<Any?> by lazy {
|
||||
AppIconPigeonCodec()
|
||||
}
|
||||
/** Sets up an instance of `AppIconApi` to handle messages through the `binaryMessenger`. */
|
||||
@JvmOverloads
|
||||
fun setUp(binaryMessenger: BinaryMessenger, api: AppIconApi?, messageChannelSuffix: String = "") {
|
||||
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.AppIconApi.setAppIcon$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val iconIdArg = args[0] as String
|
||||
api.setAppIcon(iconIdArg) { result: Result<Unit> ->
|
||||
val error = result.exceptionOrNull()
|
||||
if (error != null) {
|
||||
reply.reply(AppIconPigeonUtils.wrapError(error))
|
||||
} else {
|
||||
reply.reply(AppIconPigeonUtils.wrapResult(null))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.AppIconApi.getAppIcon$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getAppIcon())
|
||||
} catch (exception: Throwable) {
|
||||
AppIconPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package app.alextran.immich.appicon
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
|
||||
/**
|
||||
* Switches the launcher icon by toggling the `.AppIcon*` activity-aliases
|
||||
* declared in the AndroidManifest. Exactly one alias is enabled at a time;
|
||||
* the aliases all target MainActivity.
|
||||
*/
|
||||
class AppIconApiImpl(context: Context) : AppIconApi {
|
||||
private val ctx: Context = context.applicationContext
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_ICON = "classic"
|
||||
private val ICONS =
|
||||
listOf("classic", "midnight", "ocean", "sunset", "forest", "blossom", "ink", "neon", "gold")
|
||||
}
|
||||
|
||||
private fun componentFor(iconId: String): ComponentName {
|
||||
val alias = "AppIcon" + iconId.replaceFirstChar { it.uppercase() }
|
||||
return ComponentName(ctx.packageName, "app.alextran.immich.$alias")
|
||||
}
|
||||
|
||||
override fun setAppIcon(iconId: String, callback: (Result<Unit>) -> Unit) {
|
||||
if (iconId !in ICONS) {
|
||||
callback(Result.failure(FlutterError("invalid_icon", "Unknown app icon: $iconId")))
|
||||
return
|
||||
}
|
||||
|
||||
val pm = ctx.packageManager
|
||||
for (icon in ICONS) {
|
||||
val state = if (icon == iconId) {
|
||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||
} else {
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
||||
}
|
||||
pm.setComponentEnabledSetting(componentFor(icon), state, PackageManager.DONT_KILL_APP)
|
||||
}
|
||||
callback(Result.success(Unit))
|
||||
}
|
||||
|
||||
override fun getAppIcon(): String {
|
||||
val pm = ctx.packageManager
|
||||
for (icon in ICONS) {
|
||||
if (pm.getComponentEnabledSetting(componentFor(icon)) ==
|
||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||
) {
|
||||
return icon
|
||||
}
|
||||
}
|
||||
return DEFAULT_ICON
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#E11D48"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m52.25,43.55c3.4,3.08 6.15,6.39 7.91,9.5 3.03,-5.55 5.06,-12.14 5.08,-16.34 0,-0.03 0,-0.06 0,-0.08 0,-6.21 -6.06,-8.63 -11.28,-8.63 -5.22,0 -11.28,2.42 -11.28,8.63 0,0.08 0,0.2 0,0.34 2.91,1.32 6.36,3.69 9.56,6.59z" />
|
||||
<path
|
||||
android:fillColor="#FB7185"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m33.68,60.5c2.13,-2.42 5.39,-5.05 9.08,-7.27 3.92,-2.36 7.84,-4.01 11.28,-4.76 -4.22,-4.67 -9.72,-8.67 -13.62,-10 -0.03,-0.01 -0.05,-0.02 -0.08,-0.03 -5.78,-1.92 -9.9,3.23 -11.51,8.31 -1.61,5.08 -1.24,11.72 4.54,13.64 0.08,0.03 0.18,0.06 0.31,0.1z" />
|
||||
<path
|
||||
android:fillColor="#F472B6"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M79.17,46.67C77.56,41.59 73.44,36.44 67.66,38.36c-0.08,0.03 -0.19,0.06 -0.31,0.1 -0.33,3.24 -1.46,7.33 -3.17,11.34 -1.81,4.27 -4.04,7.96 -6.39,10.64 6.1,1.24 12.85,1.17 16.76,-0.1 0.03,-0.01 0.05,-0.02 0.08,-0.03 5.78,-1.92 6.15,-8.56 4.54,-13.64z" />
|
||||
<path
|
||||
android:fillColor="#BE185D"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m48.82,65.46c-0.98,-4.54 -1.3,-8.87 -0.94,-12.45 -5.64,2.67 -11.07,6.78 -13.5,10.16 -0.02,0.02 -0.03,0.05 -0.05,0.07 -3.57,5.03 -0.06,10.63 4.16,13.77 4.22,3.14 10.51,4.83 14.09,-0.2 0.05,-0.07 0.11,-0.16 0.19,-0.27 -1.59,-2.82 -3.03,-6.81 -3.95,-11.08z" />
|
||||
<path
|
||||
android:fillColor="#F9A8D4"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m73.58,62.92c-3.11,0.68 -7.26,0.84 -11.52,0.42 -4.53,-0.45 -8.64,-1.47 -11.86,-2.93 0.73,6.31 2.88,12.87 5.28,16.28 0.02,0.02 0.03,0.05 0.05,0.07 3.57,5.03 9.86,3.34 14.09,0.2 4.22,-3.14 7.74,-8.74 4.16,-13.77 -0.05,-0.07 -0.11,-0.16 -0.19,-0.27z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#16A34A"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m52.25,43.55c3.4,3.08 6.15,6.39 7.91,9.5 3.03,-5.55 5.06,-12.14 5.08,-16.34 0,-0.03 0,-0.06 0,-0.08 0,-6.21 -6.06,-8.63 -11.28,-8.63 -5.22,0 -11.28,2.42 -11.28,8.63 0,0.08 0,0.2 0,0.34 2.91,1.32 6.36,3.69 9.56,6.59z" />
|
||||
<path
|
||||
android:fillColor="#84CC16"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m33.68,60.5c2.13,-2.42 5.39,-5.05 9.08,-7.27 3.92,-2.36 7.84,-4.01 11.28,-4.76 -4.22,-4.67 -9.72,-8.67 -13.62,-10 -0.03,-0.01 -0.05,-0.02 -0.08,-0.03 -5.78,-1.92 -9.9,3.23 -11.51,8.31 -1.61,5.08 -1.24,11.72 4.54,13.64 0.08,0.03 0.18,0.06 0.31,0.1z" />
|
||||
<path
|
||||
android:fillColor="#4ADE80"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M79.17,46.67C77.56,41.59 73.44,36.44 67.66,38.36c-0.08,0.03 -0.19,0.06 -0.31,0.1 -0.33,3.24 -1.46,7.33 -3.17,11.34 -1.81,4.27 -4.04,7.96 -6.39,10.64 6.1,1.24 12.85,1.17 16.76,-0.1 0.03,-0.01 0.05,-0.02 0.08,-0.03 5.78,-1.92 6.15,-8.56 4.54,-13.64z" />
|
||||
<path
|
||||
android:fillColor="#15803D"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m48.82,65.46c-0.98,-4.54 -1.3,-8.87 -0.94,-12.45 -5.64,2.67 -11.07,6.78 -13.5,10.16 -0.02,0.02 -0.03,0.05 -0.05,0.07 -3.57,5.03 -0.06,10.63 4.16,13.77 4.22,3.14 10.51,4.83 14.09,-0.2 0.05,-0.07 0.11,-0.16 0.19,-0.27 -1.59,-2.82 -3.03,-6.81 -3.95,-11.08z" />
|
||||
<path
|
||||
android:fillColor="#65A30D"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m73.58,62.92c-3.11,0.68 -7.26,0.84 -11.52,0.42 -4.53,-0.45 -8.64,-1.47 -11.86,-2.93 0.73,6.31 2.88,12.87 5.28,16.28 0.02,0.02 0.03,0.05 0.05,0.07 3.57,5.03 9.86,3.34 14.09,0.2 4.22,-3.14 7.74,-8.74 4.16,-13.77 -0.05,-0.07 -0.11,-0.16 -0.19,-0.27z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FBBF24"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m52.25,43.55c3.4,3.08 6.15,6.39 7.91,9.5 3.03,-5.55 5.06,-12.14 5.08,-16.34 0,-0.03 0,-0.06 0,-0.08 0,-6.21 -6.06,-8.63 -11.28,-8.63 -5.22,0 -11.28,2.42 -11.28,8.63 0,0.08 0,0.2 0,0.34 2.91,1.32 6.36,3.69 9.56,6.59z" />
|
||||
<path
|
||||
android:fillColor="#FDE68A"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m33.68,60.5c2.13,-2.42 5.39,-5.05 9.08,-7.27 3.92,-2.36 7.84,-4.01 11.28,-4.76 -4.22,-4.67 -9.72,-8.67 -13.62,-10 -0.03,-0.01 -0.05,-0.02 -0.08,-0.03 -5.78,-1.92 -9.9,3.23 -11.51,8.31 -1.61,5.08 -1.24,11.72 4.54,13.64 0.08,0.03 0.18,0.06 0.31,0.1z" />
|
||||
<path
|
||||
android:fillColor="#F59E0B"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M79.17,46.67C77.56,41.59 73.44,36.44 67.66,38.36c-0.08,0.03 -0.19,0.06 -0.31,0.1 -0.33,3.24 -1.46,7.33 -3.17,11.34 -1.81,4.27 -4.04,7.96 -6.39,10.64 6.1,1.24 12.85,1.17 16.76,-0.1 0.03,-0.01 0.05,-0.02 0.08,-0.03 5.78,-1.92 6.15,-8.56 4.54,-13.64z" />
|
||||
<path
|
||||
android:fillColor="#D97706"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m48.82,65.46c-0.98,-4.54 -1.3,-8.87 -0.94,-12.45 -5.64,2.67 -11.07,6.78 -13.5,10.16 -0.02,0.02 -0.03,0.05 -0.05,0.07 -3.57,5.03 -0.06,10.63 4.16,13.77 4.22,3.14 10.51,4.83 14.09,-0.2 0.05,-0.07 0.11,-0.16 0.19,-0.27 -1.59,-2.82 -3.03,-6.81 -3.95,-11.08z" />
|
||||
<path
|
||||
android:fillColor="#FCD34D"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m73.58,62.92c-3.11,0.68 -7.26,0.84 -11.52,0.42 -4.53,-0.45 -8.64,-1.47 -11.86,-2.93 0.73,6.31 2.88,12.87 5.28,16.28 0.02,0.02 0.03,0.05 0.05,0.07 3.57,5.03 9.86,3.34 14.09,0.2 4.22,-3.14 7.74,-8.74 4.16,-13.77 -0.05,-0.07 -0.11,-0.16 -0.19,-0.27z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#111827"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m52.25,43.55c3.4,3.08 6.15,6.39 7.91,9.5 3.03,-5.55 5.06,-12.14 5.08,-16.34 0,-0.03 0,-0.06 0,-0.08 0,-6.21 -6.06,-8.63 -11.28,-8.63 -5.22,0 -11.28,2.42 -11.28,8.63 0,0.08 0,0.2 0,0.34 2.91,1.32 6.36,3.69 9.56,6.59z" />
|
||||
<path
|
||||
android:fillColor="#6B7280"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m33.68,60.5c2.13,-2.42 5.39,-5.05 9.08,-7.27 3.92,-2.36 7.84,-4.01 11.28,-4.76 -4.22,-4.67 -9.72,-8.67 -13.62,-10 -0.03,-0.01 -0.05,-0.02 -0.08,-0.03 -5.78,-1.92 -9.9,3.23 -11.51,8.31 -1.61,5.08 -1.24,11.72 4.54,13.64 0.08,0.03 0.18,0.06 0.31,0.1z" />
|
||||
<path
|
||||
android:fillColor="#374151"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M79.17,46.67C77.56,41.59 73.44,36.44 67.66,38.36c-0.08,0.03 -0.19,0.06 -0.31,0.1 -0.33,3.24 -1.46,7.33 -3.17,11.34 -1.81,4.27 -4.04,7.96 -6.39,10.64 6.1,1.24 12.85,1.17 16.76,-0.1 0.03,-0.01 0.05,-0.02 0.08,-0.03 5.78,-1.92 6.15,-8.56 4.54,-13.64z" />
|
||||
<path
|
||||
android:fillColor="#9CA3AF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m48.82,65.46c-0.98,-4.54 -1.3,-8.87 -0.94,-12.45 -5.64,2.67 -11.07,6.78 -13.5,10.16 -0.02,0.02 -0.03,0.05 -0.05,0.07 -3.57,5.03 -0.06,10.63 4.16,13.77 4.22,3.14 10.51,4.83 14.09,-0.2 0.05,-0.07 0.11,-0.16 0.19,-0.27 -1.59,-2.82 -3.03,-6.81 -3.95,-11.08z" />
|
||||
<path
|
||||
android:fillColor="#4B5563"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m73.58,62.92c-3.11,0.68 -7.26,0.84 -11.52,0.42 -4.53,-0.45 -8.64,-1.47 -11.86,-2.93 0.73,6.31 2.88,12.87 5.28,16.28 0.02,0.02 0.03,0.05 0.05,0.07 3.57,5.03 9.86,3.34 14.09,0.2 4.22,-3.14 7.74,-8.74 4.16,-13.77 -0.05,-0.07 -0.11,-0.16 -0.19,-0.27z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FA2921"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m52.25,43.55c3.4,3.08 6.15,6.39 7.91,9.5 3.03,-5.55 5.06,-12.14 5.08,-16.34 0,-0.03 0,-0.06 0,-0.08 0,-6.21 -6.06,-8.63 -11.28,-8.63 -5.22,0 -11.28,2.42 -11.28,8.63 0,0.08 0,0.2 0,0.34 2.91,1.32 6.36,3.69 9.56,6.59z" />
|
||||
<path
|
||||
android:fillColor="#ED79B5"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m33.68,60.5c2.13,-2.42 5.39,-5.05 9.08,-7.27 3.92,-2.36 7.84,-4.01 11.28,-4.76 -4.22,-4.67 -9.72,-8.67 -13.62,-10 -0.03,-0.01 -0.05,-0.02 -0.08,-0.03 -5.78,-1.92 -9.9,3.23 -11.51,8.31 -1.61,5.08 -1.24,11.72 4.54,13.64 0.08,0.03 0.18,0.06 0.31,0.1z" />
|
||||
<path
|
||||
android:fillColor="#FFB400"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M79.17,46.67C77.56,41.59 73.44,36.44 67.66,38.36c-0.08,0.03 -0.19,0.06 -0.31,0.1 -0.33,3.24 -1.46,7.33 -3.17,11.34 -1.81,4.27 -4.04,7.96 -6.39,10.64 6.1,1.24 12.85,1.17 16.76,-0.1 0.03,-0.01 0.05,-0.02 0.08,-0.03 5.78,-1.92 6.15,-8.56 4.54,-13.64z" />
|
||||
<path
|
||||
android:fillColor="#1E83F7"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m48.82,65.46c-0.98,-4.54 -1.3,-8.87 -0.94,-12.45 -5.64,2.67 -11.07,6.78 -13.5,10.16 -0.02,0.02 -0.03,0.05 -0.05,0.07 -3.57,5.03 -0.06,10.63 4.16,13.77 4.22,3.14 10.51,4.83 14.09,-0.2 0.05,-0.07 0.11,-0.16 0.19,-0.27 -1.59,-2.82 -3.03,-6.81 -3.95,-11.08z" />
|
||||
<path
|
||||
android:fillColor="#18C249"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m73.58,62.92c-3.11,0.68 -7.26,0.84 -11.52,0.42 -4.53,-0.45 -8.64,-1.47 -11.86,-2.93 0.73,6.31 2.88,12.87 5.28,16.28 0.02,0.02 0.03,0.05 0.05,0.07 3.57,5.03 9.86,3.34 14.09,0.2 4.22,-3.14 7.74,-8.74 4.16,-13.77 -0.05,-0.07 -0.11,-0.16 -0.19,-0.27z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FF2D95"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m52.25,43.55c3.4,3.08 6.15,6.39 7.91,9.5 3.03,-5.55 5.06,-12.14 5.08,-16.34 0,-0.03 0,-0.06 0,-0.08 0,-6.21 -6.06,-8.63 -11.28,-8.63 -5.22,0 -11.28,2.42 -11.28,8.63 0,0.08 0,0.2 0,0.34 2.91,1.32 6.36,3.69 9.56,6.59z" />
|
||||
<path
|
||||
android:fillColor="#00E5FF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m33.68,60.5c2.13,-2.42 5.39,-5.05 9.08,-7.27 3.92,-2.36 7.84,-4.01 11.28,-4.76 -4.22,-4.67 -9.72,-8.67 -13.62,-10 -0.03,-0.01 -0.05,-0.02 -0.08,-0.03 -5.78,-1.92 -9.9,3.23 -11.51,8.31 -1.61,5.08 -1.24,11.72 4.54,13.64 0.08,0.03 0.18,0.06 0.31,0.1z" />
|
||||
<path
|
||||
android:fillColor="#FFD600"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M79.17,46.67C77.56,41.59 73.44,36.44 67.66,38.36c-0.08,0.03 -0.19,0.06 -0.31,0.1 -0.33,3.24 -1.46,7.33 -3.17,11.34 -1.81,4.27 -4.04,7.96 -6.39,10.64 6.1,1.24 12.85,1.17 16.76,-0.1 0.03,-0.01 0.05,-0.02 0.08,-0.03 5.78,-1.92 6.15,-8.56 4.54,-13.64z" />
|
||||
<path
|
||||
android:fillColor="#7C4DFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m48.82,65.46c-0.98,-4.54 -1.3,-8.87 -0.94,-12.45 -5.64,2.67 -11.07,6.78 -13.5,10.16 -0.02,0.02 -0.03,0.05 -0.05,0.07 -3.57,5.03 -0.06,10.63 4.16,13.77 4.22,3.14 10.51,4.83 14.09,-0.2 0.05,-0.07 0.11,-0.16 0.19,-0.27 -1.59,-2.82 -3.03,-6.81 -3.95,-11.08z" />
|
||||
<path
|
||||
android:fillColor="#00FF9D"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m73.58,62.92c-3.11,0.68 -7.26,0.84 -11.52,0.42 -4.53,-0.45 -8.64,-1.47 -11.86,-2.93 0.73,6.31 2.88,12.87 5.28,16.28 0.02,0.02 0.03,0.05 0.05,0.07 3.57,5.03 9.86,3.34 14.09,0.2 4.22,-3.14 7.74,-8.74 4.16,-13.77 -0.05,-0.07 -0.11,-0.16 -0.19,-0.27z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#0284C7"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m52.25,43.55c3.4,3.08 6.15,6.39 7.91,9.5 3.03,-5.55 5.06,-12.14 5.08,-16.34 0,-0.03 0,-0.06 0,-0.08 0,-6.21 -6.06,-8.63 -11.28,-8.63 -5.22,0 -11.28,2.42 -11.28,8.63 0,0.08 0,0.2 0,0.34 2.91,1.32 6.36,3.69 9.56,6.59z" />
|
||||
<path
|
||||
android:fillColor="#38BDF8"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m33.68,60.5c2.13,-2.42 5.39,-5.05 9.08,-7.27 3.92,-2.36 7.84,-4.01 11.28,-4.76 -4.22,-4.67 -9.72,-8.67 -13.62,-10 -0.03,-0.01 -0.05,-0.02 -0.08,-0.03 -5.78,-1.92 -9.9,3.23 -11.51,8.31 -1.61,5.08 -1.24,11.72 4.54,13.64 0.08,0.03 0.18,0.06 0.31,0.1z" />
|
||||
<path
|
||||
android:fillColor="#1D4ED8"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M79.17,46.67C77.56,41.59 73.44,36.44 67.66,38.36c-0.08,0.03 -0.19,0.06 -0.31,0.1 -0.33,3.24 -1.46,7.33 -3.17,11.34 -1.81,4.27 -4.04,7.96 -6.39,10.64 6.1,1.24 12.85,1.17 16.76,-0.1 0.03,-0.01 0.05,-0.02 0.08,-0.03 5.78,-1.92 6.15,-8.56 4.54,-13.64z" />
|
||||
<path
|
||||
android:fillColor="#0891B2"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m48.82,65.46c-0.98,-4.54 -1.3,-8.87 -0.94,-12.45 -5.64,2.67 -11.07,6.78 -13.5,10.16 -0.02,0.02 -0.03,0.05 -0.05,0.07 -3.57,5.03 -0.06,10.63 4.16,13.77 4.22,3.14 10.51,4.83 14.09,-0.2 0.05,-0.07 0.11,-0.16 0.19,-0.27 -1.59,-2.82 -3.03,-6.81 -3.95,-11.08z" />
|
||||
<path
|
||||
android:fillColor="#14B8A6"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m73.58,62.92c-3.11,0.68 -7.26,0.84 -11.52,0.42 -4.53,-0.45 -8.64,-1.47 -11.86,-2.93 0.73,6.31 2.88,12.87 5.28,16.28 0.02,0.02 0.03,0.05 0.05,0.07 3.57,5.03 9.86,3.34 14.09,0.2 4.22,-3.14 7.74,-8.74 4.16,-13.77 -0.05,-0.07 -0.11,-0.16 -0.19,-0.27z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#F97316"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m52.25,43.55c3.4,3.08 6.15,6.39 7.91,9.5 3.03,-5.55 5.06,-12.14 5.08,-16.34 0,-0.03 0,-0.06 0,-0.08 0,-6.21 -6.06,-8.63 -11.28,-8.63 -5.22,0 -11.28,2.42 -11.28,8.63 0,0.08 0,0.2 0,0.34 2.91,1.32 6.36,3.69 9.56,6.59z" />
|
||||
<path
|
||||
android:fillColor="#7C3AED"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m33.68,60.5c2.13,-2.42 5.39,-5.05 9.08,-7.27 3.92,-2.36 7.84,-4.01 11.28,-4.76 -4.22,-4.67 -9.72,-8.67 -13.62,-10 -0.03,-0.01 -0.05,-0.02 -0.08,-0.03 -5.78,-1.92 -9.9,3.23 -11.51,8.31 -1.61,5.08 -1.24,11.72 4.54,13.64 0.08,0.03 0.18,0.06 0.31,0.1z" />
|
||||
<path
|
||||
android:fillColor="#FACC15"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M79.17,46.67C77.56,41.59 73.44,36.44 67.66,38.36c-0.08,0.03 -0.19,0.06 -0.31,0.1 -0.33,3.24 -1.46,7.33 -3.17,11.34 -1.81,4.27 -4.04,7.96 -6.39,10.64 6.1,1.24 12.85,1.17 16.76,-0.1 0.03,-0.01 0.05,-0.02 0.08,-0.03 5.78,-1.92 6.15,-8.56 4.54,-13.64z" />
|
||||
<path
|
||||
android:fillColor="#C026D3"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m48.82,65.46c-0.98,-4.54 -1.3,-8.87 -0.94,-12.45 -5.64,2.67 -11.07,6.78 -13.5,10.16 -0.02,0.02 -0.03,0.05 -0.05,0.07 -3.57,5.03 -0.06,10.63 4.16,13.77 4.22,3.14 10.51,4.83 14.09,-0.2 0.05,-0.07 0.11,-0.16 0.19,-0.27 -1.59,-2.82 -3.03,-6.81 -3.95,-11.08z" />
|
||||
<path
|
||||
android:fillColor="#F43F5E"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="m73.58,62.92c-3.11,0.68 -7.26,0.84 -11.52,0.42 -4.53,-0.45 -8.64,-1.47 -11.86,-2.93 0.73,6.31 2.88,12.87 5.28,16.28 0.02,0.02 0.03,0.05 0.05,0.07 3.57,5.03 9.86,3.34 14.09,0.2 4.22,-3.14 7.74,-8.74 4.16,-13.77 -0.05,-0.07 -0.11,-0.16 -0.19,-0.27z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?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_blossom"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_blossom"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?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_forest"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_forest"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?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_gold"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_gold"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?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_ink"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_ink"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?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_midnight"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_midnight"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?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_neon"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_neon"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?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_ocean"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_ocean"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?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_sunset"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_sunset"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
11
mobile/android/app/src/main/res/values/app_icon_colors.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background_midnight">#0F172A</color>
|
||||
<color name="ic_launcher_background_ocean">#FFFFFF</color>
|
||||
<color name="ic_launcher_background_sunset">#1E1B4B</color>
|
||||
<color name="ic_launcher_background_forest">#FFFFFF</color>
|
||||
<color name="ic_launcher_background_blossom">#FFF1F2</color>
|
||||
<color name="ic_launcher_background_ink">#FFFFFF</color>
|
||||
<color name="ic_launcher_background_neon">#0A0A12</color>
|
||||
<color name="ic_launcher_background_gold">#1C1917</color>
|
||||
</resources>
|
||||
10
mobile/assets/app-icons/app-icon-blossom.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" fill="#FFF1F2"/>
|
||||
<g transform="translate(21.12 21.12) scale(0.78)">
|
||||
<path d="M90.0492 58.8333C102.552 69.9022 112.627 81.7638 119.111 92.9428C130.248 73.0245 137.69 49.3562 137.784 34.2816C137.784 34.1749 137.784 34.0778 137.784 33.9872C137.784 11.6811 115.533 3 96.3651 3C77.1975 3 54.9464 11.6811 54.9464 33.9872C54.9464 34.2913 54.9464 34.699 54.9464 35.1908C65.6303 39.9407 78.2943 48.4276 90.0492 58.8333Z" fill="#E11D48"/>
|
||||
<path d="M21.8495 119.662C29.6634 110.968 41.6513 101.546 55.1792 93.5802C69.5711 85.1095 83.9662 79.1916 96.6012 76.4834C81.0995 59.736 60.8901 45.3441 46.5791 40.5975C46.4788 40.5652 46.3849 40.5361 46.3008 40.5069C25.0851 33.6151 9.95223 52.0936 4.03111 70.3229C-1.89001 88.5522 -0.511647 112.398 20.7041 119.29C20.9921 119.384 21.3803 119.51 21.8495 119.662Z" fill="#FB7185"/>
|
||||
<path d="M188.925 70.0154C183.004 51.7861 167.871 33.3077 146.656 40.1995C146.365 40.2933 145.976 40.4195 145.51 40.5716C144.297 52.1971 140.139 66.8672 133.875 81.2623C127.213 96.5731 119.043 109.823 110.414 119.442C132.798 123.881 157.608 123.642 171.974 119.073C172.074 119.041 172.168 119.009 172.252 118.983C193.468 112.088 194.847 88.2415 188.925 70.0154Z" fill="#F472B6"/>
|
||||
<path d="M77.4498 137.484C73.8421 121.18 72.6611 105.659 73.9877 92.8069C53.2703 102.375 33.3391 117.148 24.3992 129.291C24.3377 129.375 24.2794 129.456 24.2277 129.528C11.1171 147.576 24.0141 167.678 39.5223 178.941C55.0272 190.208 78.1325 196.265 91.2463 178.217C91.4275 177.971 91.667 177.641 91.9549 177.243C86.1018 167.125 80.8407 152.811 77.4498 137.484Z" fill="#BE185D"/>
|
||||
<path d="M168.379 128.363C156.948 130.802 141.712 131.382 126.084 129.871C109.463 128.266 94.3395 124.59 82.5232 119.355C85.2184 142.014 93.1132 165.536 101.898 177.79C101.959 177.874 102.018 177.955 102.069 178.026C115.18 196.074 138.285 190.017 153.793 178.751C169.298 167.484 182.199 147.382 169.088 129.337C168.907 129.091 168.667 128.761 168.379 128.363Z" fill="#F9A8D4"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
10
mobile/assets/app-icons/app-icon-classic.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" fill="#FFFFFF"/>
|
||||
<g transform="translate(21.12 21.12) scale(0.78)">
|
||||
<path d="M90.0492 58.8333C102.552 69.9022 112.627 81.7638 119.111 92.9428C130.248 73.0245 137.69 49.3562 137.784 34.2816C137.784 34.1749 137.784 34.0778 137.784 33.9872C137.784 11.6811 115.533 3 96.3651 3C77.1975 3 54.9464 11.6811 54.9464 33.9872C54.9464 34.2913 54.9464 34.699 54.9464 35.1908C65.6303 39.9407 78.2943 48.4276 90.0492 58.8333Z" fill="#FA2921"/>
|
||||
<path d="M21.8495 119.662C29.6634 110.968 41.6513 101.546 55.1792 93.5802C69.5711 85.1095 83.9662 79.1916 96.6012 76.4834C81.0995 59.736 60.8901 45.3441 46.5791 40.5975C46.4788 40.5652 46.3849 40.5361 46.3008 40.5069C25.0851 33.6151 9.95223 52.0936 4.03111 70.3229C-1.89001 88.5522 -0.511647 112.398 20.7041 119.29C20.9921 119.384 21.3803 119.51 21.8495 119.662Z" fill="#ED79B5"/>
|
||||
<path d="M188.925 70.0154C183.004 51.7861 167.871 33.3077 146.656 40.1995C146.365 40.2933 145.976 40.4195 145.51 40.5716C144.297 52.1971 140.139 66.8672 133.875 81.2623C127.213 96.5731 119.043 109.823 110.414 119.442C132.798 123.881 157.608 123.642 171.974 119.073C172.074 119.041 172.168 119.009 172.252 118.983C193.468 112.088 194.847 88.2415 188.925 70.0154Z" fill="#FFB400"/>
|
||||
<path d="M77.4498 137.484C73.8421 121.18 72.6611 105.659 73.9877 92.8069C53.2703 102.375 33.3391 117.148 24.3992 129.291C24.3377 129.375 24.2794 129.456 24.2277 129.528C11.1171 147.576 24.0141 167.678 39.5223 178.941C55.0272 190.208 78.1325 196.265 91.2463 178.217C91.4275 177.971 91.667 177.641 91.9549 177.243C86.1018 167.125 80.8407 152.811 77.4498 137.484Z" fill="#1E83F7"/>
|
||||
<path d="M168.379 128.363C156.948 130.802 141.712 131.382 126.084 129.871C109.463 128.266 94.3395 124.59 82.5232 119.355C85.2184 142.014 93.1132 165.536 101.898 177.79C101.959 177.874 102.018 177.955 102.069 178.026C115.18 196.074 138.285 190.017 153.793 178.751C169.298 167.484 182.199 147.382 169.088 129.337C168.907 129.091 168.667 128.761 168.379 128.363Z" fill="#18C249"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
10
mobile/assets/app-icons/app-icon-forest.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" fill="#FFFFFF"/>
|
||||
<g transform="translate(21.12 21.12) scale(0.78)">
|
||||
<path d="M90.0492 58.8333C102.552 69.9022 112.627 81.7638 119.111 92.9428C130.248 73.0245 137.69 49.3562 137.784 34.2816C137.784 34.1749 137.784 34.0778 137.784 33.9872C137.784 11.6811 115.533 3 96.3651 3C77.1975 3 54.9464 11.6811 54.9464 33.9872C54.9464 34.2913 54.9464 34.699 54.9464 35.1908C65.6303 39.9407 78.2943 48.4276 90.0492 58.8333Z" fill="#16A34A"/>
|
||||
<path d="M21.8495 119.662C29.6634 110.968 41.6513 101.546 55.1792 93.5802C69.5711 85.1095 83.9662 79.1916 96.6012 76.4834C81.0995 59.736 60.8901 45.3441 46.5791 40.5975C46.4788 40.5652 46.3849 40.5361 46.3008 40.5069C25.0851 33.6151 9.95223 52.0936 4.03111 70.3229C-1.89001 88.5522 -0.511647 112.398 20.7041 119.29C20.9921 119.384 21.3803 119.51 21.8495 119.662Z" fill="#84CC16"/>
|
||||
<path d="M188.925 70.0154C183.004 51.7861 167.871 33.3077 146.656 40.1995C146.365 40.2933 145.976 40.4195 145.51 40.5716C144.297 52.1971 140.139 66.8672 133.875 81.2623C127.213 96.5731 119.043 109.823 110.414 119.442C132.798 123.881 157.608 123.642 171.974 119.073C172.074 119.041 172.168 119.009 172.252 118.983C193.468 112.088 194.847 88.2415 188.925 70.0154Z" fill="#4ADE80"/>
|
||||
<path d="M77.4498 137.484C73.8421 121.18 72.6611 105.659 73.9877 92.8069C53.2703 102.375 33.3391 117.148 24.3992 129.291C24.3377 129.375 24.2794 129.456 24.2277 129.528C11.1171 147.576 24.0141 167.678 39.5223 178.941C55.0272 190.208 78.1325 196.265 91.2463 178.217C91.4275 177.971 91.667 177.641 91.9549 177.243C86.1018 167.125 80.8407 152.811 77.4498 137.484Z" fill="#15803D"/>
|
||||
<path d="M168.379 128.363C156.948 130.802 141.712 131.382 126.084 129.871C109.463 128.266 94.3395 124.59 82.5232 119.355C85.2184 142.014 93.1132 165.536 101.898 177.79C101.959 177.874 102.018 177.955 102.069 178.026C115.18 196.074 138.285 190.017 153.793 178.751C169.298 167.484 182.199 147.382 169.088 129.337C168.907 129.091 168.667 128.761 168.379 128.363Z" fill="#65A30D"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
10
mobile/assets/app-icons/app-icon-gold.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" fill="#1C1917"/>
|
||||
<g transform="translate(21.12 21.12) scale(0.78)">
|
||||
<path d="M90.0492 58.8333C102.552 69.9022 112.627 81.7638 119.111 92.9428C130.248 73.0245 137.69 49.3562 137.784 34.2816C137.784 34.1749 137.784 34.0778 137.784 33.9872C137.784 11.6811 115.533 3 96.3651 3C77.1975 3 54.9464 11.6811 54.9464 33.9872C54.9464 34.2913 54.9464 34.699 54.9464 35.1908C65.6303 39.9407 78.2943 48.4276 90.0492 58.8333Z" fill="#FBBF24"/>
|
||||
<path d="M21.8495 119.662C29.6634 110.968 41.6513 101.546 55.1792 93.5802C69.5711 85.1095 83.9662 79.1916 96.6012 76.4834C81.0995 59.736 60.8901 45.3441 46.5791 40.5975C46.4788 40.5652 46.3849 40.5361 46.3008 40.5069C25.0851 33.6151 9.95223 52.0936 4.03111 70.3229C-1.89001 88.5522 -0.511647 112.398 20.7041 119.29C20.9921 119.384 21.3803 119.51 21.8495 119.662Z" fill="#FDE68A"/>
|
||||
<path d="M188.925 70.0154C183.004 51.7861 167.871 33.3077 146.656 40.1995C146.365 40.2933 145.976 40.4195 145.51 40.5716C144.297 52.1971 140.139 66.8672 133.875 81.2623C127.213 96.5731 119.043 109.823 110.414 119.442C132.798 123.881 157.608 123.642 171.974 119.073C172.074 119.041 172.168 119.009 172.252 118.983C193.468 112.088 194.847 88.2415 188.925 70.0154Z" fill="#F59E0B"/>
|
||||
<path d="M77.4498 137.484C73.8421 121.18 72.6611 105.659 73.9877 92.8069C53.2703 102.375 33.3391 117.148 24.3992 129.291C24.3377 129.375 24.2794 129.456 24.2277 129.528C11.1171 147.576 24.0141 167.678 39.5223 178.941C55.0272 190.208 78.1325 196.265 91.2463 178.217C91.4275 177.971 91.667 177.641 91.9549 177.243C86.1018 167.125 80.8407 152.811 77.4498 137.484Z" fill="#D97706"/>
|
||||
<path d="M168.379 128.363C156.948 130.802 141.712 131.382 126.084 129.871C109.463 128.266 94.3395 124.59 82.5232 119.355C85.2184 142.014 93.1132 165.536 101.898 177.79C101.959 177.874 102.018 177.955 102.069 178.026C115.18 196.074 138.285 190.017 153.793 178.751C169.298 167.484 182.199 147.382 169.088 129.337C168.907 129.091 168.667 128.761 168.379 128.363Z" fill="#FCD34D"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
10
mobile/assets/app-icons/app-icon-ink.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" fill="#FFFFFF"/>
|
||||
<g transform="translate(21.12 21.12) scale(0.78)">
|
||||
<path d="M90.0492 58.8333C102.552 69.9022 112.627 81.7638 119.111 92.9428C130.248 73.0245 137.69 49.3562 137.784 34.2816C137.784 34.1749 137.784 34.0778 137.784 33.9872C137.784 11.6811 115.533 3 96.3651 3C77.1975 3 54.9464 11.6811 54.9464 33.9872C54.9464 34.2913 54.9464 34.699 54.9464 35.1908C65.6303 39.9407 78.2943 48.4276 90.0492 58.8333Z" fill="#111827"/>
|
||||
<path d="M21.8495 119.662C29.6634 110.968 41.6513 101.546 55.1792 93.5802C69.5711 85.1095 83.9662 79.1916 96.6012 76.4834C81.0995 59.736 60.8901 45.3441 46.5791 40.5975C46.4788 40.5652 46.3849 40.5361 46.3008 40.5069C25.0851 33.6151 9.95223 52.0936 4.03111 70.3229C-1.89001 88.5522 -0.511647 112.398 20.7041 119.29C20.9921 119.384 21.3803 119.51 21.8495 119.662Z" fill="#6B7280"/>
|
||||
<path d="M188.925 70.0154C183.004 51.7861 167.871 33.3077 146.656 40.1995C146.365 40.2933 145.976 40.4195 145.51 40.5716C144.297 52.1971 140.139 66.8672 133.875 81.2623C127.213 96.5731 119.043 109.823 110.414 119.442C132.798 123.881 157.608 123.642 171.974 119.073C172.074 119.041 172.168 119.009 172.252 118.983C193.468 112.088 194.847 88.2415 188.925 70.0154Z" fill="#374151"/>
|
||||
<path d="M77.4498 137.484C73.8421 121.18 72.6611 105.659 73.9877 92.8069C53.2703 102.375 33.3391 117.148 24.3992 129.291C24.3377 129.375 24.2794 129.456 24.2277 129.528C11.1171 147.576 24.0141 167.678 39.5223 178.941C55.0272 190.208 78.1325 196.265 91.2463 178.217C91.4275 177.971 91.667 177.641 91.9549 177.243C86.1018 167.125 80.8407 152.811 77.4498 137.484Z" fill="#9CA3AF"/>
|
||||
<path d="M168.379 128.363C156.948 130.802 141.712 131.382 126.084 129.871C109.463 128.266 94.3395 124.59 82.5232 119.355C85.2184 142.014 93.1132 165.536 101.898 177.79C101.959 177.874 102.018 177.955 102.069 178.026C115.18 196.074 138.285 190.017 153.793 178.751C169.298 167.484 182.199 147.382 169.088 129.337C168.907 129.091 168.667 128.761 168.379 128.363Z" fill="#4B5563"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
10
mobile/assets/app-icons/app-icon-midnight.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" fill="#0F172A"/>
|
||||
<g transform="translate(21.12 21.12) scale(0.78)">
|
||||
<path d="M90.0492 58.8333C102.552 69.9022 112.627 81.7638 119.111 92.9428C130.248 73.0245 137.69 49.3562 137.784 34.2816C137.784 34.1749 137.784 34.0778 137.784 33.9872C137.784 11.6811 115.533 3 96.3651 3C77.1975 3 54.9464 11.6811 54.9464 33.9872C54.9464 34.2913 54.9464 34.699 54.9464 35.1908C65.6303 39.9407 78.2943 48.4276 90.0492 58.8333Z" fill="#FA2921"/>
|
||||
<path d="M21.8495 119.662C29.6634 110.968 41.6513 101.546 55.1792 93.5802C69.5711 85.1095 83.9662 79.1916 96.6012 76.4834C81.0995 59.736 60.8901 45.3441 46.5791 40.5975C46.4788 40.5652 46.3849 40.5361 46.3008 40.5069C25.0851 33.6151 9.95223 52.0936 4.03111 70.3229C-1.89001 88.5522 -0.511647 112.398 20.7041 119.29C20.9921 119.384 21.3803 119.51 21.8495 119.662Z" fill="#ED79B5"/>
|
||||
<path d="M188.925 70.0154C183.004 51.7861 167.871 33.3077 146.656 40.1995C146.365 40.2933 145.976 40.4195 145.51 40.5716C144.297 52.1971 140.139 66.8672 133.875 81.2623C127.213 96.5731 119.043 109.823 110.414 119.442C132.798 123.881 157.608 123.642 171.974 119.073C172.074 119.041 172.168 119.009 172.252 118.983C193.468 112.088 194.847 88.2415 188.925 70.0154Z" fill="#FFB400"/>
|
||||
<path d="M77.4498 137.484C73.8421 121.18 72.6611 105.659 73.9877 92.8069C53.2703 102.375 33.3391 117.148 24.3992 129.291C24.3377 129.375 24.2794 129.456 24.2277 129.528C11.1171 147.576 24.0141 167.678 39.5223 178.941C55.0272 190.208 78.1325 196.265 91.2463 178.217C91.4275 177.971 91.667 177.641 91.9549 177.243C86.1018 167.125 80.8407 152.811 77.4498 137.484Z" fill="#1E83F7"/>
|
||||
<path d="M168.379 128.363C156.948 130.802 141.712 131.382 126.084 129.871C109.463 128.266 94.3395 124.59 82.5232 119.355C85.2184 142.014 93.1132 165.536 101.898 177.79C101.959 177.874 102.018 177.955 102.069 178.026C115.18 196.074 138.285 190.017 153.793 178.751C169.298 167.484 182.199 147.382 169.088 129.337C168.907 129.091 168.667 128.761 168.379 128.363Z" fill="#18C249"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
10
mobile/assets/app-icons/app-icon-neon.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" fill="#0A0A12"/>
|
||||
<g transform="translate(21.12 21.12) scale(0.78)">
|
||||
<path d="M90.0492 58.8333C102.552 69.9022 112.627 81.7638 119.111 92.9428C130.248 73.0245 137.69 49.3562 137.784 34.2816C137.784 34.1749 137.784 34.0778 137.784 33.9872C137.784 11.6811 115.533 3 96.3651 3C77.1975 3 54.9464 11.6811 54.9464 33.9872C54.9464 34.2913 54.9464 34.699 54.9464 35.1908C65.6303 39.9407 78.2943 48.4276 90.0492 58.8333Z" fill="#FF2D95"/>
|
||||
<path d="M21.8495 119.662C29.6634 110.968 41.6513 101.546 55.1792 93.5802C69.5711 85.1095 83.9662 79.1916 96.6012 76.4834C81.0995 59.736 60.8901 45.3441 46.5791 40.5975C46.4788 40.5652 46.3849 40.5361 46.3008 40.5069C25.0851 33.6151 9.95223 52.0936 4.03111 70.3229C-1.89001 88.5522 -0.511647 112.398 20.7041 119.29C20.9921 119.384 21.3803 119.51 21.8495 119.662Z" fill="#00E5FF"/>
|
||||
<path d="M188.925 70.0154C183.004 51.7861 167.871 33.3077 146.656 40.1995C146.365 40.2933 145.976 40.4195 145.51 40.5716C144.297 52.1971 140.139 66.8672 133.875 81.2623C127.213 96.5731 119.043 109.823 110.414 119.442C132.798 123.881 157.608 123.642 171.974 119.073C172.074 119.041 172.168 119.009 172.252 118.983C193.468 112.088 194.847 88.2415 188.925 70.0154Z" fill="#FFD600"/>
|
||||
<path d="M77.4498 137.484C73.8421 121.18 72.6611 105.659 73.9877 92.8069C53.2703 102.375 33.3391 117.148 24.3992 129.291C24.3377 129.375 24.2794 129.456 24.2277 129.528C11.1171 147.576 24.0141 167.678 39.5223 178.941C55.0272 190.208 78.1325 196.265 91.2463 178.217C91.4275 177.971 91.667 177.641 91.9549 177.243C86.1018 167.125 80.8407 152.811 77.4498 137.484Z" fill="#7C4DFF"/>
|
||||
<path d="M168.379 128.363C156.948 130.802 141.712 131.382 126.084 129.871C109.463 128.266 94.3395 124.59 82.5232 119.355C85.2184 142.014 93.1132 165.536 101.898 177.79C101.959 177.874 102.018 177.955 102.069 178.026C115.18 196.074 138.285 190.017 153.793 178.751C169.298 167.484 182.199 147.382 169.088 129.337C168.907 129.091 168.667 128.761 168.379 128.363Z" fill="#00FF9D"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
10
mobile/assets/app-icons/app-icon-ocean.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" fill="#FFFFFF"/>
|
||||
<g transform="translate(21.12 21.12) scale(0.78)">
|
||||
<path d="M90.0492 58.8333C102.552 69.9022 112.627 81.7638 119.111 92.9428C130.248 73.0245 137.69 49.3562 137.784 34.2816C137.784 34.1749 137.784 34.0778 137.784 33.9872C137.784 11.6811 115.533 3 96.3651 3C77.1975 3 54.9464 11.6811 54.9464 33.9872C54.9464 34.2913 54.9464 34.699 54.9464 35.1908C65.6303 39.9407 78.2943 48.4276 90.0492 58.8333Z" fill="#0284C7"/>
|
||||
<path d="M21.8495 119.662C29.6634 110.968 41.6513 101.546 55.1792 93.5802C69.5711 85.1095 83.9662 79.1916 96.6012 76.4834C81.0995 59.736 60.8901 45.3441 46.5791 40.5975C46.4788 40.5652 46.3849 40.5361 46.3008 40.5069C25.0851 33.6151 9.95223 52.0936 4.03111 70.3229C-1.89001 88.5522 -0.511647 112.398 20.7041 119.29C20.9921 119.384 21.3803 119.51 21.8495 119.662Z" fill="#38BDF8"/>
|
||||
<path d="M188.925 70.0154C183.004 51.7861 167.871 33.3077 146.656 40.1995C146.365 40.2933 145.976 40.4195 145.51 40.5716C144.297 52.1971 140.139 66.8672 133.875 81.2623C127.213 96.5731 119.043 109.823 110.414 119.442C132.798 123.881 157.608 123.642 171.974 119.073C172.074 119.041 172.168 119.009 172.252 118.983C193.468 112.088 194.847 88.2415 188.925 70.0154Z" fill="#1D4ED8"/>
|
||||
<path d="M77.4498 137.484C73.8421 121.18 72.6611 105.659 73.9877 92.8069C53.2703 102.375 33.3391 117.148 24.3992 129.291C24.3377 129.375 24.2794 129.456 24.2277 129.528C11.1171 147.576 24.0141 167.678 39.5223 178.941C55.0272 190.208 78.1325 196.265 91.2463 178.217C91.4275 177.971 91.667 177.641 91.9549 177.243C86.1018 167.125 80.8407 152.811 77.4498 137.484Z" fill="#0891B2"/>
|
||||
<path d="M168.379 128.363C156.948 130.802 141.712 131.382 126.084 129.871C109.463 128.266 94.3395 124.59 82.5232 119.355C85.2184 142.014 93.1132 165.536 101.898 177.79C101.959 177.874 102.018 177.955 102.069 178.026C115.18 196.074 138.285 190.017 153.793 178.751C169.298 167.484 182.199 147.382 169.088 129.337C168.907 129.091 168.667 128.761 168.379 128.363Z" fill="#14B8A6"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
10
mobile/assets/app-icons/app-icon-sunset.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="192" height="192" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" fill="#1E1B4B"/>
|
||||
<g transform="translate(21.12 21.12) scale(0.78)">
|
||||
<path d="M90.0492 58.8333C102.552 69.9022 112.627 81.7638 119.111 92.9428C130.248 73.0245 137.69 49.3562 137.784 34.2816C137.784 34.1749 137.784 34.0778 137.784 33.9872C137.784 11.6811 115.533 3 96.3651 3C77.1975 3 54.9464 11.6811 54.9464 33.9872C54.9464 34.2913 54.9464 34.699 54.9464 35.1908C65.6303 39.9407 78.2943 48.4276 90.0492 58.8333Z" fill="#F97316"/>
|
||||
<path d="M21.8495 119.662C29.6634 110.968 41.6513 101.546 55.1792 93.5802C69.5711 85.1095 83.9662 79.1916 96.6012 76.4834C81.0995 59.736 60.8901 45.3441 46.5791 40.5975C46.4788 40.5652 46.3849 40.5361 46.3008 40.5069C25.0851 33.6151 9.95223 52.0936 4.03111 70.3229C-1.89001 88.5522 -0.511647 112.398 20.7041 119.29C20.9921 119.384 21.3803 119.51 21.8495 119.662Z" fill="#7C3AED"/>
|
||||
<path d="M188.925 70.0154C183.004 51.7861 167.871 33.3077 146.656 40.1995C146.365 40.2933 145.976 40.4195 145.51 40.5716C144.297 52.1971 140.139 66.8672 133.875 81.2623C127.213 96.5731 119.043 109.823 110.414 119.442C132.798 123.881 157.608 123.642 171.974 119.073C172.074 119.041 172.168 119.009 172.252 118.983C193.468 112.088 194.847 88.2415 188.925 70.0154Z" fill="#FACC15"/>
|
||||
<path d="M77.4498 137.484C73.8421 121.18 72.6611 105.659 73.9877 92.8069C53.2703 102.375 33.3391 117.148 24.3992 129.291C24.3377 129.375 24.2794 129.456 24.2277 129.528C11.1171 147.576 24.0141 167.678 39.5223 178.941C55.0272 190.208 78.1325 196.265 91.2463 178.217C91.4275 177.971 91.667 177.641 91.9549 177.243C86.1018 167.125 80.8407 152.811 77.4498 137.484Z" fill="#C026D3"/>
|
||||
<path d="M168.379 128.363C156.948 130.802 141.712 131.382 126.084 129.871C109.463 128.266 94.3395 124.59 82.5232 119.355C85.2184 142.014 93.1132 165.536 101.898 177.79C101.959 177.874 102.018 177.955 102.069 178.026C115.18 196.074 138.285 190.017 153.793 178.751C169.298 167.484 182.199 147.382 169.088 129.337C168.907 129.091 168.667 128.761 168.379 128.363Z" fill="#F43F5E"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
20
mobile/integration_test/app_icon_test.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/platform/app_icon_api.g.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
testWidgets('switches the launcher icon and reports it back', (tester) async {
|
||||
final api = AppIconApi();
|
||||
|
||||
await api.setAppIcon('neon');
|
||||
expect(await api.getAppIcon(), 'neon');
|
||||
|
||||
await api.setAppIcon('classic');
|
||||
expect(await api.getAppIcon(), 'classic');
|
||||
|
||||
await expectLater(api.setAppIcon('not-an-icon'), throwsA(isA<PlatformException>()));
|
||||
});
|
||||
}
|
||||
@@ -16,8 +16,6 @@ PODS:
|
||||
- Flutter
|
||||
- native_video_player (1.0.0):
|
||||
- Flutter
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
- share_handler_ios (0.0.14):
|
||||
- Flutter
|
||||
- share_handler_ios/share_handler_ios_models (= 0.0.14)
|
||||
@@ -36,7 +34,6 @@ DEPENDENCIES:
|
||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||
- home_widget (from `.symlinks/plugins/home_widget/ios`)
|
||||
- native_video_player (from `.symlinks/plugins/native_video_player/ios`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- share_handler_ios (from `.symlinks/plugins/share_handler_ios/ios`)
|
||||
- share_handler_ios_models (from `.symlinks/plugins/share_handler_ios/ios/Models`)
|
||||
|
||||
@@ -57,8 +54,6 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/home_widget/ios"
|
||||
native_video_player:
|
||||
:path: ".symlinks/plugins/native_video_player/ios"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
share_handler_ios:
|
||||
:path: ".symlinks/plugins/share_handler_ios/ios"
|
||||
share_handler_ios_models:
|
||||
@@ -73,7 +68,6 @@ SPEC CHECKSUMS:
|
||||
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
|
||||
home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f
|
||||
native_video_player: b65c58951ede2f93d103a25366bdebca95081265
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
share_handler_ios: e2244e990f826b2c8eaa291ac3831569438ba0fb
|
||||
share_handler_ios_models: fc638c9b4330dc7f082586c92aee9dfa0b87b871
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 77;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@@ -153,11 +153,15 @@
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
B231F52D2E93A44A00BC45D1 /* Core */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
path = Core;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
path = Sync;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -179,6 +183,8 @@
|
||||
};
|
||||
FEE084F22EC172080045228E /* Schemas */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
path = Schemas;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -370,7 +376,6 @@
|
||||
FAC6F89A2D287C890078CB2F /* Embed Foundation Extensions */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
513DA7292DED6106813332F4 /* [CP] Embed Pods Frameworks */,
|
||||
2FA39DEC809D6D7C4A01EFCB /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -464,7 +469,7 @@
|
||||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
packageReferences = (
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
|
||||
FEE084F62EC172460045228E /* XCRemoteSwiftPackageReference "sqlite-data" */,
|
||||
FEE084F92EC1725A0045228E /* XCRemoteSwiftPackageReference "swift-http-structured-headers" */,
|
||||
);
|
||||
@@ -511,27 +516,6 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
2FA39DEC809D6D7C4A01EFCB /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
@@ -556,14 +540,10 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
@@ -772,6 +752,7 @@
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
@@ -917,6 +898,7 @@
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
@@ -946,6 +928,7 @@
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
@@ -1261,7 +1244,7 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCLocalSwiftPackageReference section */
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
|
||||
};
|
||||
|
||||
@@ -31,6 +31,7 @@ import native_video_player
|
||||
RemoteImageApiSetup.setUp(binaryMessenger: messenger, api: RemoteImageApiImpl())
|
||||
BackgroundWorkerFgHostApiSetup.setUp(binaryMessenger: messenger, api: BackgroundWorkerApiImpl())
|
||||
ConnectivityApiSetup.setUp(binaryMessenger: messenger, api: ConnectivityApiImpl())
|
||||
AppIconApiSetup.setUp(binaryMessenger: messenger, api: AppIconApiImpl())
|
||||
NetworkApiSetup.setUp(binaryMessenger: messenger, api: NetworkApiImpl())
|
||||
}
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "1024.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "1024.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "1024.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
BIN
mobile/ios/Runner/Assets.xcassets/AppIconInk.appiconset/1024.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "1024.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "1024.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "1024.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "1024.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "1024.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
114
mobile/ios/Runner/Core/AppIcon.g.swift
generated
Normal file
@@ -0,0 +1,114 @@
|
||||
// Autogenerated from Pigeon (v26.3.4), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
|
||||
import Foundation
|
||||
|
||||
#if os(iOS)
|
||||
import Flutter
|
||||
#elseif os(macOS)
|
||||
import FlutterMacOS
|
||||
#else
|
||||
#error("Unsupported platform.")
|
||||
#endif
|
||||
|
||||
private func wrapResult(_ result: Any?) -> [Any?] {
|
||||
return [result]
|
||||
}
|
||||
|
||||
private func wrapError(_ error: Any) -> [Any?] {
|
||||
if let pigeonError = error as? PigeonError {
|
||||
return [
|
||||
pigeonError.code,
|
||||
pigeonError.message,
|
||||
pigeonError.details,
|
||||
]
|
||||
}
|
||||
if let flutterError = error as? FlutterError {
|
||||
return [
|
||||
flutterError.code,
|
||||
flutterError.message,
|
||||
flutterError.details,
|
||||
]
|
||||
}
|
||||
return [
|
||||
"\(error)",
|
||||
"\(Swift.type(of: error))",
|
||||
"Stacktrace: \(Thread.callStackSymbols)",
|
||||
]
|
||||
}
|
||||
|
||||
private func isNullish(_ value: Any?) -> Bool {
|
||||
return value is NSNull || value == nil
|
||||
}
|
||||
|
||||
private func nilOrValue<T>(_ value: Any?) -> T? {
|
||||
if value is NSNull { return nil }
|
||||
return value as! T?
|
||||
}
|
||||
|
||||
|
||||
private class AppIconPigeonCodecReader: FlutterStandardReader {
|
||||
}
|
||||
|
||||
private class AppIconPigeonCodecWriter: FlutterStandardWriter {
|
||||
}
|
||||
|
||||
private class AppIconPigeonCodecReaderWriter: FlutterStandardReaderWriter {
|
||||
override func reader(with data: Data) -> FlutterStandardReader {
|
||||
return AppIconPigeonCodecReader(data: data)
|
||||
}
|
||||
|
||||
override func writer(with data: NSMutableData) -> FlutterStandardWriter {
|
||||
return AppIconPigeonCodecWriter(data: data)
|
||||
}
|
||||
}
|
||||
|
||||
class AppIconPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
|
||||
static let shared = AppIconPigeonCodec(readerWriter: AppIconPigeonCodecReaderWriter())
|
||||
}
|
||||
|
||||
|
||||
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
||||
protocol AppIconApi {
|
||||
func setAppIcon(iconId: String, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
func getAppIcon() throws -> String
|
||||
}
|
||||
|
||||
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
||||
class AppIconApiSetup {
|
||||
static var codec: FlutterStandardMessageCodec { AppIconPigeonCodec.shared }
|
||||
/// Sets up an instance of `AppIconApi` to handle messages through the `binaryMessenger`.
|
||||
static func setUp(binaryMessenger: FlutterBinaryMessenger, api: AppIconApi?, messageChannelSuffix: String = "") {
|
||||
let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
|
||||
let setAppIconChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.AppIconApi.setAppIcon\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
setAppIconChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let iconIdArg = args[0] as! String
|
||||
api.setAppIcon(iconId: iconIdArg) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
reply(wrapResult(nil))
|
||||
case .failure(let error):
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setAppIconChannel.setMessageHandler(nil)
|
||||
}
|
||||
let getAppIconChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.AppIconApi.getAppIcon\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
getAppIconChannel.setMessageHandler { _, reply in
|
||||
do {
|
||||
let result = try api.getAppIcon()
|
||||
reply(wrapResult(result))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getAppIconChannel.setMessageHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
45
mobile/ios/Runner/Core/AppIconApiImpl.swift
Normal file
@@ -0,0 +1,45 @@
|
||||
import UIKit
|
||||
|
||||
/// Switches between the alternate app icons bundled in the asset catalog
|
||||
/// (`AppIconMidnight`, `AppIconOcean`, ...). The "classic" id maps to the
|
||||
/// primary `AppIcon`.
|
||||
class AppIconApiImpl: AppIconApi {
|
||||
private static let defaultIcon = "classic"
|
||||
private static let assetPrefix = "AppIcon"
|
||||
|
||||
private func assetName(for iconId: String) -> String? {
|
||||
guard iconId != Self.defaultIcon else { return nil }
|
||||
return Self.assetPrefix + iconId.prefix(1).uppercased() + String(iconId.dropFirst())
|
||||
}
|
||||
|
||||
func setAppIcon(iconId: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
let iconName = assetName(for: iconId)
|
||||
DispatchQueue.main.async {
|
||||
guard UIApplication.shared.supportsAlternateIcons else {
|
||||
completion(
|
||||
.failure(
|
||||
PigeonError(
|
||||
code: "unsupported",
|
||||
message: "Alternate icons are not supported on this device",
|
||||
details: nil
|
||||
)))
|
||||
return
|
||||
}
|
||||
UIApplication.shared.setAlternateIconName(iconName) { error in
|
||||
if let error = error {
|
||||
completion(
|
||||
.failure(
|
||||
PigeonError(code: "set_icon_failed", message: error.localizedDescription, details: nil)
|
||||
))
|
||||
} else {
|
||||
completion(.success(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getAppIcon() throws -> String {
|
||||
guard let name = UIApplication.shared.alternateIconName else { return Self.defaultIcon }
|
||||
return String(name.dropFirst(Self.assetPrefix.count)).lowercased()
|
||||
}
|
||||
}
|
||||
21
mobile/lib/constants/app_icons.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
/// The selectable launcher icons. The [name] of each variant is the id shared
|
||||
/// with the native implementations (Android activity-aliases and iOS
|
||||
/// alternate icon asset names), so renaming a value is a breaking change.
|
||||
enum AppIconVariant {
|
||||
classic,
|
||||
midnight,
|
||||
ocean,
|
||||
sunset,
|
||||
forest,
|
||||
blossom,
|
||||
ink,
|
||||
neon,
|
||||
gold;
|
||||
|
||||
String get assetPath => 'assets/app-icons/app-icon-$name.svg';
|
||||
|
||||
String get translationKey => 'app_icon_$name';
|
||||
|
||||
static AppIconVariant fromId(String id) =>
|
||||
AppIconVariant.values.firstWhere((variant) => variant.name == id, orElse: () => AppIconVariant.classic);
|
||||
}
|
||||
109
mobile/lib/platform/app_icon_api.g.dart
generated
Normal file
@@ -0,0 +1,109 @@
|
||||
// Autogenerated from Pigeon (v26.3.4), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
// ignore_for_file: unused_import, unused_shown_name
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data' show Float64List, Int32List, Int64List;
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:meta/meta.dart' show immutable, protected, visibleForTesting;
|
||||
|
||||
Object? _extractReplyValueOrThrow(
|
||||
List<Object?>? replyList,
|
||||
String channelName, {
|
||||
required bool isNullValid,
|
||||
}) {
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel: "$channelName".',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) {
|
||||
throw PlatformException(
|
||||
code: 'null-error',
|
||||
message: 'Host platform returned null value for non-null return value.',
|
||||
);
|
||||
}
|
||||
return replyList.firstOrNull;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class _PigeonCodec extends StandardMessageCodec {
|
||||
const _PigeonCodec();
|
||||
@override
|
||||
void writeValue(WriteBuffer buffer, Object? value) {
|
||||
if (value is int) {
|
||||
buffer.putUint8(4);
|
||||
buffer.putInt64(value);
|
||||
} else {
|
||||
super.writeValue(buffer, value);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||
switch (type) {
|
||||
default:
|
||||
return super.readValueOfType(type, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AppIconApi {
|
||||
/// Constructor for [AppIconApi]. The [binaryMessenger] named argument is
|
||||
/// available for dependency injection. If it is left null, the default
|
||||
/// BinaryMessenger will be used which routes to the host platform.
|
||||
AppIconApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||
: pigeonVar_binaryMessenger = binaryMessenger,
|
||||
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
final BinaryMessenger? pigeonVar_binaryMessenger;
|
||||
|
||||
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||
|
||||
final String pigeonVar_messageChannelSuffix;
|
||||
|
||||
Future<void> setAppIcon(String iconId) async {
|
||||
final pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.AppIconApi.setAppIcon$pigeonVar_messageChannelSuffix';
|
||||
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[iconId]);
|
||||
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||
|
||||
_extractReplyValueOrThrow(
|
||||
pigeonVar_replyList,
|
||||
pigeonVar_channelName,
|
||||
isNullValid: true,
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
Future<String> getAppIcon() async {
|
||||
final pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.AppIconApi.getAppIcon$pigeonVar_messageChannelSuffix';
|
||||
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
|
||||
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||
|
||||
final Object? pigeonVar_replyValue = _extractReplyValueOrThrow(
|
||||
pigeonVar_replyList,
|
||||
pigeonVar_channelName,
|
||||
isNullValid: false,
|
||||
)
|
||||
;
|
||||
return pigeonVar_replyValue! as String;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/services/background_worker.service.dart';
|
||||
import 'package:immich_mobile/platform/app_icon_api.g.dart';
|
||||
import 'package:immich_mobile/platform/background_worker_api.g.dart';
|
||||
import 'package:immich_mobile/platform/background_worker_lock_api.g.dart';
|
||||
import 'package:immich_mobile/platform/connectivity_api.g.dart';
|
||||
@@ -21,6 +22,8 @@ final permissionApiProvider = Provider<PermissionApi>((_) => PermissionApi());
|
||||
|
||||
final connectivityApiProvider = Provider<ConnectivityApi>((_) => ConnectivityApi());
|
||||
|
||||
final appIconApiProvider = Provider<AppIconApi>((_) => AppIconApi());
|
||||
|
||||
final localImageApi = LocalImageApi();
|
||||
|
||||
final remoteImageApi = RemoteImageApi();
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/app_icons.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
|
||||
|
||||
class AppIconSetting extends HookConsumerWidget {
|
||||
const AppIconSetting({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final appIconApi = ref.watch(appIconApiProvider);
|
||||
final selectedIcon = useState(AppIconVariant.classic);
|
||||
|
||||
useEffect(() {
|
||||
appIconApi
|
||||
.getAppIcon()
|
||||
.then((id) {
|
||||
selectedIcon.value = AppIconVariant.fromId(id);
|
||||
})
|
||||
.catchError((_) {});
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
Future<void> onIconSelected(AppIconVariant variant) async {
|
||||
if (variant == selectedIcon.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await appIconApi.setAppIcon(variant.name);
|
||||
selectedIcon.value = variant;
|
||||
} catch (_) {
|
||||
if (context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: 'app_icon_change_failed'.t(context: context),
|
||||
toastType: ToastType.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SettingGroupTitle(
|
||||
title: "app_icon_title".t(context: context),
|
||||
icon: Icons.apps_outlined,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: Text(
|
||||
"app_icon_subtitle".t(context: context),
|
||||
style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceVariant),
|
||||
),
|
||||
),
|
||||
GridView.count(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
crossAxisCount: 3,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 12,
|
||||
children: [
|
||||
for (final variant in AppIconVariant.values)
|
||||
_AppIconTile(
|
||||
variant: variant,
|
||||
isSelected: variant == selectedIcon.value,
|
||||
onTap: () => onIconSelected(variant),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AppIconTile extends StatelessWidget {
|
||||
const _AppIconTile({required this.variant, required this.isSelected, required this.onTap});
|
||||
|
||||
final AppIconVariant variant;
|
||||
final bool isSelected;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||
border: Border.all(
|
||||
color: isSelected ? context.colorScheme.primary : context.colorScheme.outlineVariant,
|
||||
width: isSelected ? 3 : 1,
|
||||
),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(13)),
|
||||
child: SvgPicture.asset(variant.assetPath, width: 64, height: 64),
|
||||
),
|
||||
),
|
||||
if (isSelected)
|
||||
Positioned(
|
||||
right: 2,
|
||||
bottom: 2,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(color: context.colorScheme.primary, shape: BoxShape.circle),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Icon(Icons.check, size: 12, color: context.colorScheme.onPrimary),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
variant.translationKey.t(context: context),
|
||||
style: context.textTheme.labelMedium?.copyWith(
|
||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||
color: isSelected ? context.colorScheme.primary : null,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/widgets/settings/preference_settings/app_icon_setting.dart';
|
||||
import 'package:immich_mobile/widgets/settings/preference_settings/haptic_setting.dart';
|
||||
import 'package:immich_mobile/widgets/settings/preference_settings/share_setting.dart';
|
||||
import 'package:immich_mobile/widgets/settings/preference_settings/theme_setting.dart';
|
||||
@@ -9,7 +10,7 @@ class PreferenceSetting extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const preferenceSettings = [ThemeSetting(), HapticSetting(), ShareSetting()];
|
||||
const preferenceSettings = [ThemeSetting(), AppIconSetting(), HapticSetting(), ShareSetting()];
|
||||
|
||||
return const SettingsSubPageScaffold(settings: preferenceSettings, showDivider: true);
|
||||
}
|
||||
|
||||
20
mobile/pigeon/app_icon_api.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:pigeon/pigeon.dart';
|
||||
|
||||
@ConfigurePigeon(
|
||||
PigeonOptions(
|
||||
dartOut: 'lib/platform/app_icon_api.g.dart',
|
||||
swiftOut: 'ios/Runner/Core/AppIcon.g.swift',
|
||||
swiftOptions: SwiftOptions(includeErrorClass: false),
|
||||
kotlinOut: 'android/app/src/main/kotlin/app/alextran/immich/appicon/AppIcon.g.kt',
|
||||
kotlinOptions: KotlinOptions(package: 'app.alextran.immich.appicon'),
|
||||
dartOptions: DartOptions(),
|
||||
dartPackageName: 'immich_mobile',
|
||||
),
|
||||
)
|
||||
@HostApi()
|
||||
abstract class AppIconApi {
|
||||
@async
|
||||
void setAppIcon(String iconId);
|
||||
|
||||
String getAppIcon();
|
||||
}
|
||||
@@ -124,6 +124,7 @@ flutter:
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- assets/
|
||||
- assets/app-icons/
|
||||
- assets/feature_message/
|
||||
fonts:
|
||||
- family: GoogleSans
|
||||
|
||||
216
mobile/scripts/generate_app_icons.py
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generates the alternate app icon assets for Android and iOS.
|
||||
|
||||
Recolors the five petals of the Immich logo for each icon variant and emits:
|
||||
- assets/app-icons/app-icon-<id>.svg (in-app previews)
|
||||
- android res: drawable/ic_launcher_foreground_<id>.xml,
|
||||
mipmap-anydpi-v26/ic_launcher_<id>.xml,
|
||||
values/app_icon_colors.xml
|
||||
- ios: Assets.xcassets/AppIcon<Name>.appiconset/{Contents.json,1024.png}
|
||||
|
||||
The iOS PNGs are rendered with macOS `qlmanage`, so this script must run on
|
||||
macOS. Run it from anywhere: `python3 mobile/scripts/generate_app_icons.py`.
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
|
||||
MOBILE = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Original petal colors, in SVG document order:
|
||||
# top (red), upper-left (pink), upper-right (yellow),
|
||||
# lower-left (blue), lower-right (green)
|
||||
BASE_PETALS = ["#FA2921", "#ED79B5", "#FFB400", "#1E83F7", "#18C249"]
|
||||
BASE_BACKGROUND = "#FFFFFF"
|
||||
|
||||
# id -> (asset suffix, background, petal colors in BASE_PETALS order).
|
||||
# "classic" is the default icon and only gets a preview SVG; its native
|
||||
# assets are the stock ic_launcher / AppIcon.
|
||||
VARIANTS = {
|
||||
"classic": (BASE_BACKGROUND, BASE_PETALS),
|
||||
"midnight": ("#0F172A", BASE_PETALS),
|
||||
"ocean": ("#FFFFFF", ["#0284C7", "#38BDF8", "#1D4ED8", "#0891B2", "#14B8A6"]),
|
||||
"sunset": ("#1E1B4B", ["#F97316", "#7C3AED", "#FACC15", "#C026D3", "#F43F5E"]),
|
||||
"forest": ("#FFFFFF", ["#16A34A", "#84CC16", "#4ADE80", "#15803D", "#65A30D"]),
|
||||
"blossom": ("#FFF1F2", ["#E11D48", "#FB7185", "#F472B6", "#BE185D", "#F9A8D4"]),
|
||||
"ink": ("#FFFFFF", ["#111827", "#6B7280", "#374151", "#9CA3AF", "#4B5563"]),
|
||||
"neon": ("#0A0A12", ["#FF2D95", "#00E5FF", "#FFD600", "#7C4DFF", "#00FF9D"]),
|
||||
"gold": ("#1C1917", ["#FBBF24", "#FDE68A", "#F59E0B", "#D97706", "#FCD34D"]),
|
||||
}
|
||||
|
||||
|
||||
def recolor(text: str, petals: list[str]) -> str:
|
||||
for base, new in zip(BASE_PETALS, petals):
|
||||
pattern = re.compile(re.escape(base), re.IGNORECASE)
|
||||
text = pattern.sub(new, text)
|
||||
return text
|
||||
|
||||
|
||||
def petal_paths() -> str:
|
||||
svg = (MOBILE / "assets" / "immich-logo.svg").read_text()
|
||||
return "\n".join(re.findall(r"<path[^>]*/>", svg))
|
||||
|
||||
|
||||
def icon_svg(background: str, petals: list[str], size: int) -> str:
|
||||
# The flower spans the full 192px viewBox; scale it down so the icon
|
||||
# keeps the same padding as the original iOS app icon.
|
||||
scale = 0.78
|
||||
offset = 192 * (1 - scale) / 2
|
||||
paths = recolor(petal_paths(), petals)
|
||||
return f"""<svg width="{size}" height="{size}" viewBox="0 0 192 192" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="192" height="192" fill="{background}"/>
|
||||
<g transform="translate({offset:g} {offset:g}) scale({scale})">
|
||||
{paths}
|
||||
</g>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
|
||||
def write_previews() -> None:
|
||||
out = MOBILE / "assets" / "app-icons"
|
||||
out.mkdir(exist_ok=True)
|
||||
for variant, (background, petals) in VARIANTS.items():
|
||||
(out / f"app-icon-{variant}.svg").write_text(icon_svg(background, petals, 192))
|
||||
print(f"previews: {out}")
|
||||
|
||||
|
||||
def write_android() -> None:
|
||||
res = MOBILE / "android" / "app" / "src" / "main" / "res"
|
||||
foreground = (res / "drawable" / "ic_launcher_foreground.xml").read_text()
|
||||
|
||||
colors = ['<?xml version="1.0" encoding="utf-8"?>', "<resources>"]
|
||||
for variant, (background, petals) in VARIANTS.items():
|
||||
if variant == "classic":
|
||||
continue
|
||||
(res / "drawable" / f"ic_launcher_foreground_{variant}.xml").write_text(
|
||||
recolor(foreground, petals)
|
||||
)
|
||||
colors.append(f' <color name="ic_launcher_background_{variant}">{background}</color>')
|
||||
(res / "mipmap-anydpi-v26" / f"ic_launcher_{variant}.xml").write_text(
|
||||
f"""<?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_{variant}"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_{variant}"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
"""
|
||||
)
|
||||
colors.append("</resources>")
|
||||
(res / "values" / "app_icon_colors.xml").write_text("\n".join(colors) + "\n")
|
||||
print(f"android res: {res}")
|
||||
|
||||
|
||||
def strip_alpha(png: bytes) -> bytes:
|
||||
"""Re-encodes an RGBA PNG as opaque RGB (required for app icons)."""
|
||||
assert png[:8] == b"\x89PNG\r\n\x1a\n"
|
||||
pos, chunks, idat = 8, [], b""
|
||||
width = height = bit_depth = color_type = None
|
||||
while pos < len(png):
|
||||
(length,) = struct.unpack(">I", png[pos : pos + 4])
|
||||
kind = png[pos + 4 : pos + 8]
|
||||
data = png[pos + 8 : pos + 8 + length]
|
||||
pos += 12 + length
|
||||
if kind == b"IHDR":
|
||||
width, height, bit_depth, color_type = struct.unpack(">IIBB", data[:10])
|
||||
if kind == b"IDAT":
|
||||
idat += data
|
||||
else:
|
||||
chunks.append((kind, data))
|
||||
if color_type != 6 or bit_depth != 8:
|
||||
return png # already opaque
|
||||
|
||||
raw = zlib.decompress(idat)
|
||||
stride = width * 4
|
||||
rgb = bytearray()
|
||||
previous = bytearray(width * 4)
|
||||
for y in range(height):
|
||||
row_start = y * (stride + 1)
|
||||
filter_type = raw[row_start]
|
||||
row = bytearray(raw[row_start + 1 : row_start + 1 + stride])
|
||||
for x in range(stride):
|
||||
a = row[x - 4] if x >= 4 else 0
|
||||
b = previous[x]
|
||||
c = previous[x - 4] if x >= 4 else 0
|
||||
if filter_type == 1:
|
||||
row[x] = (row[x] + a) & 0xFF
|
||||
elif filter_type == 2:
|
||||
row[x] = (row[x] + b) & 0xFF
|
||||
elif filter_type == 3:
|
||||
row[x] = (row[x] + (a + b) // 2) & 0xFF
|
||||
elif filter_type == 4:
|
||||
p = a + b - c
|
||||
pa, pb, pc = abs(p - a), abs(p - b), abs(p - c)
|
||||
pred = a if pa <= pb and pa <= pc else b if pb <= pc else c
|
||||
row[x] = (row[x] + pred) & 0xFF
|
||||
previous = row
|
||||
rgb.append(0)
|
||||
for x in range(width):
|
||||
rgb += row[x * 4 : x * 4 + 3]
|
||||
|
||||
out = bytearray(b"\x89PNG\r\n\x1a\n")
|
||||
|
||||
def chunk(kind: bytes, data: bytes) -> None:
|
||||
out.extend(struct.pack(">I", len(data)))
|
||||
out.extend(kind)
|
||||
out.extend(data)
|
||||
out.extend(struct.pack(">I", zlib.crc32(kind + data)))
|
||||
|
||||
chunk(b"IHDR", struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0))
|
||||
chunk(b"IDAT", zlib.compress(bytes(rgb), 9))
|
||||
chunk(b"IEND", b"")
|
||||
return bytes(out)
|
||||
|
||||
|
||||
def write_ios() -> None:
|
||||
xcassets = MOBILE / "ios" / "Runner" / "Assets.xcassets"
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
tmp = Path(tmp_dir)
|
||||
for variant, (background, petals) in VARIANTS.items():
|
||||
if variant == "classic":
|
||||
continue
|
||||
svg = tmp / f"{variant}.svg"
|
||||
svg.write_text(icon_svg(background, petals, 1024))
|
||||
subprocess.run(
|
||||
["qlmanage", "-t", "-s", "1024", "-o", tmp_dir, str(svg)],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
rendered = tmp / f"{variant}.svg.png"
|
||||
if not rendered.exists():
|
||||
sys.exit(f"qlmanage failed to render {svg}")
|
||||
|
||||
iconset = xcassets / f"AppIcon{variant.capitalize()}.appiconset"
|
||||
shutil.rmtree(iconset, ignore_errors=True)
|
||||
iconset.mkdir()
|
||||
(iconset / "1024.png").write_bytes(strip_alpha(rendered.read_bytes()))
|
||||
(iconset / "Contents.json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "1024.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024",
|
||||
}
|
||||
],
|
||||
"info": {"author": "xcode", "version": 1},
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
print(f"ios appiconsets: {xcassets}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
write_previews()
|
||||
write_android()
|
||||
write_ios()
|
||||