diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 84352e59..00000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -app/src/main/assets/appverifier_rs-third-party-licenses.html linguist-detectable=false -appverifier_rs/about.hbs linguist-detectable=false diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index ae3f30ae..6d0ee1c2 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eadda98d..e631c9c3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,19 +4,10 @@ Thanks for your interest in contributing! If you want to suggest a feature or notify us about a bug, please use the issue tracker. -If you want to contribute to the internal verification info database, you currently must contribute in code form. -You can use IntelliJ IDEA Community edition, which is what the lead developer, soupslurpr uses. -It is available at https://www.jetbrains.com/idea/download/?section=windows#:~:text=free%20to%20use-,IntelliJ%20IDEA%20Community%20Edition,-The%20IDE%20for +Before working on a feature, please make sure to discuss the planned implementation in the issue for +the feature and get approval from @soupslurpr to ensure it meets the project's requirements. -Open the file at app/src/main/kotlin/dev/soupslurpr/appverifier/InternalVerificationInfoDatabase.kt for the database and start adding entries from the bottom. -Use AppVerifier (with "Show hasMultipleSigners" on in Settings) to get the verification info. -Other tools may not provide all the needed info or hashes so do not use them. -You must check the app's website or repo to see which sources they say the app is officially available from (view INTERNAL_DATABASE_CRITERIA.md), -and check the verification info of each source to see if it's the same. If the package name is -the same but not the hashes, then you have to make a `Hashes` in the same `InternalDatabaseVerificationInfo` with the hashes for the app from that source and whether it has multiple signers. -If the package name is different, then create another `InternalDatabaseVerificationInfo`. -Please view the comment for `InternalDatabaseVerificationInfo` for more info and examples. -Make one pull request for every app instead of multiple apps in one pull request. +Contributions to the internal verification info database might not be accepted at this time. If you need help with development or have questions it's recommended to join the AppVerifier room on Matrix at https://matrix.to/#/#appverifier:matrix.org and ask for help there from [soupslurpr](https://github.com/soupslurpr), diff --git a/README.md b/README.md index 30307536..09f1eef5 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,10 @@ AppVerifier is an app signing certificate hash viewer and verifier.\ It enables you to easily verify that your apps are genuine with others! AppVerifier takes the app's package name and signing certificates hash(es) and compares them to the ones you provided or the ones in the internal database to verify that your apps are genuine.\ -If an app isn't on the internal database yet, you can simply share the verification info to others and receive verification info from them and +You can simply share the verification info to others and receive verification info from them and share the received verification info to AppVerifier and you will see the verification status.\ AppVerifier does the heavy lifting for you 💪 -Check [CONTRIBUTING.md](CONTRIBUTING.md) if you'd like to contribute to the internal database. - ## Download AppVerifier is available on the [Accrescent](https://accrescent.app) app store and GitHub releases. [Accrescent](https://accrescent.app) is the recommended way to get AppVerifier as it is more secure than GitHub releases.\ diff --git a/app/.gitignore b/app/.gitignore index 42afabfd..956c004d 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1 +1,2 @@ -/build \ No newline at end of file +/build +/release \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 82b34efa..fcb54814 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("com.android.application") id("org.jetbrains.kotlin.android") + id("org.jetbrains.kotlin.plugin.compose") version "2.0.0" } android { @@ -11,13 +12,17 @@ android { applicationId = "dev.soupslurpr.appverifier" minSdk = 28 targetSdk = 34 - versionCode = 9 - versionName = "0.7.0" + versionCode = 10 + versionName = "0.8.0" - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary = true } + + ndk { + abiFilters.clear() + abiFilters.addAll(listOf("arm64-v8a", "x86_64")) + } } compileOptions { @@ -32,12 +37,7 @@ android { buildConfig = true } composeOptions { - kotlinCompilerExtensionVersion = "1.5.9" - } - packaging { - resources { - excludes += "/META-INF/{AL2.0,LGPL2.1}" - } + kotlinCompilerExtensionVersion = "1.5.14" } androidResources { generateLocaleConfig = true @@ -63,15 +63,9 @@ android { signingConfig = signingConfigs.getByName("debug") } } - bundle { - language { - /** Disable splits for language for now since Accrescent does not support automatically - * fetching language splits when language is changed and instead needs a reinstall of the app. - * Remove once Accrescent gets support. - */ - enableSplit = false - } - } + // Useless since we don't publish to the Google Play Store and they are the only ones who can + // view it. + // Reference: https://developer.android.com/reference/tools/gradle-api/8.6/com/android/build/api/dsl/DependenciesInfo dependenciesInfo { // Disables dependency metadata when building APKs. includeInApk = false @@ -81,15 +75,15 @@ android { } dependencies { - implementation("androidx.core:core-ktx:1.12.0") - implementation("androidx.activity:activity-compose:1.8.2") - implementation("androidx.navigation:navigation-compose:2.7.7") - implementation("androidx.datastore:datastore-preferences:1.0.0") + implementation("androidx.core:core-ktx:1.13.1") + implementation("androidx.activity:activity-compose:1.9.0") + implementation("androidx.navigation:navigation-compose:2.8.0-beta05") + implementation("androidx.datastore:datastore-preferences:1.1.1") implementation("androidx.compose.material:material-icons-extended") - implementation("com.google.android.material:material:1.11.0") - implementation("com.google.accompanist:accompanist-drawablepainter:0.33.2-alpha") + implementation("com.google.accompanist:accompanist-drawablepainter:0.35.1-alpha") - implementation(platform("androidx.compose:compose-bom:2024.02.00")) - implementation("androidx.compose.ui:ui-tooling-preview") + implementation(platform("androidx.compose:compose-bom:2024.06.00")) implementation("androidx.compose.material3:material3") + implementation("androidx.compose.ui:ui-tooling-preview") + debugImplementation("androidx.compose.ui:ui-tooling") } diff --git a/app/release/signing-command.txt b/app/release/signing-command.txt deleted file mode 100644 index 842a4521..00000000 --- a/app/release/signing-command.txt +++ /dev/null @@ -1 +0,0 @@ -java -jar ./bundletool-all-1.15.6.jar build-apks --bundle="./app-release.aab" --output="./AppVerifier-app-release.apks" --overwrite --ks-key-alias="key0" --ks="/Users/user/AppVerifierkeystore.jks" --ks-pass=pass:"" \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 52528ca8..13bcd662 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,14 +15,13 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:memtagMode="async" - android:theme="@style/Theme.appverifier" - tools:targetApi="34"> + android:theme="@style/Theme.AppVerifier" + tools:targetApi="33"> + android:documentLaunchMode="intoExisting"> diff --git a/app/src/main/kotlin/dev/soupslurpr/appverifier/AppVerifier.kt b/app/src/main/kotlin/dev/soupslurpr/appverifier/AppVerifier.kt index 4752dca1..90cc4fdf 100644 --- a/app/src/main/kotlin/dev/soupslurpr/appverifier/AppVerifier.kt +++ b/app/src/main/kotlin/dev/soupslurpr/appverifier/AppVerifier.kt @@ -4,6 +4,15 @@ import android.graphics.drawable.Drawable import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.StringRes +import androidx.compose.animation.AnimatedContentScope +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.SizeTransform +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideIn +import androidx.compose.animation.slideOut import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.padding @@ -20,9 +29,15 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.IntOffset +import androidx.navigation.NamedNavArgument +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavDeepLink +import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable +import androidx.navigation.compose.navigation import androidx.navigation.compose.rememberNavController import dev.soupslurpr.appverifier.data.Hashes import dev.soupslurpr.appverifier.data.InternalDatabaseInfo @@ -37,7 +52,6 @@ import dev.soupslurpr.appverifier.ui.StartupScreen import dev.soupslurpr.appverifier.ui.VerifyAppScreen import dev.soupslurpr.appverifier.ui.VerifyAppViewModel import kotlinx.coroutines.launch -import kotlin.random.Random enum class AppVerifierScreens(@StringRes val title: Int) { Start(title = R.string.app_name), @@ -80,14 +94,6 @@ fun AppVerifierApp( // backStackEntry?.destination?.route ?: AppVerifierScreens.Start.name // ) - val randomValue = Random.nextInt(0, 10) - val splashMessage = rememberSaveable { - when (randomValue) { - 0 -> "Gotta verify 'em all!" - else -> "App verification, but easy." - } - } - val context = LocalContext.current val openApkFileLauncher = @@ -122,22 +128,20 @@ fun AppVerifierApp( AppVerifierScreens.Start.name }, modifier = modifier.padding( - innerPadding.calculateStartPadding(LayoutDirection.Ltr), + innerPadding.calculateStartPadding(LocalLayoutDirection.current), innerPadding.calculateTopPadding(), - innerPadding.calculateEndPadding(LayoutDirection.Ltr) + innerPadding.calculateEndPadding(LocalLayoutDirection.current) ), ) { - composable(route = AppVerifierScreens.Start.name) { + composableWithDefaultSlideTransitions(route = AppVerifierScreens.Start) { StartupScreen( modifier = modifier, - splashMessage = splashMessage, onSettingsButtonClicked = { navController.navigate(AppVerifierScreens.Settings.name) }, onAppListButtonClicked = { navController.navigate(AppVerifierScreens.AppList.name) }, - verifyAppViewModel = verifyAppViewModel, onVerifyApkFileButtonClicked = { openApkFileLauncher.launch(arrayOf("application/vnd.android.package-archive")) }, @@ -149,7 +153,7 @@ fun AppVerifierApp( } ) } - composable(route = AppVerifierScreens.AppList.name) { + composableWithDefaultSlideTransitions(route = AppVerifierScreens.AppList) { AppListScreen( searchQuery, { name: String, packageName: String, hashes: Hashes, icon: Drawable, internalDatabaseInfo: @@ -171,7 +175,7 @@ fun AppVerifierApp( { verifyAppViewModel.getInternalDatabaseInfoFromVerificationInfo(it) } ) } - composable(route = AppVerifierScreens.VerifyApp.name) { + composableWithDefaultSlideTransitions(route = AppVerifierScreens.VerifyApp) { VerifyAppScreen( verifyAppUiState.value.icon.value, verifyAppUiState.value.name.value, @@ -191,7 +195,7 @@ fun AppVerifierApp( } ) } - composable(route = AppVerifierScreens.Settings.name) { + composableWithDefaultSlideTransitions(route = AppVerifierScreens.Settings) { SettingsScreen( onLicenseIconButtonClicked = { navController.navigate(AppVerifierScreens.License.name) @@ -208,21 +212,144 @@ fun AppVerifierApp( } ) } - composable(route = AppVerifierScreens.License.name) { + composableWithDefaultSlideTransitions(route = AppVerifierScreens.License) { LicenseScreen() } - composable(route = AppVerifierScreens.PrivacyPolicy.name) { + composableWithDefaultSlideTransitions(route = AppVerifierScreens.PrivacyPolicy) { PrivacyPolicyScreen() } - composable(route = AppVerifierScreens.Credits.name) { + composableWithDefaultSlideTransitions(route = AppVerifierScreens.Credits) { CreditsScreen() } - composable(route = AppVerifierScreens.Donation.name) { + composableWithDefaultSlideTransitions(route = AppVerifierScreens.Donation) { DonationScreen() } } } } +fun getStateDestinationRoute(state: NavBackStackEntry): AppVerifierScreens? { + state.destination.route?.let { return AppVerifierScreens.valueOf(it) } + return null +} +fun getEnterTransition( + initialState: NavBackStackEntry, + targetState: NavBackStackEntry, +): EnterTransition { + val initialNavBarRoute = getStateDestinationRoute(initialState) + val targetNavBarRoute = getStateDestinationRoute(targetState) + + return if ((initialNavBarRoute != null) && (targetNavBarRoute != null)) { + slideIn { + IntOffset( + if (initialNavBarRoute.ordinal > targetNavBarRoute.ordinal) { + -it.width + } else { + it.width + }, 0 + ) + } + fadeIn() + } else { + EnterTransition.None + } +} + +fun getExitTransition( + initialState: NavBackStackEntry, + targetState: NavBackStackEntry, +): ExitTransition { + val initialNavBarRoute = getStateDestinationRoute(initialState) + val targetNavBarRoute = getStateDestinationRoute(targetState) + + return if ((initialNavBarRoute != null) && (targetNavBarRoute != null)) { + slideOut { + IntOffset( + if (initialNavBarRoute.ordinal > targetNavBarRoute.ordinal) { + it.width + } else { + -it.width + }, 0 + ) + } + fadeOut() + } else { + ExitTransition.None + } +} +fun NavGraphBuilder.composableWithDefaultSlideTransitions( + route: AppVerifierScreens, + arguments: List = emptyList(), + deepLinks: List = emptyList(), + enterTransition: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> EnterTransition?)? = null, + exitTransition: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> ExitTransition?)? = null, + popEnterTransition: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> EnterTransition?)? = enterTransition, + popExitTransition: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> ExitTransition?)? = exitTransition, + sizeTransform: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> SizeTransform?)? = null, + content: @Composable (AnimatedContentScope.(NavBackStackEntry) -> Unit), +) { + composable(route.name, arguments, deepLinks, if (enterTransition == null) { + { + getEnterTransition(initialState, targetState) + } + } else { + null + }, if (exitTransition == null) { + { + getExitTransition(initialState, targetState) + } + } else { + null + }, if (popEnterTransition == null) { + { + getEnterTransition(initialState, targetState) + } + } else { + null + }, if (popExitTransition == null) { + { + getExitTransition(initialState, targetState) + } + } else { + null + }, sizeTransform, content) +} + +fun NavGraphBuilder.navigationWithDefaultSlideTransitions( + startDestination: String, + route: AppVerifierScreens, + arguments: List = emptyList(), + deepLinks: List = emptyList(), + enterTransition: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> EnterTransition?)? = null, + exitTransition: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> ExitTransition?)? = null, + popEnterTransition: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> EnterTransition?)? = enterTransition, + popExitTransition: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> ExitTransition?)? = exitTransition, + sizeTransform: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> SizeTransform?)? = null, + builder: NavGraphBuilder.() -> Unit, +) { + navigation(startDestination, route.name, arguments, deepLinks, if (enterTransition == null) { + { + getEnterTransition(initialState, targetState) + } + } else { + null + }, if (exitTransition == null) { + { + getExitTransition(initialState, targetState) + } + } else { + null + }, if (popEnterTransition == null) { + { + getEnterTransition(initialState, targetState) + } + } else { + null + }, if (popExitTransition == null) { + { + getExitTransition(initialState, targetState) + } + } else { + null + }, sizeTransform, builder) +} \ No newline at end of file diff --git a/app/src/main/kotlin/dev/soupslurpr/appverifier/InternalVerificationInfoDatabase.kt b/app/src/main/kotlin/dev/soupslurpr/appverifier/InternalVerificationInfoDatabase.kt index fcf53b75..a21a344e 100644 --- a/app/src/main/kotlin/dev/soupslurpr/appverifier/InternalVerificationInfoDatabase.kt +++ b/app/src/main/kotlin/dev/soupslurpr/appverifier/InternalVerificationInfoDatabase.kt @@ -113,7 +113,8 @@ val internalVerificationInfoDatabase = setOf( Source.GOOGLE_PLAY_STORE ), listOf( - "29:F3:4E:5F:27:F2:11:B4:24:BC:5B:F9:D6:71:62:C0:EA:FB:A2:DA:35:AF:35:C1:64:16:FC:44:62:76:BA:26" + "29:F3:4E:5F:27:F2:11:B4:24:BC:5B:F9:D6:71:62:C0:EA:FB:A2:DA:35:AF:35:C1:64:16:FC:44:62:76:BA:26", + "4B:E4:F6:CD:5B:E8:44:08:3E:90:02:79:DC:82:2A:F6:5A:54:7F:EC:C2:6A:BA:7F:F1:F5:20:3A:45:51:8C:D8" ), false ) diff --git a/app/src/main/kotlin/dev/soupslurpr/appverifier/ui/StartupScreen.kt b/app/src/main/kotlin/dev/soupslurpr/appverifier/ui/StartupScreen.kt index c569854e..ef7853f0 100644 --- a/app/src/main/kotlin/dev/soupslurpr/appverifier/ui/StartupScreen.kt +++ b/app/src/main/kotlin/dev/soupslurpr/appverifier/ui/StartupScreen.kt @@ -19,8 +19,8 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.List import androidx.compose.material.icons.filled.FileOpen -import androidx.compose.material.icons.filled.List import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon @@ -34,17 +34,14 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import dev.soupslurpr.appverifier.R @Composable fun StartupScreen( modifier: Modifier, - splashMessage: String, onSettingsButtonClicked: () -> Unit, onAppListButtonClicked: () -> Unit, - verifyAppViewModel: VerifyAppViewModel, onVerifyApkFileButtonClicked: () -> Unit, onLaunchedEffect: () -> Unit, ) { @@ -74,20 +71,15 @@ fun StartupScreen( ) } Text( - text = stringResource(R.string.welcome), + text = stringResource(R.string.app_name), style = typography.headlineLarge ) - Text( - text = splashMessage, - style = typography.bodySmall, - textAlign = TextAlign.Center - ) FilledTonalButton( modifier = modifier.fillMaxWidth(), onClick = { onAppListButtonClicked() } ) { Icon( - imageVector = Icons.Filled.List, + imageVector = Icons.AutoMirrored.Filled.List, contentDescription = null ) Spacer(modifier = modifier.width(8.dp)) diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index ac94b34f..00000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap/ic_launcher.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to app/src/main/res/mipmap/ic_launcher.xml diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 25bfa8e6..5e3e0d39 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,16 +1,5 @@ + - - + +