diff --git a/app/build.gradle b/app/build.gradle index 7aac143c2e..6209fc560a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -51,6 +51,7 @@ android { resources { excludes += '/META-INF/{AL2.0,LGPL2.1}' + excludes += 'google/protobuf/descriptor.proto' } } @@ -86,9 +87,11 @@ android { String sharedTestDir = 'src/sharedTest/java' test { java.srcDirs += sharedTestDir + kotlin.srcDirs += sharedTestDir } androidTest { java.srcDirs += sharedTestDir + kotlin.srcDirs += sharedTestDir assets.srcDirs += files("$projectDir/schemas".toString()) } } @@ -240,7 +243,6 @@ dependencies { } coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:${desugarJdkLibsVersion}" implementation platform("com.google.firebase:firebase-bom:${firebaseBomVersion}") - implementation("com.google.firebase:firebase-perf") implementation fileTree(include: ['*.aar'], dir: 'libs') implementation "androidx.fragment:fragment-ktx:${fragmentVersion}" implementation("androidx.activity:activity-ktx:$activity_version") @@ -349,12 +351,10 @@ dependencies { implementation "com.google.firebase:firebase-messaging" implementation 'com.google.firebase:firebase-analytics' implementation 'com.google.firebase:firebase-crashlytics' - - implementation("com.google.protobuf:protobuf-javalite") { - version { - strictly '3.11.0' - } + implementation('com.google.firebase:firebase-perf') { + exclude group: 'com.google.firebase', module: 'protolite-well-known-types' } + implementation "com.google.protobuf:protobuf-javalite:4.29.3" implementation "com.android.billingclient:billing-ktx:${billingKtxVersion}" implementation "com.google.mlkit:barcode-scanning:${mlkitBarcodeVersion}" @@ -465,6 +465,7 @@ dependencies { androidTestImplementation("androidx.test.espresso:espresso-contrib:${espressoVersion}", { exclude group: 'com.android.support', module: 'support-annotations' exclude group: 'org.checkerframework', module: 'checker' + exclude group: 'com.google.protobuf', module: 'protobuf-lite' }) androidTestImplementation "androidx.test.espresso:espresso-idling-resource:$espressoVersion" androidTestImplementation "androidx.test.ext:junit:${androidxJunitVersion}" @@ -479,11 +480,8 @@ dependencies { // ML Kit implementation 'com.google.mlkit:entity-extraction:16.0.0-beta6' - testImplementation("com.google.protobuf:protobuf-javalite") { - version { - strictly '3.11.0' - } - } + testImplementation "com.google.protobuf:protobuf-javalite:4.29.3" + androidTestImplementation "com.google.protobuf:protobuf-javalite:4.29.3" // SumSub implementation("com.sumsub.sns:idensic-mobile-sdk:$sumsubVersion") { @@ -552,4 +550,3 @@ task syncStrings { } } } - diff --git a/app/src/androidTest/java/one/mixin/android/crypto/TipTest.kt b/app/src/androidTest/java/one/mixin/android/crypto/TipTest.kt index 5c802a2452..ba5d0604bf 100644 --- a/app/src/androidTest/java/one/mixin/android/crypto/TipTest.kt +++ b/app/src/androidTest/java/one/mixin/android/crypto/TipTest.kt @@ -18,16 +18,19 @@ import kotlin.time.Duration.Companion.days class TipTest { @Test fun testTipGuard() { - val suite = Tip.newSuiteBn256() - val signer = suite.scalar() - signer.setBytes("0da58ccc3b323d92af281367333f4c120418ed2700de803046947f59707b3479".hexStringToByteArray()) - val user = suite.scalar() - assert(signer.publicKey().publicKeyString() == "5HSsddpV8HiKbu9vL3ZB69dtDjaZdQAn8RuL2aK1d1yZknUhBAXNhJLkZfCc2RwTxcaxKonNsXnQJFGcM8jgBztGTHzCA26LgKZWCe74Bw8VJ51FyqCGTysSLnNvkKPT3gh1RhjbyKPEoq3d3DXhJEQJt7GhVgZC82VeMfME9LnYECn9Pui1ta") + val signer = + Tip.newPrivateKeyFromBytes( + "0da58ccc3b323d92af281367333f4c120418ed2700de803046947f59707b3479".hexStringToByteArray(), + ) + val signerPublic = Tip.publicKeyFromBytes(signer) + val signerIdentity = Tip.pointPublicKeyString(signerPublic) + + val user = Tip.newPrivateKeyFromBytes("p8ogX1BMb-IsRisEBS2kOchXEqjbqxtsXR8J9Bf0AGI".base64RawURLDecode()) + val userPublic = Tip.publicKeyFromBytes(user) + val userIdentity = Tip.pointPublicKeyString(userPublic) - user.setBytes("p8ogX1BMb-IsRisEBS2kOchXEqjbqxtsXR8J9Bf0AGI".base64RawURLDecode()) - val ephemeral = suite.scalar() - ephemeral.setBytes("-e7M3ZD5k-rW6KQ7GVfV9V9bpmfbUY5y8HiqqBGv8-r46YMRRSlyc-ZKGU3s92gsC9GVuIhgn33I".base64RawURLDecode()) - val eBytes = ephemeral.privateKeyBytes() + val ephemeral = Tip.newPrivateKeyFromBytes("-e7M3ZD5k-rW6KQ7GVfV9V9bpmfbUY5y8HiqqBGv8-r46YMRRSlyc-ZKGU3s92gsC9GVuIhgn33I".base64RawURLDecode()) + val eBytes = ephemeral val nonce = 1024L val nonceBytes = nonce.toBeByteArray() @@ -36,21 +39,19 @@ class TipTest { val graceBytes = grace.toBeByteArray() println("grace: ${graceBytes.toHex()}") - val sPk = signer.publicKey() - println("sPK: ${sPk.publicKeyString()}") - val uPk = user.publicKey() - val pKeyBytes = uPk.publicKeyBytes() - println("uSk: ${user.privateKeyBytes().toHex()}") - println("uPk: ${uPk.publicKeyString()}") + println("sPK: $signerIdentity") + val pKeyBytes = userPublic + println("uSk: ${user.toHex()}") + println("uPk: $userIdentity") val msg = pKeyBytes + eBytes + nonceBytes + graceBytes println("msg: ${msg.toHex()}") - val sig = user.sign(msg) + val sig = Tip.signFromBytes(user, msg) println("sig: ${sig.toHex()}") val data = TipSignData( - identity = uPk.publicKeyString(), + identity = userIdentity, ephemeral = eBytes.toHex(), nonce = nonce, grace = grace, @@ -60,10 +61,10 @@ class TipTest { val json = Gson().toJson(data).toByteArray() println("json: ${json.toHex()}") - val cipher = Tip.encrypt(sPk, user, json) + val cipher = Tip.encrypt(signerIdentity, user.toHex(), json) println("cipher: ${cipher.toHex()}") - val plain = Tip.decrypt(uPk, signer, cipher) + val plain = Tip.decrypt(userIdentity, signer.toHex(), cipher) assert(json.contentEquals(plain)) } diff --git a/app/src/androidTest/java/one/mixin/android/db/BaseMigrationTest.kt b/app/src/androidTest/java/one/mixin/android/db/BaseMigrationTest.kt index 368c19a166..c43009a6e6 100644 --- a/app/src/androidTest/java/one/mixin/android/db/BaseMigrationTest.kt +++ b/app/src/androidTest/java/one/mixin/android/db/BaseMigrationTest.kt @@ -1,9 +1,65 @@ package one.mixin.android.db +import androidx.room.migration.Migration import androidx.room.testing.MigrationTestHelper import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory import androidx.test.platform.app.InstrumentationRegistry import one.mixin.android.Constants +import one.mixin.android.Constants.DataBase.CURRENT_VERSION +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_15_16 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_16_17 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_17_18 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_18_19 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_19_20 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_20_21 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_21_22 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_22_23 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_23_24 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_24_25 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_25_26 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_26_27 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_27_28 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_28_29 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_29_30 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_30_31 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_31_32 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_32_33 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_33_34 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_34_35 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_35_36 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_36_37 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_37_38 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_38_39 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_39_40 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_40_41 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_41_42 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_42_43 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_43_44 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_44_45 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_45_46 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_46_47 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_47_48 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_48_49 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_49_50 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_50_51 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_51_52 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_52_53 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_53_54 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_54_55 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_55_56 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_56_57 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_57_58 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_58_59 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_59_60 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_60_61 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_61_62 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_62_63 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_63_64 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_64_65 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_65_66 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_66_67 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_67_68 +import one.mixin.android.db.MixinDatabaseMigrations.Companion.MIGRATION_68_69 import org.junit.After import org.junit.Before import org.junit.Rule @@ -14,10 +70,68 @@ open class BaseMigrationTest { val migrationTestHelper = MigrationTestHelper( InstrumentationRegistry.getInstrumentation(), - MixinDatabase::class.java.canonicalName, + requireNotNull(MixinDatabase::class.java.canonicalName), FrameworkSQLiteOpenHelperFactory(), ) + protected val allMixinMigrations = + arrayOf( + MIGRATION_15_16, + MIGRATION_16_17, + MIGRATION_17_18, + MIGRATION_18_19, + MIGRATION_19_20, + MIGRATION_20_21, + MIGRATION_21_22, + MIGRATION_22_23, + MIGRATION_23_24, + MIGRATION_24_25, + MIGRATION_25_26, + MIGRATION_26_27, + MIGRATION_27_28, + MIGRATION_28_29, + MIGRATION_29_30, + MIGRATION_30_31, + MIGRATION_31_32, + MIGRATION_32_33, + MIGRATION_33_34, + MIGRATION_34_35, + MIGRATION_35_36, + MIGRATION_36_37, + MIGRATION_37_38, + MIGRATION_38_39, + MIGRATION_39_40, + MIGRATION_40_41, + MIGRATION_41_42, + MIGRATION_42_43, + MIGRATION_43_44, + MIGRATION_44_45, + MIGRATION_45_46, + MIGRATION_46_47, + MIGRATION_47_48, + MIGRATION_48_49, + MIGRATION_49_50, + MIGRATION_50_51, + MIGRATION_51_52, + MIGRATION_52_53, + MIGRATION_53_54, + MIGRATION_54_55, + MIGRATION_55_56, + MIGRATION_56_57, + MIGRATION_57_58, + MIGRATION_58_59, + MIGRATION_59_60, + MIGRATION_60_61, + MIGRATION_61_62, + MIGRATION_62_63, + MIGRATION_63_64, + MIGRATION_64_65, + MIGRATION_65_66, + MIGRATION_66_67, + MIGRATION_67_68, + MIGRATION_68_69, + ) + @Before fun setUp() { } @@ -31,6 +145,29 @@ open class BaseMigrationTest { db.close() } + protected fun createDatabase(version: Int, dbName: String = Constants.DataBase.DB_NAME) { + val db = migrationTestHelper.createDatabase(dbName, version) + db.close() + } + + protected fun runMixinMigrationsAndValidate( + fromVersion: Int, + toVersion: Int = CURRENT_VERSION, + ) { + val migrations = + allMixinMigrations + .filter { it.startVersion >= fromVersion && it.endVersion <= toVersion } + .sortedBy { it.startVersion } + .toTypedArray() + + migrationTestHelper.runMigrationsAndValidate( + Constants.DataBase.DB_NAME, + toVersion, + true, + *migrations, + ) + } + fun create16() { val db = migrationTestHelper.createDatabase(Constants.DataBase.DB_NAME, 16) db.close() diff --git a/app/src/androidTest/java/one/mixin/android/db/MigrationCurrentVersionTest.kt b/app/src/androidTest/java/one/mixin/android/db/MigrationCurrentVersionTest.kt new file mode 100644 index 0000000000..8f27f8ea8f --- /dev/null +++ b/app/src/androidTest/java/one/mixin/android/db/MigrationCurrentVersionTest.kt @@ -0,0 +1,19 @@ +package one.mixin.android.db + +import one.mixin.android.Constants.DataBase.CURRENT_VERSION +import one.mixin.android.Constants.DataBase.MINI_VERSION +import org.junit.Test + +class MigrationCurrentVersionTest : BaseMigrationTest() { + @Test + fun migrate_all_historical_versions_to_current() { + for (fromVersion in MINI_VERSION until CURRENT_VERSION) { + try { + createDatabase(fromVersion) + runMixinMigrationsAndValidate(fromVersion) + } catch (t: Throwable) { + throw AssertionError("Failed to migrate MixinDatabase from $fromVersion to $CURRENT_VERSION", t) + } + } + } +} diff --git a/app/src/androidTest/java/one/mixin/android/db/PerpsMigrationTest.kt b/app/src/androidTest/java/one/mixin/android/db/PerpsMigrationTest.kt new file mode 100644 index 0000000000..9f45d71772 --- /dev/null +++ b/app/src/androidTest/java/one/mixin/android/db/PerpsMigrationTest.kt @@ -0,0 +1,32 @@ +package one.mixin.android.db + +import androidx.room.testing.MigrationTestHelper +import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory +import androidx.test.platform.app.InstrumentationRegistry +import one.mixin.android.Constants.DataBase.PERPS_DB_NAME +import org.junit.Rule +import org.junit.Test + +class PerpsMigrationTest { + @Suppress("DEPRECATION") + @get:Rule + val migrationTestHelper = + MigrationTestHelper( + InstrumentationRegistry.getInstrumentation(), + requireNotNull(PerpsDatabase::class.java.canonicalName), + FrameworkSQLiteOpenHelperFactory(), + ) + + @Test + fun migrate_1_2() { + val db = migrationTestHelper.createDatabase(PERPS_DB_NAME, 1) + db.close() + + migrationTestHelper.runMigrationsAndValidate( + PERPS_DB_NAME, + 2, + true, + PerpsDatabase.MIGRATION_1_2, + ) + } +} diff --git a/app/src/main/java/one/mixin/android/compose/AppBar.kt b/app/src/main/java/one/mixin/android/compose/AppBar.kt index 4bca21152b..c8a328c021 100644 --- a/app/src/main/java/one/mixin/android/compose/AppBar.kt +++ b/app/src/main/java/one/mixin/android/compose/AppBar.kt @@ -127,21 +127,23 @@ fun MixinTopAppBar( @Preview @Composable fun PreviewMixinAppBar() { - MixinTopAppBar( - navigationIcon = { - MixinBackButton() - }, - title = { - Text(text = "Title") - }, - actions = { - IconButton(onClick = { }) { - Icon( - painter = painterResource(id = R.drawable.ic_more), - contentDescription = null, - tint = MixinAppTheme.colors.icon, - ) - } - }, - ) + MixinAppTheme { + MixinTopAppBar( + navigationIcon = { + MixinBackButton() + }, + title = { + Text(text = "Title") + }, + actions = { + IconButton(onClick = { }) { + Icon( + painter = painterResource(id = R.drawable.ic_more), + contentDescription = null, + tint = MixinAppTheme.colors.icon, + ) + } + }, + ) + } } diff --git a/app/src/main/java/one/mixin/android/compose/Dialogs.kt b/app/src/main/java/one/mixin/android/compose/Dialogs.kt index 4730dcac8d..ac4fd05dfb 100644 --- a/app/src/main/java/one/mixin/android/compose/Dialogs.kt +++ b/app/src/main/java/one/mixin/android/compose/Dialogs.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.SideEffect import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -25,6 +26,9 @@ fun IndeterminateProgressDialog( title: String = "", cancelable: Boolean? = null, ) { + if (LocalInspectionMode.current) { + return + } val context = LocalContext.current val activity = context.findFragmentActivityOrNull() diff --git a/app/src/main/java/one/mixin/android/compose/InputAmountUsageExample.kt b/app/src/main/java/one/mixin/android/compose/InputAmountUsageExample.kt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/app/src/main/java/one/mixin/android/compose/theme/Theme.kt b/app/src/main/java/one/mixin/android/compose/theme/Theme.kt index 5438cc4dbf..eb05de999d 100644 --- a/app/src/main/java/one/mixin/android/compose/theme/Theme.kt +++ b/app/src/main/java/one/mixin/android/compose/theme/Theme.kt @@ -10,19 +10,17 @@ import androidx.compose.material.RippleConfiguration import androidx.compose.material.RippleDefaults import androidx.compose.material.darkColors import androidx.compose.material.lightColors +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.colorspace.ColorSpaces import androidx.compose.ui.platform.LocalContext -import one.mixin.android.MixinApplication -import one.mixin.android.extension.isNightMode import one.mixin.android.extension.isScreenWideColorGamut import one.mixin.android.util.isCurrChinese -val isP3Supported = MixinApplication.appContext.isScreenWideColorGamut() - class AppColors( val primary: Color, val accent: Color, @@ -45,20 +43,8 @@ class AppColors( val walletRed: Color = Color(0xFFF67070), val walletGreen: Color = Color(0xFF50BD5C), val walletOrange: Color = Color(0xFFFFAA00), - val marketRed: Color = if (isP3Supported) Color( - colorSpace = ColorSpaces.DisplayP3, - red = 0.898f, - green = 0.471f, - blue = 0.455f, - alpha = 1f - ) else Color(0xFFE57874), - val marketGreen: Color = if (isP3Supported) Color( - colorSpace = ColorSpaces.DisplayP3, - red = 0.314f, - green = 0.741f, - blue = 0.361f, - alpha = 1f - ) else Color(0xFF50BD5C), + val marketRed: Color, + val marketGreen: Color, val shadow: Color = Color(0x33AAAAAA), val unchecked: Color, val tipWarning: Color, @@ -90,8 +76,73 @@ object MixinAppTheme { } -private val LightColorPalette = - AppColors( +private fun createMarketRedColor(isP3Supported: Boolean): Color { + if (isP3Supported) { + return Color( + colorSpace = ColorSpaces.DisplayP3, + red = 0.898f, + green = 0.471f, + blue = 0.455f, + alpha = 1f, + ) + } + return Color(0xFFE57874) +} + +private fun createMarketGreenColor(isP3Supported: Boolean): Color { + if (isP3Supported) { + return Color( + colorSpace = ColorSpaces.DisplayP3, + red = 0.314f, + green = 0.741f, + blue = 0.361f, + alpha = 1f, + ) + } + return Color(0xFF50BD5C) +} + +private fun createAppColors( + isDarkTheme: Boolean, + isP3Supported: Boolean, +): AppColors { + val marketRed: Color = createMarketRedColor(isP3Supported) + val marketGreen: Color = createMarketGreenColor(isP3Supported) + if (isDarkTheme) { + return AppColors( + primary = Color(0xFF2c3136), + accent = Color(0xFF3D75E3), + textPrimary = Color(0xFFFFFFFF), + textAssist = Color(0xFF7F878F), + textMinor = Color(0xFFD3D4D5), + textRemarks = Color(0xFF6E7073), + icon = Color(0xFFEAEAEB), + iconGray = Color(0xFF808691), + iconAction = Color(0xFFFFFFFF), + backgroundWindow = Color(0xFF23272B), + background = Color(0xFF2c3136), + backgroundDark = Color(0xFF121212), + backgroundGrayLight = Color(0xFF3B3F44), + backgroundGray = Color(0xFF3B3F44), + unchecked = Color(0xFFECECEC), + tipWarning = Color(0xFF3E373B), + tipWarningBorder = Color(0xFFE86B67), + borderPrimary = Color(0x33FFFFFF), + bgGradientStart = Color(0xFF2C3136), + bgGradientEnd = Color(0xFF1C2029), + borderColor = Color(0xFF6E7073), + walletBlue = Color(0xFF64B5F6), + walletYellow = Color(0xFFFFEE58), + walletPurple = Color(0xFFBA68C8), + badgeRed = Color(0xFFF67070), + warning = Color(0xFFF6A417), + bgClip = Color(0xFF3B3F44), + borderGray = Color(0xFFD6D6D6), + marketRed = marketRed, + marketGreen = marketGreen, + ) + } + return AppColors( primary = Color(0xFFFFFFFF), accent = Color(0xFF3D75E3), textPrimary = Color(0xFF000000), @@ -120,53 +171,22 @@ private val LightColorPalette = warning = Color(0xFFF6A417), bgClip = Color(0xFFF5F7FA), borderGray = Color(0xFFD6D6D6), + marketRed = marketRed, + marketGreen = marketGreen, ) +} -private val DarkColorPalette = - AppColors( - primary = Color(0xFF2c3136), - accent = Color(0xFF3D75E3), - textPrimary = Color(0xFFFFFFFF), - textAssist = Color(0xFF7F878F), - textMinor = Color(0xFFD3D4D5), - textRemarks = Color(0xFF6E7073), - icon = Color(0xFFEAEAEB), - iconGray = Color(0xFF808691), - iconAction = Color(0xFFFFFFFF), - backgroundWindow = Color(0xFF23272B), - background = Color(0xFF2c3136), - backgroundDark = Color(0xFF121212), - backgroundGrayLight = Color(0xFF3B3F44), - backgroundGray = Color(0xFF3B3F44), - unchecked = Color(0xFFECECEC), - tipWarning = Color(0xFF3E373B), - tipWarningBorder = Color(0xFFE86B67), - borderPrimary = Color(0x33FFFFFF), - bgGradientStart = Color(0xFF2C3136), - bgGradientEnd = Color(0xFF1C2029), - borderColor = Color(0xFF6E7073), - walletBlue = Color(0xFF64B5F6), - walletYellow = Color(0xFFFFEE58), - walletPurple = Color(0xFFBA68C8), - badgeRed = Color(0xFFF67070), - warning = Color(0xFFF6A417), - bgClip = Color(0xFF3B3F44), - borderGray = Color(0xFFD6D6D6), - ) - -private val LocalColors = compositionLocalOf { LightColorPalette } +private val LocalColors = compositionLocalOf { createAppColors(isDarkTheme = false, isP3Supported = false) } @Composable fun MixinAppTheme( - darkTheme: Boolean = MixinApplication.get().isNightMode(), + darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit, ) { - val colors = - if (darkTheme) { - DarkColorPalette - } else { - LightColorPalette - } + val context = LocalContext.current + val isInPreview: Boolean = LocalInspectionMode.current + val isP3Supported: Boolean = if (isInPreview) false else context.isScreenWideColorGamut() + val colors: AppColors = createAppColors(isDarkTheme = darkTheme, isP3Supported = isP3Supported) val textSelectionColors = TextSelectionColors( handleColor = Color(0xFF3D75E3), @@ -196,8 +216,6 @@ fun MixinAppTheme( @Composable @DrawableRes fun languageBasedImage(@DrawableRes defaultImage:Int, @DrawableRes zh:Int) : Int{ - val context = LocalContext.current - val drawableRes = when { isCurrChinese() -> zh else -> defaultImage diff --git a/app/src/main/java/one/mixin/android/db/PerpsDatabase.kt b/app/src/main/java/one/mixin/android/db/PerpsDatabase.kt index 00b784aeb1..0c439fd0f0 100644 --- a/app/src/main/java/one/mixin/android/db/PerpsDatabase.kt +++ b/app/src/main/java/one/mixin/android/db/PerpsDatabase.kt @@ -35,7 +35,7 @@ abstract class PerpsDatabase : RoomDatabase() { private var INSTANCE: PerpsDatabase? = null private val lock = Any() private var currentIdentityNumber: String? = null - private val MIGRATION_1_2 = + internal val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(db: SupportSQLiteDatabase) { db.execSQL("ALTER TABLE markets ADD COLUMN category TEXT NOT NULL DEFAULT ''") diff --git a/app/src/main/java/one/mixin/android/extension/ViewExtension.kt b/app/src/main/java/one/mixin/android/extension/ViewExtension.kt index d39534decf..a06cea5b42 100644 --- a/app/src/main/java/one/mixin/android/extension/ViewExtension.kt +++ b/app/src/main/java/one/mixin/android/extension/ViewExtension.kt @@ -1,6 +1,15 @@ +@file:Suppress( + "unused", + "FunctionName", + "FoldableIfThen", + "IfThenToElvis", + "UNUSED_PARAMETER", +) + package one.mixin.android.extension import android.animation.Animator +import android.animation.AnimatorListenerAdapter import android.animation.ObjectAnimator import android.animation.ValueAnimator import android.app.Activity @@ -35,9 +44,11 @@ import androidx.appcompat.widget.PopupMenu import androidx.core.animation.doOnEnd import androidx.core.animation.doOnStart import androidx.core.graphics.ColorUtils -import androidx.core.view.ViewCompat -import androidx.core.view.ViewPropertyAnimatorListener import androidx.core.view.drawToBitmap +import androidx.core.view.forEach +import androidx.core.view.forEachIndexed +import androidx.core.view.isVisible +import androidx.core.view.marginBottom import androidx.core.view.updateLayoutParams import androidx.navigation.NavController import androidx.navigation.NavOptions @@ -57,13 +68,15 @@ const val ANIMATION_DURATION_SHORT = 260L const val ANIMATION_DURATION_SHORTEST = 120L fun View.hideKeyboard() { - val inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + val inputMethodManager: InputMethodManager = + context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager inputMethodManager.hideSoftInputFromWindow(windowToken, 0) } fun View.showKeyboard() { if (requestFocus()) { - val inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + val inputMethodManager: InputMethodManager = + context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager inputMethodManager.showSoftInput(this, SHOW_IMPLICIT) } } @@ -78,17 +91,7 @@ fun View.fadeIn( ) { this.visibility = VISIBLE this.alpha = 0f - ViewCompat.animate(this).alpha(maxAlpha).setDuration(duration).setListener( - object : ViewPropertyAnimatorListener { - override fun onAnimationStart(view: View) { - } - - override fun onAnimationEnd(view: View) { - } - - override fun onAnimationCancel(view: View) {} - }, - ).start() + animate().alpha(maxAlpha).setDuration(duration).start() } fun View.fadeOut(isGone: Boolean = false) { @@ -101,21 +104,15 @@ fun View.fadeOut( isGone: Boolean = false, ) { this.alpha = 1f - ViewCompat.animate(this).alpha(0f).setStartDelay(delay).setDuration(duration).setListener( - object : ViewPropertyAnimatorListener { - override fun onAnimationStart(view: View) { - view.isDrawingCacheEnabled = true - } - - override fun onAnimationEnd(view: View) { - view.visibility = if (isGone) GONE else INVISIBLE - view.alpha = 0f - view.isDrawingCacheEnabled = false - } - - override fun onAnimationCancel(view: View) {} - }, - ) + animate().alpha(0f).setStartDelay(delay).setDuration(duration) + .setListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + visibility = if (isGone) GONE else INVISIBLE + alpha = 0f + } + }, + ) } fun View.translationX(value: Float) { @@ -126,7 +123,7 @@ fun View.translationX( value: Float, duration: Long, ) { - ViewCompat.animate(this).setDuration(duration).translationX(value).start() + animate().setDuration(duration).translationX(value).start() } fun View.translationY( @@ -141,18 +138,15 @@ fun View.translationY( duration: Long, endAction: (() -> Unit)? = null, ) { - ViewCompat.animate(this).setDuration(duration).translationY(value) + animate().setDuration(duration).translationY(value) .setListener( - object : ViewPropertyAnimatorListener { - override fun onAnimationEnd(view: View) { - endAction?.let { it() } + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + endAction?.invoke() } - - override fun onAnimationCancel(view: View) { - endAction?.let { it() } + override fun onAnimationCancel(animation: Animator) { + endAction?.invoke() } - - override fun onAnimationStart(view: View) {} }, ) .start() @@ -318,27 +312,32 @@ fun View.circularReveal() { circularReveal.start() } +@Suppress("unused") fun EditText.showCursor() { this.requestFocus() this.isCursorVisible = true } +@Suppress("unused") fun EditText.hideCursor() { this.clearFocus() this.isCursorVisible = false } +@Suppress("unused") fun ViewGroup.inflate( @LayoutRes layoutRes: Int, attachToRoot: Boolean = false, -) = LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)!! +): View { + return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot) +} fun View.navigateUp() { try { findNavController().navigateUp() - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { // Workaround with https://issuetracker.google.com/issues/128881182 - } catch (e: IllegalStateException) { + } catch (_: IllegalStateException) { Timber.w("View $this does not have a NavController set") } } @@ -346,10 +345,10 @@ fun View.navigateUp() { fun NavController.safeNavigateUp(): Boolean { return try { navigateUp() - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { // Workaround with https://issuetracker.google.com/issues/128881182 false - } catch (e: IllegalStateException) { + } catch (_: IllegalStateException) { false } } @@ -361,9 +360,9 @@ fun View.navigate( ) { try { findNavController().navigate(resId, bundle, navOptions) - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { // Workaround with https://issuetracker.google.com/issues/128881182 - } catch (e: IllegalStateException) { + } catch (_: IllegalStateException) { Timber.w("View $this does not have a NavController set") } } @@ -400,10 +399,21 @@ fun View.bounce() { spring.endValue = 1.0 } +@Suppress("UnusedReceiverParameter", "FunctionName", "unused") +@Deprecated("Use intProperty", ReplaceWith("intProperty(name, getAction, setAction)")) fun View.IntProperty( name: String, getAction: (View) -> Int, setAction: (View, Int) -> Unit, +): Property { + return intProperty(name = name, getAction = getAction, setAction = setAction) +} + +@Suppress("UnusedReceiverParameter", "unused") +fun View.intProperty( + name: String, + getAction: (View) -> Int, + setAction: (View, Int) -> Unit, ): Property { return object : Property(Int::class.java, name) { override fun get(obj: View): Int { @@ -441,16 +451,17 @@ fun Int.withAlpha(alpha: Float): Int { } fun PopupMenu.showIcon() { - val menuHelper: Any - val argTypes: Array?> + val menuHelper: Any? try { val fMenuHelper: Field = PopupMenu::class.java.getDeclaredField("mPopup") fMenuHelper.isAccessible = true menuHelper = fMenuHelper.get(this) - argTypes = arrayOf(Boolean::class.javaPrimitiveType) - menuHelper.javaClass.getDeclaredMethod("setForceShowIcon", *argTypes) - .invoke(menuHelper, true) - } catch (e: Exception) { + val argTypes: Array?> = arrayOf(Boolean::class.javaPrimitiveType) + if (menuHelper != null) { + menuHelper.javaClass.getDeclaredMethod("setForceShowIcon", *argTypes) + .invoke(menuHelper, true) + } + } catch (_: Exception) { } } @@ -501,7 +512,9 @@ var View.backgroundColor: Int var View.backgroundDrawable: Drawable? inline get() = background - set(value) = setBackgroundDrawable(value) + set(value) { + background = value + } var View.backgroundResource: Int @Deprecated("Property does not have a getter") diff --git a/app/src/main/java/one/mixin/android/job/AttachmentDownloadJob.kt b/app/src/main/java/one/mixin/android/job/AttachmentDownloadJob.kt index f81faeaa10..82f775faea 100644 --- a/app/src/main/java/one/mixin/android/job/AttachmentDownloadJob.kt +++ b/app/src/main/java/one/mixin/android/job/AttachmentDownloadJob.kt @@ -101,13 +101,14 @@ class AttachmentDownloadJob( shareable = this.shareable }.attachmentId } catch (e: Exception) { - message.content!! + requireNotNull(message.content) }, ) val body = attachmentCall!!.execute().body() - if (body != null && (body.isSuccess || !isCancelled) && body.data != null) { - val attachmentResponse = body.data!! - attachmentResponse.view_url?.let { + if (body != null && body.isSuccess && !isCancelled && body.data != null) { + val attachmentResponse = requireNotNull(body.data) + val viewUrl: String? = attachmentResponse.view_url + viewUrl?.let { val result = decryptAttachment(it) if (result) { val attachmentExtra = GsonHelper.customGson.toJson(AttachmentExtra(attachmentResponse.attachment_id, message.messageId, attachmentResponse.created_at, shareable)) @@ -193,7 +194,7 @@ class AttachmentDownloadJob( return true } else if (response.isSuccessful && !isCancelled && response.body != null) { val sink = destination.sink().buffer() - sink.writeAll(response.body!!.source()) + sink.writeAll(requireNotNull(response.body).source()) sink.close() if (message.category.endsWith("_IMAGE")) { val attachmentCipherInputStream = diff --git a/app/src/main/java/one/mixin/android/job/RefreshAddressJob.kt b/app/src/main/java/one/mixin/android/job/RefreshAddressJob.kt index 3b6b06cd33..db5c5fee93 100644 --- a/app/src/main/java/one/mixin/android/job/RefreshAddressJob.kt +++ b/app/src/main/java/one/mixin/android/job/RefreshAddressJob.kt @@ -15,9 +15,8 @@ class RefreshAddressJob(private val chainId: String) : BaseJob( override fun onRun() = runBlocking { val response = tokenService.addresses(chainId) if (response != null && response.isSuccess && response.data != null) { - response.data?.let { - addressDao.insertList(it) - } + val addresses = requireNotNull(response.data) + addressDao.insertList(addresses) } } } diff --git a/app/src/main/java/one/mixin/android/job/SendMessageJob.kt b/app/src/main/java/one/mixin/android/job/SendMessageJob.kt index 5dbaafb16c..7650178967 100644 --- a/app/src/main/java/one/mixin/android/job/SendMessageJob.kt +++ b/app/src/main/java/one/mixin/android/job/SendMessageJob.kt @@ -271,11 +271,12 @@ open class SendMessageJob( } else { message.content!!.toByteArray() } + val participantPublicKey: String = participantSessionKey.publicKey ?: return val encryptContent = encryptedProtocol.encryptMessage( keyPair, plaintext, - participantSessionKey.publicKey!!.base64RawURLDecode(), + participantPublicKey.base64RawURLDecode(), participantSessionKey.sessionId, extensionSessionKey?.publicKey?.base64RawURLDecode(), extensionSessionKey?.sessionId, diff --git a/app/src/main/java/one/mixin/android/job/TranscriptAttachmentDownloadJob.kt b/app/src/main/java/one/mixin/android/job/TranscriptAttachmentDownloadJob.kt index dbb2f1dff5..f7dc1085eb 100644 --- a/app/src/main/java/one/mixin/android/job/TranscriptAttachmentDownloadJob.kt +++ b/app/src/main/java/one/mixin/android/job/TranscriptAttachmentDownloadJob.kt @@ -87,8 +87,9 @@ class TranscriptAttachmentDownloadJob( } attachmentCall = conversationApi.getAttachment(attachmentId) val body = attachmentCall!!.execute().body() - if (body != null && (body.isSuccess || !isCancelled) && body.data != null) { - val viewUrl = body.data?.view_url + if (body != null && body.isSuccess && !isCancelled && body.data != null) { + val attachmentResponse = body.data + val viewUrl = attachmentResponse?.view_url if (viewUrl != null) { if (decryptAttachment(viewUrl, transcriptMessage)) { processTranscript() @@ -162,7 +163,7 @@ class TranscriptAttachmentDownloadJob( return true } else if (response.isSuccessful && !isCancelled && response.body != null) { val sink = destination.sink().buffer() - sink.writeAll(response.body!!.source()) + sink.writeAll(requireNotNull(response.body).source()) sink.close() when { transcriptMessage.type.endsWith("_IMAGE") -> { diff --git a/app/src/main/java/one/mixin/android/net/Diagnosis.kt b/app/src/main/java/one/mixin/android/net/Diagnosis.kt index 7d07ab52db..49a58e5600 100644 --- a/app/src/main/java/one/mixin/android/net/Diagnosis.kt +++ b/app/src/main/java/one/mixin/android/net/Diagnosis.kt @@ -133,13 +133,16 @@ private fun getExportIp( val client = OkHttpClient() var ipRequest = Request.Builder().url(EXPORT_IP_PRIMARY).build() try { - var data = - client.newCall(ipRequest).execute().body?.string() - ?: throw IOException("EXPORT_IP_PRIMARY no data") + var data: String = client.newCall(ipRequest).execute().body.string() + if (data.isBlank()) { + throw IOException("EXPORT_IP_PRIMARY no data") + } val url = data.substringIgnoreError(data.indexOf("src=") + 4, data.lastIndexOf("frameborder")).replace("'".toRegex(), "").replace(" ".toRegex(), "") ipRequest = Request.Builder().url(url).build() - data = client.newCall(ipRequest).execute().body?.string() - ?: throw IOException("EXPORT_IP_PRIMARY no data") + data = client.newCall(ipRequest).execute().body.string() + if (data.isBlank()) { + throw IOException("EXPORT_IP_PRIMARY no data") + } val dataIp = data.substringIgnoreError(data.indexOf("您的IP地址信息") + 10) val dataAddress = dataIp.substringIgnoreError(0, dataIp.indexOf("
")) val ips = dataAddress.split(" ").toTypedArray() @@ -149,7 +152,7 @@ private fun getExportIp( Timber.i("Get export ip from $EXPORT_IP_PRIMARY meet ${e.localizedMessage}") try { ipRequest = Request.Builder().url(EXPORT_IP_SECONDARY).build() - val exportIp = client.newCall(ipRequest).execute().body?.string() + val exportIp: String = client.newCall(ipRequest).execute().body.string() result.append("${context.getString(R.string.export_ip)}: $exportIp") } catch (e: Exception) { Timber.i("Get export ip from $EXPORT_IP_SECONDARY meet ${e.localizedMessage}") diff --git a/app/src/main/java/one/mixin/android/pay/Lighting.kt b/app/src/main/java/one/mixin/android/pay/Lighting.kt index ba6b98d6cd..7bb9f8f2e9 100644 --- a/app/src/main/java/one/mixin/android/pay/Lighting.kt +++ b/app/src/main/java/one/mixin/android/pay/Lighting.kt @@ -13,8 +13,9 @@ internal suspend fun parseLightning( parseLighting: suspend (String) -> PaymentResponse? ): ExternalTransfer? { val r = parseLighting(url) ?: return null - val assetId = r.asset?.assetId ?:return null - val chainId = r.asset?.chainId ?:return null + val asset = r.asset ?: return null + val assetId = asset.assetId ?: return null + val chainId = asset.chainId ?: return null val destination = r.destination ?: return null val addressResponse = validateAddress(assetId, chainId, destination) ?: return null diff --git a/app/src/main/java/one/mixin/android/ui/auth/compose/AuthBottomSheetDialogCompose.kt b/app/src/main/java/one/mixin/android/ui/auth/compose/AuthBottomSheetDialogCompose.kt index 7c788cd804..68175cf468 100644 --- a/app/src/main/java/one/mixin/android/ui/auth/compose/AuthBottomSheetDialogCompose.kt +++ b/app/src/main/java/one/mixin/android/ui/auth/compose/AuthBottomSheetDialogCompose.kt @@ -371,30 +371,32 @@ fun Modifier.verticalScrollbar( @Composable @Preview fun AuthBottomSheetDialogComposePreview() { - val context = LocalContext.current - AuthBottomSheetDialogCompose( - name = "Team Mixin", - iconUrl = "https://mixin-images.zeromesh.net/E2y0BnTopFK9qey0YI-8xV3M82kudNnTaGw0U5SU065864SsewNUo6fe9kDF1HIzVYhXqzws4lBZnLj1lPsjk-0=s256", - scopes = - listOf( - Scope.generateScopeFromString(context, "PROFILE:READ"), - Scope.generateScopeFromString(context, "PHONE:READ"), - Scope.generateScopeFromString(context, "MESSAGES:REPRESENT"), - Scope.generateScopeFromString(context, "CONTACTS:READ"), - Scope.generateScopeFromString(context, "ASSETS:READ"), - Scope.generateScopeFromString(context, "SNAPSHOTS:READ"), - Scope.generateScopeFromString(context, "APPS:READ"), - Scope.generateScopeFromString(context, "APPS:WRITE"), - Scope.generateScopeFromString(context, "CIRCLES:READ"), - Scope.generateScopeFromString(context, "CIRCLES:WRITE"), - Scope.generateScopeFromString(context, "COLLECTIBLES:READ"), - ), - {}, - AuthStep.INPUT, - "", - {}, - {}, - {}, - null, - ) + MixinAppTheme { + val context = LocalContext.current + AuthBottomSheetDialogCompose( + name = "Team Mixin", + iconUrl = "https://mixin-images.zeromesh.net/E2y0BnTopFK9qey0YI-8xV3M82kudNnTaGw0U5SU065864SsewNUo6fe9kDF1HIzVYhXqzws4lBZnLj1lPsjk-0=s256", + scopes = + listOf( + Scope.generateScopeFromString(context, "PROFILE:READ"), + Scope.generateScopeFromString(context, "PHONE:READ"), + Scope.generateScopeFromString(context, "MESSAGES:REPRESENT"), + Scope.generateScopeFromString(context, "CONTACTS:READ"), + Scope.generateScopeFromString(context, "ASSETS:READ"), + Scope.generateScopeFromString(context, "SNAPSHOTS:READ"), + Scope.generateScopeFromString(context, "APPS:READ"), + Scope.generateScopeFromString(context, "APPS:WRITE"), + Scope.generateScopeFromString(context, "CIRCLES:READ"), + Scope.generateScopeFromString(context, "CIRCLES:WRITE"), + Scope.generateScopeFromString(context, "COLLECTIBLES:READ"), + ), + {}, + AuthStep.INPUT, + "", + {}, + {}, + {}, + null, + ) + } } diff --git a/app/src/main/java/one/mixin/android/ui/auth/compose/PinKeyBoard.kt b/app/src/main/java/one/mixin/android/ui/auth/compose/PinKeyBoard.kt index 21ec73f753..d55b406fdc 100644 --- a/app/src/main/java/one/mixin/android/ui/auth/compose/PinKeyBoard.kt +++ b/app/src/main/java/one/mixin/android/ui/auth/compose/PinKeyBoard.kt @@ -53,6 +53,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -82,14 +83,19 @@ fun PinKeyBoard( onVerifyRequest: ((String) -> Unit)?, ) { val context = LocalContext.current + val isInPreview = LocalInspectionMode.current // val open = context.defaultSharedPreferences.getBoolean(Constants.Account.PREF_BIOMETRICS, false) // val biometricEnable = !open && BiometricUtil.isSupport(context) - val showBiometric = BiometricUtil.shouldShowBiometric(context) - val randomKeyboardEnabled by LocalContext.current.defaultSharedPreferences - .booleanValueAsState( - key = Constants.Account.PREF_RANDOM, - defaultValue = false, - ) + val showBiometric = if (isInPreview) false else BiometricUtil.shouldShowBiometric(context) + val randomKeyboardEnabled by if (isInPreview) { + remember { mutableStateOf(false) } + } else { + LocalContext.current.defaultSharedPreferences + .booleanValueAsState( + key = Constants.Account.PREF_RANDOM, + defaultValue = false, + ) + } val list = if (randomKeyboardEnabled) { mutableListOf("1", "2", "3", "4", "5", "6", "7", "8", "9", "0").apply { @@ -416,5 +422,7 @@ fun PinKeyBoard( @Preview @Composable fun PinKeyBoardPreview() { - PinKeyBoard(AuthStep.INPUT, "", {}, null, null) + MixinAppTheme { + PinKeyBoard(AuthStep.INPUT, "", {}, null, null) + } } diff --git a/app/src/main/java/one/mixin/android/ui/home/inscription/component/ShareCard.kt b/app/src/main/java/one/mixin/android/ui/home/inscription/component/ShareCard.kt index a265a07ae3..2b01b93b26 100644 --- a/app/src/main/java/one/mixin/android/ui/home/inscription/component/ShareCard.kt +++ b/app/src/main/java/one/mixin/android/ui/home/inscription/component/ShareCard.kt @@ -36,6 +36,7 @@ import coil3.request.transformations import one.mixin.android.R import one.mixin.android.compose.CoilImage import one.mixin.android.compose.CoilImageCompat +import one.mixin.android.compose.theme.MixinAppTheme import one.mixin.android.inscription.compose.Barcode import one.mixin.android.inscription.compose.TextInscription import one.mixin.android.ui.home.web3.components.InscriptionState @@ -143,12 +144,14 @@ fun ShareCard(modifier: Modifier, qrcode: Bitmap, inscriptionHash: String, value @Preview @Composable private fun DashedDividerPreview() { - DashedDivider( - thickness = 1.dp, - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) + MixinAppTheme { + DashedDivider( + thickness = 1.dp, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) + } } @Composable @@ -174,4 +177,4 @@ fun DashedDivider( ) ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/one/mixin/android/ui/home/web3/components/InputAction.kt b/app/src/main/java/one/mixin/android/ui/home/web3/components/InputAction.kt index cca601d60c..40bec1bc9f 100644 --- a/app/src/main/java/one/mixin/android/ui/home/web3/components/InputAction.kt +++ b/app/src/main/java/one/mixin/android/ui/home/web3/components/InputAction.kt @@ -85,5 +85,7 @@ fun InputAction( @Preview @Composable fun PreviewInputActionMax() { - InputAction("MAX") {} + MixinAppTheme { + InputAction("MAX") {} + } } diff --git a/app/src/main/java/one/mixin/android/ui/home/web3/components/Review.kt b/app/src/main/java/one/mixin/android/ui/home/web3/components/Review.kt index 8df83ca674..d79d3a897a 100644 --- a/app/src/main/java/one/mixin/android/ui/home/web3/components/Review.kt +++ b/app/src/main/java/one/mixin/android/ui/home/web3/components/Review.kt @@ -46,6 +46,7 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -481,9 +482,15 @@ private fun ApproveChangeItem( private fun SingleBalanceChangeItem( bc: BalanceChange ) { - val viewModel = hiltViewModel() - val priceUsd: String? by viewModel.getTokenPriceUsdFlow(bc.assetId) - .collectAsStateWithLifecycle(initialValue = null) + val isInPreview: Boolean = LocalInspectionMode.current + val priceUsd: String? = if (isInPreview) { + null + } else { + val viewModel: Web3ViewModel = hiltViewModel() + val collectedPriceUsd: String? by viewModel.getTokenPriceUsdFlow(bc.assetId) + .collectAsStateWithLifecycle(initialValue = null) + collectedPriceUsd + } val fiatPrice = bc.formatPrice(priceUsd) Row( @@ -528,9 +535,15 @@ private fun SingleBalanceChangeItem( private fun BalanceChangeItem( balanceChange: BalanceChange, ) { - val viewModel = hiltViewModel() - val priceUsd: String? by viewModel.getTokenPriceUsdFlow(balanceChange.assetId) - .collectAsStateWithLifecycle(initialValue = null) + val isInPreview: Boolean = LocalInspectionMode.current + val priceUsd: String? = if (isInPreview) { + null + } else { + val viewModel: Web3ViewModel = hiltViewModel() + val collectedPriceUsd: String? by viewModel.getTokenPriceUsdFlow(balanceChange.assetId) + .collectAsStateWithLifecycle(initialValue = null) + collectedPriceUsd + } val fiatPrice = balanceChange.formatPrice(priceUsd) Row( modifier = Modifier.fillMaxWidth(), @@ -662,9 +675,10 @@ private fun Item( @Preview @Composable fun PreviewMessage() { - Box(modifier = Modifier.background(MixinAppTheme.colors.background)) { - MessagePreview( - content = """{ + MixinAppTheme { + Box(modifier = Modifier.background(MixinAppTheme.colors.background)) { + MessagePreview( + content = """{ "raw": [ "0x9df67f5a05fb594c4357d87221cbd69f1d5a6fbb", "{\"types\":{\"Alias\":[{\"name\":\"from\",\"type\":\"address\"},{\"name\":\"alias\",\"type\":\"address\"},{\"name\":\"timestamp\",\"type\":\"uint64\"}],\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"}]},\"domain\":{\"name\":\"snapshot\",\"version\":\"0.1.4\"},\"primaryType\":\"Alias\",\"message\":{\"from\":\"0x9df67f5a05fb594c4357d87221cbd69f1d5a6fbb\",\"alias\":\"0x8f14e8dbc7b3619e5210201022f637f271545c90\",\"timestamp\":\"1710766295\"}}" @@ -672,7 +686,8 @@ fun PreviewMessage() { "type": "TYPED_MESSAGE" } """, - ) { + ) { + } } } } @@ -680,27 +695,31 @@ fun PreviewMessage() { @Preview @Composable private fun TransactionPreview() { - TransactionPreview(balance = BigDecimal(0.134), chain = Chain.Ethereum, null) + MixinAppTheme { + TransactionPreview(balance = BigDecimal(0.134), chain = Chain.Ethereum, null) + } } @Preview @Composable private fun WarningPreview() { - Box( - modifier = - Modifier - .fillMaxWidth() - .height(300.dp), - ) { - ActionBottom( - modifier = Modifier.align(Alignment.BottomCenter), - cancelTitle = stringResource(id = R.string.Cancel), - confirmTitle = stringResource(id = R.string.Confirm), - cancelAction = { }, + MixinAppTheme { + Box( + modifier = + Modifier + .fillMaxWidth() + .height(300.dp), ) { - } + ActionBottom( + modifier = Modifier.align(Alignment.BottomCenter), + cancelTitle = stringResource(id = R.string.Cancel), + confirmTitle = stringResource(id = R.string.Confirm), + cancelAction = { }, + ) { + } - Warning(modifier = Modifier.align(Alignment.BottomCenter)) + Warning(modifier = Modifier.align(Alignment.BottomCenter)) + } } } @@ -776,9 +795,11 @@ fun ActionBottom( @Preview @Composable fun TransferBottomPreview() { - Column { - ActionBottom(modifier = Modifier, stringResource(id = R.string.Cancel), stringResource(id = R.string.Confirm), {}, {}) - ActionBottom(modifier = Modifier, stringResource(id = R.string.Discard), stringResource(id = R.string.Send), {}, {}) + MixinAppTheme { + Column { + ActionBottom(modifier = Modifier, stringResource(id = R.string.Cancel), stringResource(id = R.string.Confirm), {}, {}) + ActionBottom(modifier = Modifier, stringResource(id = R.string.Discard), stringResource(id = R.string.Send), {}, {}) + } } } @@ -792,7 +813,9 @@ fun BalanceChangePreview() { @Preview @Composable fun ItemPreview() { - Item(Item("Compute Unit Limit", "1400000 compute units")) + MixinAppTheme { + Item(Item("Compute Unit Limit", "1400000 compute units")) + } } @Preview @@ -810,13 +833,17 @@ fun SolanaParsedTxPreviewPreview() { @Preview @Composable fun InstructionPreview() { - Instruction(ParsedInstruction("", "", "", info = "cannot decode instruction for Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB")) + MixinAppTheme { + Instruction(ParsedInstruction("", "", "", info = "cannot decode instruction for Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB")) + } } @Preview @Composable fun SolanaParsedTxNullPreview() { - ParsedTxPreview(parsedTx = null, asset = null, solanaTxSource = SolanaTxSource.Web) + MixinAppTheme { + ParsedTxPreview(parsedTx = null, asset = null, solanaTxSource = SolanaTxSource.Web) + } } @Preview @@ -824,7 +851,9 @@ fun SolanaParsedTxNullPreview() { fun SolanaParsedTxInstructionNullPreview() { val data = """{"instructions":[]}""" val parsedTx = GsonHelper.customGson.fromJson(data, ParsedTx::class.java) - ParsedTxPreview(parsedTx = parsedTx, asset = null, solanaTxSource = SolanaTxSource.Web) + MixinAppTheme { + ParsedTxPreview(parsedTx = parsedTx, asset = null, solanaTxSource = SolanaTxSource.Web) + } } @Preview @@ -832,7 +861,9 @@ fun SolanaParsedTxInstructionNullPreview() { fun SolanaParsedTxBalanceChangeNullWebPreview() { val data = """{"instructions":[{"program_id":"ComputeBudget111111111111111111111111111111","program_name":"ComputeBudget","instruction_name":"SetComputeUnitLimit","items":[{"key":"Compute Unit Limit","value":"600000 compute units"}]},{"program_id":"ComputeBudget111111111111111111111111111111","program_name":"ComputeBudget","instruction_name":"SetComputeUnitPrice","items":[{"key":"Compute Unit Price","value":"0.1 lamports per compute unit"}]},{"program_id":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL","program_name":"AssociatedTokenAccount","instruction_name":"Create"},{"program_id":"11111111111111111111111111111111","program_name":"System","instruction_name":"Transfer","items":[{"key":"Transfer Amount (SOL)","value":"0.01"}]},{"program_id":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","program_name":"Token","instruction_name":"SyncNative"},{"program_id":"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4","program_name":"Jupiter","instruction_name":"Route","items":[{"key":"Route Plan","value":""},{"key":"In Amount","value":"824635312696"},{"key":"Quoted Out Amount","value":"824635312704"},{"key":"Slippage Bps","value":"824635312712"},{"key":"Platform Fee Bps","value":"50"}],"token_changes":[{"address":"So11111111111111111111111111111111111111112","amount":10000000,"is_pay":true},{"address":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","amount":1323264,"is_pay":false}]},{"program_id":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","program_name":"Token","instruction_name":"CloseAccount"}]}""" val parsedTx = GsonHelper.customGson.fromJson(data, ParsedTx::class.java) - ParsedTxPreview(parsedTx = parsedTx, asset = null, solanaTxSource = SolanaTxSource.Web) + MixinAppTheme { + ParsedTxPreview(parsedTx = parsedTx, asset = null, solanaTxSource = SolanaTxSource.Web) + } } @Preview @@ -840,7 +871,9 @@ fun SolanaParsedTxBalanceChangeNullWebPreview() { fun SolanaParsedTxBalanceChangeNullInnerPreview() { val data = """{"instructions":[{"program_id":"ComputeBudget111111111111111111111111111111","program_name":"ComputeBudget","instruction_name":"SetComputeUnitLimit","items":[{"key":"Compute Unit Limit","value":"600000 compute units"}]},{"program_id":"ComputeBudget111111111111111111111111111111","program_name":"ComputeBudget","instruction_name":"SetComputeUnitPrice","items":[{"key":"Compute Unit Price","value":"0.1 lamports per compute unit"}]},{"program_id":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL","program_name":"AssociatedTokenAccount","instruction_name":"Create"},{"program_id":"11111111111111111111111111111111","program_name":"System","instruction_name":"Transfer","items":[{"key":"Transfer Amount (SOL)","value":"0.01"}]},{"program_id":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","program_name":"Token","instruction_name":"SyncNative"},{"program_id":"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4","program_name":"Jupiter","instruction_name":"Route","items":[{"key":"Route Plan","value":""},{"key":"In Amount","value":"824635312696"},{"key":"Quoted Out Amount","value":"824635312704"},{"key":"Slippage Bps","value":"824635312712"},{"key":"Platform Fee Bps","value":"50"}],"token_changes":[{"address":"So11111111111111111111111111111111111111112","amount":10000000,"is_pay":true},{"address":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","amount":1323264,"is_pay":false}]},{"program_id":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","program_name":"Token","instruction_name":"CloseAccount"}]}""" val parsedTx = GsonHelper.customGson.fromJson(data, ParsedTx::class.java) - ParsedTxPreview(parsedTx = parsedTx, asset = null, solanaTxSource = SolanaTxSource.InnerSwap) + MixinAppTheme { + ParsedTxPreview(parsedTx = parsedTx, asset = null, solanaTxSource = SolanaTxSource.InnerSwap) + } } @Preview @@ -848,5 +881,7 @@ fun SolanaParsedTxBalanceChangeNullInnerPreview() { fun SolanaParsedTxTokenNullPreview() { val data = """{"balance_changes":[{"address":"So11111111111111111111111111111111111111112","amount":-10000000},{"address":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","amount":1323264}],"instructions":[{"program_id":"ComputeBudget111111111111111111111111111111","program_name":"ComputeBudget","instruction_name":"SetComputeUnitLimit","items":[{"key":"Compute Unit Limit","value":"600000 compute units"}]},{"program_id":"ComputeBudget111111111111111111111111111111","program_name":"ComputeBudget","instruction_name":"SetComputeUnitPrice","items":[{"key":"Compute Unit Price","value":"0.1 lamports per compute unit"}]},{"program_id":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL","program_name":"AssociatedTokenAccount","instruction_name":"Create"},{"program_id":"11111111111111111111111111111111","program_name":"System","instruction_name":"Transfer","items":[{"key":"Transfer Amount (SOL)","value":"0.01"}]},{"program_id":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","program_name":"Token","instruction_name":"SyncNative"},{"program_id":"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4","program_name":"Jupiter","instruction_name":"Route","items":[{"key":"Route Plan","value":""},{"key":"In Amount","value":"824635312696"},{"key":"Quoted Out Amount","value":"824635312704"},{"key":"Slippage Bps","value":"824635312712"},{"key":"Platform Fee Bps","value":"50"}],"token_changes":[{"address":"So11111111111111111111111111111111111111112","amount":10000000,"is_pay":true},{"address":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","amount":1323264,"is_pay":false}]},{"program_id":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","program_name":"Token","instruction_name":"CloseAccount"}]}""" val parsedTx = GsonHelper.customGson.fromJson(data, ParsedTx::class.java) - ParsedTxPreview(parsedTx = parsedTx, asset = null, solanaTxSource = SolanaTxSource.InnerSwap) -} \ No newline at end of file + MixinAppTheme { + ParsedTxPreview(parsedTx = parsedTx, asset = null, solanaTxSource = SolanaTxSource.InnerSwap) + } +} diff --git a/app/src/main/java/one/mixin/android/ui/home/web3/components/SlippageInfo.kt b/app/src/main/java/one/mixin/android/ui/home/web3/components/SlippageInfo.kt index 5f91c2bfb8..4f6e6fa25c 100644 --- a/app/src/main/java/one/mixin/android/ui/home/web3/components/SlippageInfo.kt +++ b/app/src/main/java/one/mixin/android/ui/home/web3/components/SlippageInfo.kt @@ -112,12 +112,15 @@ private fun SlippageInfo( @Preview @Composable fun PreviewSlippageInfo() { - SlippageInfo(slippageBps = 50, true) {} + MixinAppTheme { + SlippageInfo(slippageBps = 50, true) {} + } } @Preview @Composable fun PreviewSlippageInfoWarning() { - SlippageInfo(slippageBps = 600, true) {} + MixinAppTheme { + SlippageInfo(slippageBps = 600, true) {} + } } - diff --git a/app/src/main/java/one/mixin/android/ui/home/web3/stake/StakePage.kt b/app/src/main/java/one/mixin/android/ui/home/web3/stake/StakePage.kt index 906579f102..db6fa57b72 100644 --- a/app/src/main/java/one/mixin/android/ui/home/web3/stake/StakePage.kt +++ b/app/src/main/java/one/mixin/android/ui/home/web3/stake/StakePage.kt @@ -345,11 +345,15 @@ fun Item( @Preview @Composable private fun InputPreview() { - Input(text = "123") {} + MixinAppTheme { + Input(text = "123") {} + } } @Preview @Composable private fun ValidatorInfoPreview() { - ValidatorInfo(Validator("J2nUHEAgZFRyuJbFjdqPrAa9gyWDuc7hErtDQHPhsYRp", "Mixin Validator", "", "", "", "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png", "", 123123131231231, 9, 123124, 123123)) -} \ No newline at end of file + MixinAppTheme { + ValidatorInfo(Validator("J2nUHEAgZFRyuJbFjdqPrAa9gyWDuc7hErtDQHPhsYRp", "Mixin Validator", "", "", "", "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png", "", 123123131231231, 9, 123124, 123123)) + } +} diff --git a/app/src/main/java/one/mixin/android/ui/home/web3/stake/ValidatorsPage.kt b/app/src/main/java/one/mixin/android/ui/home/web3/stake/ValidatorsPage.kt index 8c4d81e05a..d67c09b6d0 100644 --- a/app/src/main/java/one/mixin/android/ui/home/web3/stake/ValidatorsPage.kt +++ b/app/src/main/java/one/mixin/android/ui/home/web3/stake/ValidatorsPage.kt @@ -221,11 +221,15 @@ private fun SearchInput( @Composable fun PreviewValidatorItem() { val validator = Validator("J2nUHEAgZFRyuJbFjdqPrAa9gyWDuc7hErtDQHPhsYRp", "Mixin Validator", "", "", "", "https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png", "", 123123131231231, 9, 123124, 123123) - ValidatorItem(validator) { } + MixinAppTheme { + ValidatorItem(validator) { } + } } @Preview @Composable fun PreviewSearchInput() { - SearchInput("") -} \ No newline at end of file + MixinAppTheme { + SearchInput("") + } +} diff --git a/app/src/main/java/one/mixin/android/ui/home/web3/trade/SwapSlippagePage.kt b/app/src/main/java/one/mixin/android/ui/home/web3/trade/SwapSlippagePage.kt index aa7dc0456c..e9a782e893 100644 --- a/app/src/main/java/one/mixin/android/ui/home/web3/trade/SwapSlippagePage.kt +++ b/app/src/main/java/one/mixin/android/ui/home/web3/trade/SwapSlippagePage.kt @@ -289,23 +289,27 @@ private fun String.isSlippageValid(): Boolean { @Preview @Composable fun PreviewAuto() { - Auto( - auto = - remember { - mutableStateOf(true) - }, - originAuto = true, - originBps = 80, - ) + MixinAppTheme { + Auto( + auto = + remember { + mutableStateOf(true) + }, + originAuto = true, + originBps = 80, + ) + } } @Preview @Composable fun PreviewCustom() { - Custom( - bps = - remember { - mutableStateOf("50") - }, - ) + MixinAppTheme { + Custom( + bps = + remember { + mutableStateOf("50") + }, + ) + } } diff --git a/app/src/main/java/one/mixin/android/ui/home/web3/trade/SwapTokenPage.kt b/app/src/main/java/one/mixin/android/ui/home/web3/trade/SwapTokenPage.kt index cd17c5ffec..b888c68c0d 100644 --- a/app/src/main/java/one/mixin/android/ui/home/web3/trade/SwapTokenPage.kt +++ b/app/src/main/java/one/mixin/android/ui/home/web3/trade/SwapTokenPage.kt @@ -133,6 +133,8 @@ fun SwapTokenPage( @Preview(widthDp = 300) @Composable fun SwapTokenPagePreView() { - SwapTokenPage(token = SwapToken("","1111111111111111111111111", "", 9, "Solana", "SOL", "", SwapChain("", "Solana", "SOL", "", ""))) { + MixinAppTheme { + SwapTokenPage(token = SwapToken("", "1111111111111111111111111", "", 9, "Solana", "SOL", "", SwapChain("", "Solana", "SOL", "", ""))) { + } } } diff --git a/app/src/main/java/one/mixin/android/ui/landing/components/CreateAccountPage.kt b/app/src/main/java/one/mixin/android/ui/landing/components/CreateAccountPage.kt index 338aaef73b..1158d763eb 100644 --- a/app/src/main/java/one/mixin/android/ui/landing/components/CreateAccountPage.kt +++ b/app/src/main/java/one/mixin/android/ui/landing/components/CreateAccountPage.kt @@ -126,5 +126,7 @@ fun CreateItem(@DrawableRes iconId: Int, @StringRes titleId: Int, @StringRes sub @Preview @Composable fun CreateAccountPagePreview() { - CreateAccountPage({},{}, {}, {}, {}, {}) -} \ No newline at end of file + MixinAppTheme { + CreateAccountPage({}, {}, {}, {}, {}, {}) + } +} diff --git a/app/src/main/java/one/mixin/android/ui/landing/components/MnemonicPhraseInput.kt b/app/src/main/java/one/mixin/android/ui/landing/components/MnemonicPhraseInput.kt index 59ff77fbec..0a3864f811 100644 --- a/app/src/main/java/one/mixin/android/ui/landing/components/MnemonicPhraseInput.kt +++ b/app/src/main/java/one/mixin/android/ui/landing/components/MnemonicPhraseInput.kt @@ -986,11 +986,13 @@ fun InputBar(string: String, callback: (String) -> Unit) { @Preview(backgroundColor = 0xFFFFFFFF, showSystemUi = true) @Composable fun MnemonicPhraseInputPreview() { - MnemonicPhraseInput( - state = MnemonicState.Input, - onScan = {}, - onComplete = { mnemonicList -> /* Handle mnemonic change */ }, - ) + MixinAppTheme { + MnemonicPhraseInput( + state = MnemonicState.Input, + onScan = {}, + onComplete = { }, + ) + } } @Composable diff --git a/app/src/main/java/one/mixin/android/ui/media/pager/MediaPagerActivity.kt b/app/src/main/java/one/mixin/android/ui/media/pager/MediaPagerActivity.kt index 8d406cced3..fdd51e1c66 100644 --- a/app/src/main/java/one/mixin/android/ui/media/pager/MediaPagerActivity.kt +++ b/app/src/main/java/one/mixin/android/ui/media/pager/MediaPagerActivity.kt @@ -1,3 +1,5 @@ +@file:Suppress("DEPRECATION") + package one.mixin.android.ui.media.pager import android.Manifest diff --git a/app/src/main/java/one/mixin/android/ui/media/pager/transcript/TranscriptMediaPagerActivity.kt b/app/src/main/java/one/mixin/android/ui/media/pager/transcript/TranscriptMediaPagerActivity.kt index e06a1bd597..87b1d27882 100644 --- a/app/src/main/java/one/mixin/android/ui/media/pager/transcript/TranscriptMediaPagerActivity.kt +++ b/app/src/main/java/one/mixin/android/ui/media/pager/transcript/TranscriptMediaPagerActivity.kt @@ -593,7 +593,7 @@ class TranscriptMediaPagerActivity : BaseActivity(), DismissFrameLayout.OnDismis private fun dismiss() { binding.viewPager.visibility = View.INVISIBLE - overridePendingTransition(0, 0) + setExitTransition(enterAnim = 0, exitAnim = 0) super.finish() } @@ -750,7 +750,19 @@ class TranscriptMediaPagerActivity : BaseActivity(), DismissFrameLayout.OnDismis override fun finish() { super.finish() - overridePendingTransition(0, R.anim.scale_out) + setExitTransition(enterAnim = 0, exitAnim = R.anim.scale_out) + } + + private fun setExitTransition( + enterAnim: Int, + exitAnim: Int, + ) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + overrideActivityTransition(Activity.OVERRIDE_TRANSITION_CLOSE, enterAnim, exitAnim) + return + } + @Suppress("DEPRECATION") + overridePendingTransition(enterAnim, exitAnim) } private val mediaPagerAdapterListener = diff --git a/app/src/main/java/one/mixin/android/ui/setting/ui/page/MixinMemberUpgradePage.kt b/app/src/main/java/one/mixin/android/ui/setting/ui/page/MixinMemberUpgradePage.kt index 68c7d76c37..abe0a56dfb 100644 --- a/app/src/main/java/one/mixin/android/ui/setting/ui/page/MixinMemberUpgradePage.kt +++ b/app/src/main/java/one/mixin/android/ui/setting/ui/page/MixinMemberUpgradePage.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -58,7 +59,8 @@ fun MixinMemberUpgradePage( onContactTeamMixin: () -> Unit = {}, onViewInvoice: (MembershipOrder) -> Unit = {} ) { - val viewModel: MemberViewModel = hiltViewModel() + val isInPreview = LocalInspectionMode.current + val viewModel: MemberViewModel? = if (isInPreview) null else hiltViewModel() var purchaseState by remember { mutableStateOf(PlanPurchaseState()) } var savedOrderId by remember { mutableStateOf(null) } @@ -76,12 +78,28 @@ fun MixinMemberUpgradePage( ) } - val pendingOrderState by viewModel.pendingOrder.collectAsState() - val subscriptionPlans by viewModel.subscriptionPlans.collectAsState() + val pendingOrderState = if (isInPreview) { + null + } else { + viewModel?.pendingOrder?.collectAsState()?.value + } + val subscriptionPlans = if (isInPreview) { + emptyList() + } else { + viewModel?.subscriptionPlans?.collectAsState()?.value.orEmpty() + } LaunchedEffect(Unit) { + if (isInPreview) { + purchaseState = purchaseState.copy( + availablePlans = emptyList(), + availablePlayStorePlans = emptySet(), + loading = false, + ) + return@LaunchedEffect + } try { - val response = viewModel.getPlans() + val response = requireNotNull(viewModel).getPlans() if (response.isSuccess && response.data != null) { val availablePlayStorePlans = if (BuildConfig.IS_GOOGLE_PLAY) { val billingPlanIds = subscriptionPlans.map { it.planId }.toSet() @@ -117,9 +135,10 @@ fun MixinMemberUpgradePage( } LaunchedEffect(pendingOrderState?.orderId ?: "") { + if (isInPreview) return@LaunchedEffect try { while (pendingOrderState?.orderId.isNullOrEmpty().not()) { - val orderResponse = viewModel.getOrder(pendingOrderState!!.orderId) + val orderResponse = requireNotNull(viewModel).getOrder(pendingOrderState!!.orderId) if (orderResponse?.isSuccess == true && orderResponse.data != null) { val order = orderResponse.data!! val status = MemberOrderStatus.fromString(order.status) @@ -127,14 +146,14 @@ fun MixinMemberUpgradePage( when (status) { MemberOrderStatus.PAID, MemberOrderStatus.COMPLETED -> { Timber.d("Order completed: ${order.orderId}") - viewModel.insertOrders(order) + requireNotNull(viewModel).insertOrders(order) onClose() break } MemberOrderStatus.FAILED, MemberOrderStatus.EXPIRED, MemberOrderStatus.CANCEL -> { Timber.d("Order failed: ${order.orderId}") - viewModel.insertOrders(order) + requireNotNull(viewModel).insertOrders(order) onClose() break } @@ -188,7 +207,8 @@ fun MixinMemberUpgradePage( val plan = mapLocalPlanToMemberOrderPlan(selectedPlan, purchaseState.availablePlans) ?: return@MemberUpgradePaymentButton - viewModel.viewModelScope.launch(CoroutineExceptionHandler { _, error -> + val memberViewModel = viewModel ?: return@MemberUpgradePaymentButton + memberViewModel.viewModelScope.launch(CoroutineExceptionHandler { _, error -> purchaseState = purchaseState.copy(loading = false) purchaseState = purchaseState.copy( error = ErrorHandler.getErrorMessage(error) @@ -200,7 +220,7 @@ fun MixinMemberUpgradePage( } else { MemberOrderRequest(plan = plan.plan) } - val orderResponse = viewModel.createMemberOrder(orderRequest) + val orderResponse = memberViewModel.createMemberOrder(orderRequest) if (orderResponse.isSuccess && orderResponse.data != null) { orderResponse.data?.orderId?.let { orderId -> diff --git a/app/src/main/java/one/mixin/android/ui/tip/wc/sessionproposal/WCPinBoard.kt b/app/src/main/java/one/mixin/android/ui/tip/wc/sessionproposal/WCPinBoard.kt index d8f7bd3abb..b6a20d1741 100644 --- a/app/src/main/java/one/mixin/android/ui/tip/wc/sessionproposal/WCPinBoard.kt +++ b/app/src/main/java/one/mixin/android/ui/tip/wc/sessionproposal/WCPinBoard.kt @@ -54,6 +54,7 @@ import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString @@ -91,13 +92,18 @@ fun WCPinBoard( onPinComplete: ((String) -> Unit)?, ) { val context = LocalContext.current + val isInPreview = LocalInspectionMode.current val clipboardManager: ClipboardManager = LocalClipboardManager.current - val showBiometric = allowBiometric && BiometricUtil.shouldShowBiometric(context) - val randomKeyboardEnabled by LocalContext.current.defaultSharedPreferences - .booleanValueAsState( - key = Constants.Account.PREF_RANDOM, - defaultValue = false, - ) + val showBiometric = allowBiometric && !isInPreview && BiometricUtil.shouldShowBiometric(context) + val randomKeyboardEnabled by if (isInPreview) { + remember { mutableStateOf(false) } + } else { + LocalContext.current.defaultSharedPreferences + .booleanValueAsState( + key = Constants.Account.PREF_RANDOM, + defaultValue = false, + ) + } val list = if (randomKeyboardEnabled) { mutableListOf("1", "2", "3", "4", "5", "6", "7", "8", "9", "0").apply { @@ -460,5 +466,7 @@ fun WCPinBoard( @Preview @Composable fun WCPinBoardPreview() { - WCPinBoard(Step.Input, null, allowBiometric = false, true, {}, {}, {}, null, null) + MixinAppTheme { + WCPinBoard(Step.Input, null, allowBiometric = false, true, {}, {}, {}, null, null) + } } diff --git a/app/src/main/java/one/mixin/android/ui/tip/wc/sessionrequest/SessionRequestPage.kt b/app/src/main/java/one/mixin/android/ui/tip/wc/sessionrequest/SessionRequestPage.kt index 33623d8232..dfdbcbf514 100644 --- a/app/src/main/java/one/mixin/android/ui/tip/wc/sessionrequest/SessionRequestPage.kt +++ b/app/src/main/java/one/mixin/android/ui/tip/wc/sessionrequest/SessionRequestPage.kt @@ -588,17 +588,21 @@ fun FeeInfo( @Preview @Composable private fun NetworkInfoPreview() { - FeeInfo("0.0169028 ETH", BigDecimal("7.57")) + MixinAppTheme { + FeeInfo("0.0169028 ETH", BigDecimal("7.57")) + } } @Preview @Composable private fun HintPreview() { - Column(modifier = Modifier.padding(8.dp)) { - Hint(Hint.NoPreview) - Box(modifier = Modifier.height(8.dp)) - Hint(Hint.Cancel) - Box(modifier = Modifier.height(8.dp)) - Hint(Hint.SpeedUp) + MixinAppTheme { + Column(modifier = Modifier.padding(8.dp)) { + Hint(Hint.NoPreview) + Box(modifier = Modifier.height(8.dp)) + Hint(Hint.Cancel) + Box(modifier = Modifier.height(8.dp)) + Hint(Hint.SpeedUp) + } } } diff --git a/app/src/main/java/one/mixin/android/ui/transfer/compose/SelectDatePage.kt b/app/src/main/java/one/mixin/android/ui/transfer/compose/SelectDatePage.kt index cf2ff89c54..23a9bfcf8a 100644 --- a/app/src/main/java/one/mixin/android/ui/transfer/compose/SelectDatePage.kt +++ b/app/src/main/java/one/mixin/android/ui/transfer/compose/SelectDatePage.kt @@ -315,5 +315,7 @@ fun SegmentedControl( @Composable @Preview fun SelectDatePagePreview() { - SelectDatePage(onExit = {}, onResult = {}) + MixinAppTheme { + SelectDatePage(onExit = {}, onResult = {}) + } } diff --git a/app/src/main/java/one/mixin/android/ui/wallet/components/AssetDashboardScreen.kt b/app/src/main/java/one/mixin/android/ui/wallet/components/AssetDashboardScreen.kt index 559dcaf062..dbe600d97c 100644 --- a/app/src/main/java/one/mixin/android/ui/wallet/components/AssetDashboardScreen.kt +++ b/app/src/main/java/one/mixin/android/ui/wallet/components/AssetDashboardScreen.kt @@ -930,9 +930,11 @@ fun CreateSafeCard( @Preview @Composable fun CardPreview() { - Column { - CreateSafeCard {} - Spacer(modifier = Modifier.height(8.dp)) - UpgradeSafeCard({}, {}) + MixinAppTheme { + Column { + CreateSafeCard {} + Spacer(modifier = Modifier.height(8.dp)) + UpgradeSafeCard({}, {}) + } } }