diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index c86ee021ae..3308758ac9 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -47,6 +47,7 @@ androidx-test-junit = "1.1.5" androidx-test-monitor = "1.6.1" androidx-test-rules = "1.5.0" androidx-test-runner = "1.5.0" +androidx-window = "1.3.0" androidx-work-runtimeKtx = "2.10.0" anrwatchdog = "1.4.0" appsflyer = "6.16.0" @@ -236,6 +237,7 @@ androidx-test-runner = { module = "androidx.test:runner", version.ref = "android androidx-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } androidx-ui-viewbinding = { module = "androidx.compose.ui:ui-viewbinding", version.ref = "uiViewbinding" } +androidx-window = { module = "androidx.window:window", version.ref = "androidx-window" } androidx-work-runtimeKtx = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work-runtimeKtx" } anrwatchdog = { module = "com.github.anrwatchdog:anrwatchdog", version.ref = "anrwatchdog" } diff --git a/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt b/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt index fba85afff2..739368232d 100644 --- a/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt +++ b/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt @@ -139,6 +139,7 @@ object FirebaseRemoteConfigHelper { const val NAVI_PAY_SEND_MONEY_PRE_VALIDATION_ENABLED = "NAVI_PAY_SEND_MONEY_PRE_VALIDATION_ENABLED" const val NAVI_PAY_AUTO_READ_OTP_DISABLED = "NAVI_PAY_AUTO_READ_OTP_DISABLED" + const val NAVI_PAY_SET_AS_WALLPAPER_ENABLED = "NAVI_PAY_SET_AS_WALLPAPER_ENABLED" // COMMON const val LITMUS_EXPERIMENTS_CACHE_DURATION_IN_MILLIS = diff --git a/android/navi-pay/build.gradle b/android/navi-pay/build.gradle index 075bb62e28..9218d5809a 100644 --- a/android/navi-pay/build.gradle +++ b/android/navi-pay/build.gradle @@ -97,6 +97,7 @@ dependencies { implementation libs.androidx.paging.compose implementation libs.androidx.room.paging implementation libs.androidx.sqlite + implementation libs.androidx.window implementation libs.dagger.hiltAndroid implementation libs.kotlinx.serialization.json implementation libs.mlkit.barcodeScanning diff --git a/android/navi-pay/src/main/AndroidManifest.xml b/android/navi-pay/src/main/AndroidManifest.xml index 85c06f9d8b..973326b390 100644 --- a/android/navi-pay/src/main/AndroidManifest.xml +++ b/android/navi-pay/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ + diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/analytics/NaviPayAnalytics.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/analytics/NaviPayAnalytics.kt index 2ae71df2c0..afd5d12310 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/analytics/NaviPayAnalytics.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/analytics/NaviPayAnalytics.kt @@ -5164,6 +5164,10 @@ class NaviPayAnalytics private constructor() { ), ) } + + fun onSetAsWallpaperClicked() { + NaviTrackEvent.trackEventOnClickStream(eventName = "NaviPay_SetAsWallpaper_Clicked") + } } inner class NaviPayViewModel { diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/model/UpiSettingDetails.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/model/UpiSettingDetails.kt index 470361b41c..588b16ad55 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/model/UpiSettingDetails.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/model/UpiSettingDetails.kt @@ -49,6 +49,8 @@ enum class CardType { ADD_ACCOUNT, } +data class QrAsWallpaperStateHolder(val qrDetails: QrDetails?, val showSnackBar: Boolean) + sealed class SettingAction { data class Download(val qrDetails: QrDetails?) : SettingAction() diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/ui/UPISettingSDK.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/ui/UPISettingSDK.kt index 469ae00dc1..3a8ea13f97 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/ui/UPISettingSDK.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/ui/UPISettingSDK.kt @@ -12,15 +12,36 @@ import android.os.Bundle import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Snackbar +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.SnackbarResult import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver @@ -28,20 +49,31 @@ import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.navi.base.deeplink.DeepLinkManager import com.navi.base.model.CtaData +import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper +import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper.NAVI_PAY_SET_AS_WALLPAPER_ENABLED import com.navi.common.ui.compose.DrawerState import com.navi.common.utils.toCtaData +import com.navi.design.font.FontWeightEnum +import com.navi.design.font.getFontWeight +import com.navi.design.font.naviFontFamily +import com.navi.naviwidgets.extensions.NaviText +import com.navi.pay.R import com.navi.pay.analytics.NaviPayAnalytics import com.navi.pay.common.settingscreen.model.QrDetails import com.navi.pay.common.settingscreen.model.SettingAction import com.navi.pay.common.settingscreen.utils.downloadQR +import com.navi.pay.common.settingscreen.utils.getQRBitmapForWallpaper import com.navi.pay.common.settingscreen.utils.saveQrInGallery import com.navi.pay.common.settingscreen.utils.shareQR import com.navi.pay.common.settingscreen.viewmodel.SettingSDKViewmodel +import com.navi.pay.common.theme.color.NaviPayColor import com.navi.pay.common.utils.NaviPayCommonUtils import com.navi.pay.common.utils.launchOnboardingSDK import com.navi.pay.utils.ACCOUNT_ID import com.navi.pay.utils.ObserveAsEvents import com.navi.pay.utils.SAVINGS_ONLY_ENABLED_ACCOUNTS +import com.navi.pay.utils.noRippleClickable +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -99,6 +131,10 @@ fun UPISettingSDK( val naviPayAccessEligibility = remember { NaviPayCommonUtils.getNaviPayAccessEligibility(activity) } + + val qrAsWallpaperState by + settingSDKViewmodel.qrAsWallpaperStateHolder.collectAsStateWithLifecycle() + val galleryLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> @@ -150,6 +186,16 @@ fun UPISettingSDK( galleryLauncher = galleryLauncher, ) naviSettingSDKAnalytics.onDownloadQRClicked() + + if (FirebaseRemoteConfigHelper.getBoolean(NAVI_PAY_SET_AS_WALLPAPER_ENABLED)) { + settingSDKViewmodel.updateQrAsWallpaperState( + showSnackBar = true, + qrDetails = action.qrDetails, + ) + } else { + Toast.makeText(activity, "Downloaded successfully", Toast.LENGTH_SHORT) + .show() + } } is SettingAction.Copy -> { clipboardManager.setText(annotatedString = AnnotatedString(action.copy)) @@ -200,11 +246,118 @@ fun UPISettingSDK( } } } - UPISettingContent( - upiSettingDetails = upiSettingDetails, - onSelected = onSelected, - drawerState = drawerState, - qrPagerAnimationAvailable = qrPagerAnimationAvailable, - updateQrPagerAnimationCounter = settingSDKViewmodel::incrementQrPagerAnimationCounter, - ) + + Box { + UPISettingContent( + upiSettingDetails = upiSettingDetails, + onSelected = onSelected, + drawerState = drawerState, + qrPagerAnimationAvailable = qrPagerAnimationAvailable, + updateQrPagerAnimationCounter = settingSDKViewmodel::incrementQrPagerAnimationCounter, + ) + + if (qrAsWallpaperState.showSnackBar) { + SnackBarSection( + modifier = + Modifier.align(Alignment.TopCenter) + .padding(top = 640.dp) + .fillMaxWidth() + .padding(horizontal = 16.dp), + onDismissSnackBar = { + settingSDKViewmodel.updateQrAsWallpaperState(showSnackBar = false) + }, + onActionClicked = { + val wallpaperBitmap = + getQRBitmapForWallpaper( + activity = activity, + qrDetails = qrAsWallpaperState.qrDetails, + ) + + wallpaperBitmap?.let { + settingSDKViewmodel.setWallpaper( + bitmap = it, + setAsHomeScreen = true, + setAsLockScreen = false, + ) + } + + naviSettingSDKAnalytics.onSetAsWallpaperClicked() + settingSDKViewmodel.updateQrAsWallpaperState(showSnackBar = false) + }, + message = stringResource(R.string.np_qr_downloaded), + actionLabel = stringResource(R.string.np_set_as_wallpaper), + ) + } + } +} + +@Composable +private fun SnackBarSection( + modifier: Modifier = Modifier, + onDismissSnackBar: () -> Unit, + onActionClicked: () -> Unit, + message: String, + actionLabel: String, +) { + val snackState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + + SnackbarHost(modifier = modifier, hostState = snackState) { + Snackbar( + content = { + Row( + modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + painter = painterResource(id = R.drawable.navi_pay_ic_checked_circle_green), + contentDescription = null, + modifier = Modifier.size(14.dp), + ) + + Spacer(modifier = Modifier.width(8.dp)) + + NaviText( + text = message, + fontFamily = naviFontFamily, + fontSize = 12.sp, + lineHeight = 18.sp, + fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR), + color = NaviPayColor.textWhite, + ) + + Spacer(modifier = Modifier.weight(1f)) + + NaviText( + text = actionLabel, + fontFamily = naviFontFamily, + fontSize = 12.sp, + lineHeight = 18.sp, + fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR), + color = NaviPayColor.textWhite, + textDecoration = TextDecoration.Underline, + modifier = Modifier.noRippleClickable { onActionClicked.invoke() }, + ) + } + } + ) + } + + LaunchedEffect(Unit) { + scope.launch { + val snackBarResult = + snackState.showSnackbar( + message = message, + actionLabel = actionLabel, + duration = SnackbarDuration.Short, + ) + + when (snackBarResult) { + SnackbarResult.Dismissed -> onDismissSnackBar() + SnackbarResult.ActionPerformed -> { + onActionClicked() + } + } + } + } } diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/utils/SettingsSDKUtils.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/utils/SettingsSDKUtils.kt index da45edd2eb..df5e2d6cc3 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/utils/SettingsSDKUtils.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/utils/SettingsSDKUtils.kt @@ -11,14 +11,18 @@ import android.app.Activity import android.content.ContentValues import android.content.Intent import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color import android.net.Uri import android.os.Build import android.os.Environment import android.provider.MediaStore import android.view.LayoutInflater -import android.widget.Toast +import android.view.View import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.result.ActivityResult +import androidx.core.graphics.createBitmap +import androidx.window.layout.WindowMetricsCalculator import com.navi.base.model.ActionData import com.navi.common.screenshot.ShareBitmapImage import com.navi.common.upi.IS_ONBOARDING_REQUIRED @@ -62,7 +66,6 @@ fun downloadQR( outputStream.close() } } - Toast.makeText(activity, "Downloaded successfully", Toast.LENGTH_SHORT).show() NaviPayNotificationHandler.showNotification( context = activity.baseContext, @@ -116,6 +119,47 @@ private fun getQRBitmap(activity: Activity, qrDetails: QrDetails?): Bitmap? { } } +fun getQRBitmapForWallpaper(activity: Activity, qrDetails: QrDetails?): Bitmap? { + try { + + val windowMetrics = + WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity) + val screenWidth = windowMetrics.bounds.width() + val screenHeight = windowMetrics.bounds.height() + + val wallpaperBitmap = createBitmap(width = screenWidth, height = screenHeight) + val canvas = Canvas(wallpaperBitmap) + canvas.drawColor(Color.WHITE) + + val binding = LayoutShareQrBinding.inflate(LayoutInflater.from(activity)) + binding.nameTv.setText(qrDetails?.linkedAccountEntity?.name.orEmpty()) + binding.vpaTv.setText(qrDetails?.linkedAccountEntity?.primaryVpa.orEmpty()) + binding.vpaImage.setImageURI(qrDetails?.qrCodeUri) + val qrView = binding.root + + qrView.measure( + View.MeasureSpec.makeMeasureSpec(screenWidth, View.MeasureSpec.AT_MOST), + View.MeasureSpec.makeMeasureSpec(screenHeight, View.MeasureSpec.AT_MOST), + ) + qrView.layout(0, 0, qrView.measuredWidth, qrView.measuredHeight) + + val qrBitmap = createBitmap(width = qrView.measuredWidth, height = qrView.measuredHeight) + qrView.draw(Canvas(qrBitmap)) + + val left = (screenWidth - qrView.measuredWidth) / 2 + + val verticalOffset = screenHeight * 0.05f + val top = ((screenHeight - qrView.measuredHeight) / 2) - verticalOffset + + canvas.drawBitmap(qrBitmap, left.toFloat(), top, null) + + return wallpaperBitmap + } catch (e: Exception) { + e.printStackTrace() + return null + } +} + fun getFilteredLinkedAccounts( accountType: String, linkedAccounts: List, diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/utils/WallpaperManager.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/utils/WallpaperManager.kt new file mode 100644 index 0000000000..184539f5bd --- /dev/null +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/utils/WallpaperManager.kt @@ -0,0 +1,56 @@ +/* + * + * * Copyright © 2025 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.pay.common.settingscreen.utils + +import android.app.WallpaperManager as AndroidWallpaperManager +import android.content.Context +import android.graphics.Bitmap +import com.navi.common.utils.log +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject + +interface WallpaperManager { + fun setWallpaper(bitmap: Bitmap, setAsHomeScreen: Boolean, setAsLockScreen: Boolean): Boolean +} + +class WallpaperManagerImpl @Inject constructor(@ApplicationContext private val context: Context) : + WallpaperManager { + + private val androidWallpaperManagerInstance = AndroidWallpaperManager.getInstance(context) + + override fun setWallpaper( + bitmap: Bitmap, + setAsHomeScreen: Boolean, + setAsLockScreen: Boolean, + ): Boolean { + if ( + !androidWallpaperManagerInstance.isWallpaperSupported || + !androidWallpaperManagerInstance.isSetWallpaperAllowed + ) { + return false + } + + try { + + val flag = + when { + setAsHomeScreen && setAsLockScreen -> + AndroidWallpaperManager.FLAG_LOCK or AndroidWallpaperManager.FLAG_SYSTEM + setAsHomeScreen -> AndroidWallpaperManager.FLAG_SYSTEM + setAsLockScreen -> AndroidWallpaperManager.FLAG_LOCK + else -> AndroidWallpaperManager.FLAG_SYSTEM + } + + androidWallpaperManagerInstance.setBitmap(bitmap, null, true, flag) + return true + } catch (e: Exception) { + e.log() + return false + } + } +} diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/viewmodel/SettingSDKViewmodel.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/viewmodel/SettingSDKViewmodel.kt index ff2b65dc7d..db49106348 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/viewmodel/SettingSDKViewmodel.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/common/settingscreen/viewmodel/SettingSDKViewmodel.kt @@ -7,6 +7,7 @@ package com.navi.pay.common.settingscreen.viewmodel +import android.graphics.Bitmap import androidx.lifecycle.Lifecycle import androidx.lifecycle.viewModelScope import com.navi.base.cache.model.NaviCacheEntity @@ -18,7 +19,10 @@ import com.navi.pay.analytics.NaviPayAnalytics import com.navi.pay.analytics.NaviPayAnalytics.Companion.NAVI_PAY_UPI_SETTINGS_SDK import com.navi.pay.common.model.view.NaviPayFlowType import com.navi.pay.common.repository.SharedPreferenceRepository +import com.navi.pay.common.settingscreen.model.QrAsWallpaperStateHolder +import com.navi.pay.common.settingscreen.model.QrDetails import com.navi.pay.common.settingscreen.model.UpiSettingDetails +import com.navi.pay.common.settingscreen.utils.WallpaperManager import com.navi.pay.common.settingscreen.utils.isOnboardingRequired import com.navi.pay.common.setup.NaviPayManager import com.navi.pay.common.usecase.LinkedAccountsUseCase @@ -57,12 +61,13 @@ constructor( private val linkedAccountsUseCase: LinkedAccountsUseCase, private val naviPayPspManager: NaviPayPspManager, val naviPayOnboardingNavigator: NaviPayOnboardingNavigator, + private val wallpaperManager: WallpaperManager, ) : NaviPayBaseVM() { private val _upiSettingDetails = MutableStateFlow(UpiSettingDetails()) val upiSettingDetails = _upiSettingDetails.asStateFlow() - val naviSettingSDKAnalytics: NaviPayAnalytics.NaviSettingSDK = + private val naviSettingSDKAnalytics: NaviPayAnalytics.NaviSettingSDK = NaviPayAnalytics.INSTANCE.NaviSettingSDK() private val qrPagerAnimationCounter = getQrPagerAnimationCounter() @@ -84,6 +89,10 @@ constructor( private var pendingCollectRequestCount = 0 private var autoPayPendingRequestCount = 0 + private val _qrAsWallpaperStateHolder = + MutableStateFlow(QrAsWallpaperStateHolder(qrDetails = null, showSnackBar = false)) + val qrAsWallpaperStateHolder = _qrAsWallpaperStateHolder.asStateFlow() + init { viewModelScope.launch(Dispatchers.IO) { linkedAccountsUseCase.execute(screenName = screenName).collectLatest { linkedAccounts -> @@ -251,6 +260,16 @@ constructor( } } + fun updateQrAsWallpaperState(qrDetails: QrDetails? = null, showSnackBar: Boolean) { + _qrAsWallpaperStateHolder.update { + QrAsWallpaperStateHolder(qrDetails = qrDetails, showSnackBar = showSnackBar) + } + } + + fun setWallpaper(bitmap: Bitmap, setAsHomeScreen: Boolean, setAsLockScreen: Boolean): Boolean { + return wallpaperManager.setWallpaper(bitmap, setAsHomeScreen, setAsLockScreen) + } + override val screenName: String get() = NAVI_PAY_UPI_SETTINGS_SDK } diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/network/di/NaviPayModule.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/network/di/NaviPayModule.kt index a31e847365..75df1ca26c 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/network/di/NaviPayModule.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/network/di/NaviPayModule.kt @@ -22,6 +22,8 @@ import com.navi.pay.common.connectivity.NaviPayNetworkConnectivityImpl import com.navi.pay.common.magiclocation.util.NetworkInfoDataProvider import com.navi.pay.common.magiclocation.util.NetworkInfoDataProviderImpl import com.navi.pay.common.repository.SharedPreferenceRepository +import com.navi.pay.common.settingscreen.utils.WallpaperManager +import com.navi.pay.common.settingscreen.utils.WallpaperManagerImpl import com.navi.pay.common.setup.NaviPayManager import com.navi.pay.common.utils.DeviceInfoImpl import com.navi.pay.common.utils.DeviceInfoProvider @@ -252,6 +254,9 @@ abstract class NaviPayViewModelScopedModule { abstract fun bindNetworkInfoDataProvider( networkInfoDataProviderImpl: NetworkInfoDataProviderImpl ): NetworkInfoDataProvider + + @Binds + abstract fun bindWallpaperManager(wallpaperManagerImpl: WallpaperManagerImpl): WallpaperManager } @Module diff --git a/android/navi-pay/src/main/res/values/strings.xml b/android/navi-pay/src/main/res/values/strings.xml index 4fc0187ab8..a72805c573 100644 --- a/android/navi-pay/src/main/res/values/strings.xml +++ b/android/navi-pay/src/main/res/values/strings.xml @@ -851,4 +851,6 @@ Retry OTP validation Please logout and try logging in again. Ensure that the mobile number used for UPI registration is in your device and the OTP is auto-entered. Logout + QR downloaded + Set as wallpaper \ No newline at end of file