diff --git a/.idea/runConfigurations/ui_integration_test_connectedCheck.xml b/.idea/runConfigurations/ui_integration_test_connectedCheck.xml new file mode 100644 index 00000000..7155d06a --- /dev/null +++ b/.idea/runConfigurations/ui_integration_test_connectedCheck.xml @@ -0,0 +1,55 @@ + + + + + \ No newline at end of file diff --git a/docs/testing/manual_testing/QR Scan.md b/docs/testing/manual_testing/QR Scan.md index 3ba7a52f..6eeeb706 100644 --- a/docs/testing/manual_testing/QR Scan.md +++ b/docs/testing/manual_testing/QR Scan.md @@ -1,48 +1,61 @@ Note: To be able to fully test the QR Scan screen and its logic, we recommend you to test it on real Android devices, which have hardware camera accessories. Although there is a way on how to test the scanning functionalities on an Android emulator too. See the next section on how to set up a QR code for the emulated camera scene. # Test Prerequisites +- Prepare at least two Zcash wallet addresses - one valid and one invalid. A valid address for Testnet can be `tmEjY6KfCryQhJ1hKSGiA7p8EeVggpvN78r`. The invalid one can be its modification. - Check you have a wallet configured in the Zcash wallet app - Open Zcash wallet app in the system Settings app. Visit the permissions screen, then select Camera and make sure that you have the Camera permission denied. -- Prepare at least two Zcash wallet addresses - one valid and one invalid. A valid address for Testnet can be `tmEjY6KfCryQhJ1hKSGiA7p8EeVggpvN78r`. The invalid one can be its modification. +- The previous step of resetting the Camera permission needs to be repeated before each following subtests by one of these ways: + - Reset the wallet app data from system Settings app to reset the Camera permission + - Reinstall the wallet app + - Change the Camera permission settings by switching between available options (the options may differ on different Android SDK versions). # Android emulator setup (optional) This section is optional and is required only if you'd like to test on an Android emulator device. 1. Follow these [steps](https://developer.android.com/studio/install) to download and install Android studio -2. And these [steps](https://developer.android.com/studio/run/managing-avds#createavd) help you set up an Android emulator -3. Then you'll need to create a valid Zcash address QR code image. It can be done, for example, with the [QR Code Generator](https://www.qr-code-generator.com/) tool. -4. Download the image -5. Start the emulator -6. Click on More in the emulator panel to open the Extended controls window -7. Click on Camera -8. Click Add Image in the Wall section -9. Select your QR code image and close the window -10. Once you're in the QR Scan screen with the virtual camera opened, see these [instructions](https://developers.google.com/ar/develop/java/emulator#control_the_virtual_scene) on how to move in virtual scene. -11. The last step is to let the scanner read your QR code to be able to move to the smaller room (behind the dog), which will have the code displayed on the room wall. +1. And these [steps](https://developer.android.com/studio/run/managing-avds#createavd) help you set up an Android emulator +1. Then you'll need to create a valid Zcash address QR code image. It can be done, for example, with the [QR Code Generator](https://www.qr-code-generator.com/) tool. +1. Download the image +1. Start the emulator +1. Click on More in the emulator panel to open the Extended controls window +1. Click on Camera +1. Click Add Image in the Wall section +1. Select your QR code image and close the window +1. Once you're in the QR Scan screen with the virtual camera opened, see these [instructions](https://developers.google.com/ar/develop/java/emulator#control_the_virtual_scene) on how to move in virtual scene. +1. The last step is to let the scanner read your QR code to be able to move to the smaller room (behind the dog), which will have the code displayed on the room wall. # Check Camera permission allow functionality 1. Open QR Scan screen by QR code icon from the Home screen -2. Camera permission dialog should be prompt -3. Grant camera permission with Allow button (or its modifications) -4. Camera view and its square frame should appear +1. Camera permission dialog should be prompt +1. Grant camera permission with Allow button (or its modifications) +1. Camera view and its square frame should appear # Check Camera permission deny functionality 1. Open QR Scan screen by QR code icon from the Home screen -2. Camera permission dialog should be prompt -3. Deny camera permission with Deny button (or its modifications) -4. Camera view and its square frame shouldn't be visible. The screen should be black. Also, Open system Settings app button on the bottom side of the screen should be visible now. -5. Hit the button. -6. System Settings app should open on the Zcash wallet app screen. +1. Camera permission dialog should be prompt +1. Deny camera permission with Deny button (or its modifications) +1. Camera view and its square frame shouldn't be visible. The screen should be black. Also, Open system Settings app button on the bottom side of the screen should be visible now. +1. Hit the button. +1. System Settings app should open on the Zcash wallet app screen. + +# Check Camera permission dismiss functionality +1. Open QR Scan screen by QR code icon from the Home screen +1. Camera permission dialog should be prompt +1. Dismiss the Camera permission by back navigation or by touching outside the Camera permission popup +1. Rotate the device to trigger a configuration change (make sure the screen rotation is enabled on the device) +1. Ensure that the Camera permission is shown again +1. Leave the QR Scan screen by back navigation; you're on Home screen now +1. Ensure that Camera permission is not shown above the Home screen # Scan valid QR code 1. Grant the Camera permission with one of the previous procedures -2. Create QR code from the valid Zcash wallet address. You can use, for example, the [QR Code Generator](https://www.qr-code-generator.com/) tool. -3. Scan the created QR code -4. The code should be scanned and validated -5. App should then close the QR Scan screen and navigate to another screen to proceed with the scanned result +1. Create QR code from the valid Zcash wallet address. You can use, for example, the [QR Code Generator](https://www.qr-code-generator.com/) tool. +1. Scan the created QR code +1. The code should be scanned and validated +1. App should then close the QR Scan screen and navigate to another screen to proceed with the scanned result # Scan invalid QR code 1. Grant the Camera permission with one of the previous procedures -2. Create QR code from the valid Zcash wallet address. You can use, for example, the [QR Code Generator](https://www.qr-code-generator.com/) tool. -3. Scan the created QR code -4. The code should be scanned but not validated -5. The app UI should not be changed and the Camera view should be still available for scanning another codes \ No newline at end of file +1. Create QR code from the valid Zcash wallet address. You can use, for example, the [QR Code Generator](https://www.qr-code-generator.com/) tool. +1. Scan the created QR code +1. The code should be scanned but not validated +1. The app UI should not be changed and the Camera view should be still available for scanning another codes \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index d39d5753..fcc0687e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -104,24 +104,24 @@ JGIT_VERSION=6.1.0.202203080745-r KTLINT_VERSION=0.46.1 PLAY_PUBLISHER_PLUGIN_VERSION=3.7.0 -ACCOMPANIST_PERMISSIONS_VERSION=0.23.1 -ANDROIDX_ACTIVITY_VERSION=1.5.0 -ANDROIDX_ANNOTATION_VERSION=1.4.0 -ANDROIDX_APPCOMPAT_VERSION=1.5.0 +ACCOMPANIST_PERMISSIONS_VERSION=0.25.1 +ANDROIDX_ACTIVITY_VERSION=1.6.0 +ANDROIDX_ANNOTATION_VERSION=1.5.0 +ANDROIDX_APPCOMPAT_VERSION=1.5.1 ANDROIDX_CAMERA_VERSION=1.1.0 -ANDROIDX_COMPOSE_COMPILER_VERSION=1.3.0-beta01 -ANDROIDX_COMPOSE_MATERIAL3_VERSION=1.0.0-alpha14 -ANDROIDX_COMPOSE_VERSION=1.2.0-rc03 +ANDROIDX_COMPOSE_COMPILER_VERSION=1.3.1 +ANDROIDX_COMPOSE_MATERIAL3_VERSION=1.0.0-beta03 +ANDROIDX_COMPOSE_VERSION=1.2.1 ANDROIDX_CONSTRAINTLAYOUT_VERSION=1.0.1 -ANDROIDX_CORE_VERSION=1.8.0 +ANDROIDX_CORE_VERSION=1.9.0 ANDROIDX_ESPRESSO_VERSION=3.5.0-alpha07 -ANDROIDX_LIFECYCLE_VERSION=2.5.0 -ANDROIDX_NAVIGATION_COMPOSE_VERSION=2.5.0 +ANDROIDX_LIFECYCLE_VERSION=2.5.1 +ANDROIDX_NAVIGATION_COMPOSE_VERSION=2.5.2 ANDROIDX_SECURITY_CRYPTO_VERSION=1.1.0-alpha03 ANDROIDX_SPLASH_SCREEN_VERSION=1.0.0 ANDROIDX_TEST_JUNIT_VERSION=1.1.4-alpha07 ANDROIDX_TEST_ORCHESTRATOR_VERSION=1.4.2-alpha04 -ANDROIDX_TEST_CORE_VERSION=1.5.0-alpha01 +ANDROIDX_TEST_CORE_VERSION=1.5.0-alpha02 ANDROIDX_TEST_RUNNER_VERSION=1.5.0-alpha04 ANDROIDX_UI_AUTOMATOR_VERSION=2.2.0-alpha1 ANDROIDX_WORK_MANAGER_VERSION=2.7.1 diff --git a/settings.gradle.kts b/settings.gradle.kts index 4034b67f..d43dba9b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -118,7 +118,7 @@ dependencyResolutionManagement { val androidxComposeCompilerVersion = extra["ANDROIDX_COMPOSE_COMPILER_VERSION"].toString() val androidxComposeMaterial3Version = extra["ANDROIDX_COMPOSE_MATERIAL3_VERSION"].toString() val androidxComposeVersion = extra["ANDROIDX_COMPOSE_VERSION"].toString() - val androidxConstraintlayoutVersion = extra["ANDROIDX_CONSTRAINTLAYOUT_VERSION"].toString() + val androidxConstraintLayoutVersion = extra["ANDROIDX_CONSTRAINTLAYOUT_VERSION"].toString() val androidxCoreVersion = extra["ANDROIDX_CORE_VERSION"].toString() val androidxEspressoVersion = extra["ANDROIDX_ESPRESSO_VERSION"].toString() val androidxLifecycleVersion = extra["ANDROIDX_LIFECYCLE_VERSION"].toString() @@ -167,7 +167,7 @@ dependencyResolutionManagement { library("androidx-compose-ui-fonts", "androidx.compose.ui:ui-text-google-fonts:$androidxComposeVersion") library("androidx-compose-compiler", "androidx.compose.compiler:compiler:$androidxComposeCompilerVersion") library("androidx-core", "androidx.core:core-ktx:$androidxCoreVersion") - library("androidx-constraintlayout", "androidx.constraintlayout:constraintlayout-compose:$androidxConstraintlayoutVersion") + library("androidx-constraintlayout", "androidx.constraintlayout:constraintlayout-compose:$androidxConstraintLayoutVersion") library("androidx-lifecycle-livedata", "androidx.lifecycle:lifecycle-livedata-ktx:$androidxLifecycleVersion") library("androidx-navigation-compose", "androidx.navigation:navigation-compose:$androidxNavigationComposeVersion") library("androidx-security-crypto", "androidx.security:security-crypto-ktx:$androidxSecurityCryptoVersion") diff --git a/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/common/Global.kt b/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/common/Global.kt index 3d5c4ce4..003e622f 100644 --- a/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/common/Global.kt +++ b/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/common/Global.kt @@ -1,4 +1,4 @@ -package co.electriccoin.zcash.ui.integration.test +package co.electriccoin.zcash.ui.integration.test.common import android.content.Context import android.os.Build @@ -50,10 +50,10 @@ fun getPermissionPositiveButtonUiObject(): UiObject? { ) } -fun waitForDeviceIdle(timeoutMillis: Duration = 1000.milliseconds) { +fun waitForDeviceIdle(timeout: Duration = 1000.milliseconds) { val instrumentation = InstrumentationRegistry.getInstrumentation() UiDevice.getInstance(instrumentation).waitForWindowUpdate( ApplicationProvider.getApplicationContext().packageName, - timeoutMillis.inWholeMilliseconds + timeout.inWholeMilliseconds ) } diff --git a/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewIntegrationTest.kt b/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewIntegrationTest.kt index 0e4625df..eb29871d 100644 --- a/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewIntegrationTest.kt +++ b/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewIntegrationTest.kt @@ -7,7 +7,7 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.test.filters.LargeTest import co.electriccoin.zcash.test.UiTestPrerequisites import co.electriccoin.zcash.ui.integration.test.common.IntegrationTestingActivity -import co.electriccoin.zcash.ui.integration.test.getPermissionPositiveButtonUiObject +import co.electriccoin.zcash.ui.integration.test.common.getPermissionPositiveButtonUiObject import co.electriccoin.zcash.ui.screen.scan.ScanTag import co.electriccoin.zcash.ui.screen.scan.model.ScanState import org.junit.Assert diff --git a/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTest.kt b/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTest.kt index e00c0737..17d0cf31 100644 --- a/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTest.kt +++ b/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTest.kt @@ -12,9 +12,9 @@ import androidx.test.filters.LargeTest import co.electriccoin.zcash.test.UiTestPrerequisites import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.integration.test.common.IntegrationTestingActivity -import co.electriccoin.zcash.ui.integration.test.getPermissionPositiveButtonUiObject -import co.electriccoin.zcash.ui.integration.test.getStringResource -import co.electriccoin.zcash.ui.integration.test.waitForDeviceIdle +import co.electriccoin.zcash.ui.integration.test.common.getPermissionPositiveButtonUiObject +import co.electriccoin.zcash.ui.integration.test.common.getStringResource +import co.electriccoin.zcash.ui.integration.test.common.waitForDeviceIdle import co.electriccoin.zcash.ui.screen.scan.ScanTag import co.electriccoin.zcash.ui.screen.scan.model.ScanState import org.junit.Assert.assertEquals @@ -93,7 +93,7 @@ class ScanViewTest : UiTestPrerequisites() { assertEquals(ScanState.Scanning, testSetup.getScanState()) // we need to actively wait for the camera preview initialization - waitForDeviceIdle(timeoutMillis = 5000.milliseconds) + waitForDeviceIdle(timeout = 5000.milliseconds) composeTestRule.onNodeWithTag(ScanTag.CAMERA_VIEW).also { it.assertIsDisplayed() diff --git a/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTestSetup.kt b/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTestSetup.kt index 35454161..9e49f40b 100644 --- a/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTestSetup.kt +++ b/ui-integration-test-lib/src/androidTest/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTestSetup.kt @@ -4,8 +4,8 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.ui.test.junit4.ComposeContentTestRule import co.electriccoin.zcash.ui.design.theme.ZcashTheme -import co.electriccoin.zcash.ui.integration.test.getPermissionNegativeButtonUiObject -import co.electriccoin.zcash.ui.integration.test.getPermissionPositiveButtonUiObject +import co.electriccoin.zcash.ui.integration.test.common.getPermissionNegativeButtonUiObject +import co.electriccoin.zcash.ui.integration.test.common.getPermissionPositiveButtonUiObject import co.electriccoin.zcash.ui.screen.scan.model.ScanState import co.electriccoin.zcash.ui.screen.scan.view.Scan import org.junit.Assert.assertNotNull diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/view/AboutView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/view/AboutView.kt index 0bb496b0..dd76a234 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/view/AboutView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/view/AboutView.kt @@ -13,8 +13,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource @@ -57,8 +57,9 @@ fun About( } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun AboutTopAppBar(onBack: () -> Unit) { - SmallTopAppBar( + TopAppBar( title = { Text(text = stringResource(id = R.string.about_title)) }, navigationIcon = { IconButton( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressView.kt index a011329e..208641cc 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressView.kt @@ -19,11 +19,12 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowDropDownCircle import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -70,8 +71,9 @@ fun WalletAddresses(walletAddresses: WalletAddresses, onBack: () -> Unit) { } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun WalletDetailTopAppBar(onBack: () -> Unit) { - SmallTopAppBar( + TopAppBar( title = { Text( text = stringResource(id = R.string.wallet_address_title) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt index 05bb9a24..0e11eb53 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt @@ -26,8 +26,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -113,12 +113,13 @@ fun Home( } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun HomeTopAppBar( isDebugMenuEnabled: Boolean, resetSdk: () -> Unit, wipeEntireWallet: () -> Unit ) { - SmallTopAppBar( + TopAppBar( title = { Text(text = stringResource(id = R.string.app_name)) }, actions = { if (isDebugMenuEnabled) { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingView.kt index 9b9b3a08..d45f5468 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingView.kt @@ -23,8 +23,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -100,6 +100,7 @@ fun Onboarding( } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun OnboardingTopAppBar( onboardingState: OnboardingState, isDebugMenuEnabled: Boolean, @@ -107,7 +108,7 @@ private fun OnboardingTopAppBar( ) { val currentStage = onboardingState.current.collectAsState().value - SmallTopAppBar( + TopAppBar( title = { Text(text = stringResource(id = R.string.app_name)) }, navigationIcon = if (IS_NAVIGATION_IN_APP_BAR && currentStage.hasPrevious()) { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/profile/view/ProfileView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/profile/view/ProfileView.kt index 92059728..3a4aba43 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/profile/view/ProfileView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/profile/view/ProfileView.kt @@ -7,11 +7,12 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -83,8 +84,9 @@ fun Profile( } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun ProfileTopAppBar(onBack: () -> Unit) { - SmallTopAppBar( + TopAppBar( title = { Text(text = stringResource(id = R.string.profile_title)) }, navigationIcon = { IconButton( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/view/RequestView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/view/RequestView.kt index 47c35187..ce768658 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/view/RequestView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/view/RequestView.kt @@ -13,9 +13,9 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -77,8 +77,9 @@ fun Request( } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun RequestTopAppBar(onBack: () -> Unit) { - SmallTopAppBar( + TopAppBar( title = { Text(text = stringResource(id = R.string.request_title)) }, navigationIcon = { IconButton( @@ -97,6 +98,7 @@ private fun RequestTopAppBar(onBack: () -> Unit) { // TODO [#217]: Need to handle changing of Locale after user input, but before submitting the button. // TODO [#288]: TextField component can't do long-press backspace. @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun RequestMainContent( paddingValues: PaddingValues, myAddress: WalletAddress.Unified, diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreView.kt index 31009630..f1788b31 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreView.kt @@ -25,9 +25,9 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState @@ -137,8 +137,9 @@ fun RestoreWallet( } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun RestoreTopAppBar(onBack: () -> Unit, onClear: () -> Unit) { - SmallTopAppBar( + TopAppBar( title = { Text(text = stringResource(id = R.string.restore_header)) }, navigationIcon = { IconButton( @@ -269,6 +270,7 @@ private fun ChipGridWithText( // TODO [#288]: TextField component can't do long-press backspace. @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun NextWordTextField(modifier: Modifier = Modifier, text: String, setText: (String) -> Unit) { /* * Treat the user input as a password, but disable the transformation to obscure input. diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/view/ScanView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/view/ScanView.kt index 9fabd51e..06d3a5a2 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/view/ScanView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/view/ScanView.kt @@ -25,11 +25,11 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -65,16 +65,18 @@ import co.electriccoin.zcash.ui.screen.scan.model.ScanState import co.electriccoin.zcash.ui.screen.scan.util.QrCodeAnalyzer import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.PermissionState +import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState +import com.google.accompanist.permissions.shouldShowRationale import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.guava.await -import java.util.UUID import kotlin.math.roundToInt // TODO [#423]: https://github.com/zcash/secant-android-wallet/issues/423 +// TODO QR scan screen elements transparency @Preview("Scan") @Composable fun PreviewScan() { @@ -159,8 +161,9 @@ fun ScanBottomItems( } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun ScanTopAppBar(onBack: () -> Unit) { - SmallTopAppBar( + TopAppBar( title = { Text(text = stringResource(id = R.string.scan_header)) }, navigationIcon = { IconButton( @@ -194,7 +197,7 @@ private fun ScanMainContent( val (scanState, setScanState) = rememberSaveable { mutableStateOf( - if (permissionState.hasPermission) { + if (permissionState.status.isGranted) { ScanState.Scanning } else { ScanState.Permission @@ -202,14 +205,19 @@ private fun ScanMainContent( ) } - if (!permissionState.hasPermission) { + if (!permissionState.status.isGranted) { setScanState(ScanState.Permission) - LaunchedEffect(key1 = UUID.randomUUID()) { - permissionState.launchPermissionRequest() + if (permissionState.status.shouldShowRationale) { + // keep blank screen with a link to the app settings + // user denied the permission previously + } else { + LaunchedEffect(key1 = true) { + permissionState.launchPermissionRequest() + } } } else if (scanState == ScanState.Failed) { // keep current state - } else if (permissionState.hasPermission) { + } else if (permissionState.status.isGranted) { if (scanState != ScanState.Scanning) { setScanState(ScanState.Scanning) } @@ -240,6 +248,7 @@ private fun ScanMainContent( } ScanState.Scanning -> { // TODO [#437]: https://github.com/zcash/secant-android-wallet/issues/437 + // TODO Scan QR Screen Frame Analysing onScanStateChanged(ScanState.Scanning) ScanCameraView( onScanned = onScanned, @@ -312,7 +321,7 @@ fun ScanCameraView( // we check the permission first, as the ProcessCameraProvider's emit won't be called again after // recomposition with the permission granted - val cameraProviderFlow = if (permissionState.hasPermission) { + val cameraProviderFlow = if (permissionState.status.isGranted) { remember { flow { emit(ProcessCameraProvider.getInstance(context).await()) } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seed/view/SeedView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seed/view/SeedView.kt index bf999a50..aeb71c3a 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seed/view/SeedView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seed/view/SeedView.kt @@ -11,8 +11,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -64,8 +64,9 @@ fun Seed( } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun SeedTopAppBar(onBack: () -> Unit) { - SmallTopAppBar( + TopAppBar( title = { Text(text = stringResource(id = R.string.seed_title)) }, navigationIcon = { IconButton( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt index ad6a8a09..5f494ee8 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt @@ -16,9 +16,9 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -96,8 +96,9 @@ fun Send( } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun SendTopAppBar(onBack: () -> Unit) { - SmallTopAppBar( + TopAppBar( title = { Text(text = stringResource(id = R.string.send_title)) }, navigationIcon = { IconButton( @@ -146,6 +147,7 @@ private fun SendMainContent( // TODO [#294]: DetektAll failed LongMethod @Suppress("LongMethod") @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun SendForm( paddingValues: PaddingValues, myBalance: Zatoshi, diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsView.kt index f43d1ebf..be57730c 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsView.kt @@ -9,8 +9,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -57,8 +57,9 @@ fun Settings( } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun SettingsTopAppBar(onBack: () -> Unit) { - SmallTopAppBar( + TopAppBar( title = { Text(text = stringResource(id = R.string.settings_header)) }, navigationIcon = { IconButton( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/view/SupportView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/view/SupportView.kt index d7308baf..b75e1ec3 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/view/SupportView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/view/SupportView.kt @@ -14,11 +14,11 @@ import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable @@ -83,8 +83,9 @@ fun Support( } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun SupportTopAppBar(onBack: () -> Unit) { - SmallTopAppBar( + TopAppBar( title = { Text(text = stringResource(id = R.string.support_header)) }, navigationIcon = { IconButton( @@ -100,6 +101,7 @@ private fun SupportTopAppBar(onBack: () -> Unit) { } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun SupportMainContent( paddingValues: PaddingValues, message: String, diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/view/UpdateView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/view/UpdateView.kt index 1ed67ff9..8300159e 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/view/UpdateView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/view/UpdateView.kt @@ -14,10 +14,10 @@ import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -111,8 +111,9 @@ fun UpdateOverlayRunning(updateInfo: UpdateInfo) { } @Composable +@OptIn(ExperimentalMaterial3Api::class) private fun UpdateTopAppBar(updateInfo: UpdateInfo) { - SmallTopAppBar( + TopAppBar( title = { Text( text = stringResource(