From 07ceec38a4afd4740cde7c7b366a7f8747e73523 Mon Sep 17 00:00:00 2001 From: Adam Wushensky Date: Thu, 26 May 2022 13:38:31 -0400 Subject: [PATCH 1/3] Prevent a crash when the fragment is detached --- .../cardscan/CardScanFragment.kt | 48 +++++++++++-------- .../framework/util/AppDetails.kt | 4 +- .../stripecardscan/framework/util/Device.kt | 28 ++++++----- .../stripecardscan/scanui/ScanFragment.kt | 8 ++-- 4 files changed, 49 insertions(+), 39 deletions(-) diff --git a/stripecardscan/src/main/java/com/stripe/android/stripecardscan/cardscan/CardScanFragment.kt b/stripecardscan/src/main/java/com/stripe/android/stripecardscan/cardscan/CardScanFragment.kt index d0232dc9618..f3ea60abb06 100644 --- a/stripecardscan/src/main/java/com/stripe/android/stripecardscan/cardscan/CardScanFragment.kt +++ b/stripecardscan/src/main/java/com/stripe/android/stripecardscan/cardscan/CardScanFragment.kt @@ -124,7 +124,7 @@ class CardScanFragment : ScanFragment(), SimpleScanStateful { ) { launch(Dispatchers.Main) { changeScanState(CardScanState.Correct) - cameraAdapter.unbindFromLifecycle(requireActivity()) + activity?.let { cameraAdapter.unbindFromLifecycle(it) } resultListener.cardScanComplete(ScannedCard(result.pan)) }.let { } } @@ -203,13 +203,13 @@ class CardScanFragment : ScanFragment(), SimpleScanStateful { * Set up viewFinderWindowView and viewFinderBorderView centered with predefined margins */ private fun setupViewFinderConstraints() { - val screenSize = Resources.getSystem().displayMetrics.let { Size(it.widthPixels, it.heightPixels) } + val viewFinderMargin = ( min(screenSize.width, screenSize.height) * - requireActivity().getFloatResource(R.dimen.stripeViewFinderMargin) + (context?.getFloatResource(R.dimen.stripeViewFinderMargin) ?: 0F) ).roundToInt() listOf(viewBinding.viewFinderWindow, viewBinding.viewFinderBorder).forEach { view -> @@ -241,14 +241,16 @@ class CardScanFragment : ScanFragment(), SimpleScanStateful { * Once the camera stream is available, start processing images. */ override suspend fun onCameraStreamAvailable(cameraStream: Flow>) { - scanFlow.startFlow( - context = requireActivity(), - imageStream = cameraStream, - viewFinder = viewBinding.viewFinderWindow.asRect(), - lifecycleOwner = this, - coroutineScope = this, - parameters = null - ) + context?.let { + scanFlow.startFlow( + context = it, + imageStream = cameraStream, + viewFinder = viewBinding.viewFinderWindow.asRect(), + lifecycleOwner = this, + coroutineScope = this, + parameters = null + ) + } } /** @@ -267,20 +269,24 @@ class CardScanFragment : ScanFragment(), SimpleScanStateful { override fun displayState(newState: CardScanState, previousState: CardScanState?) { when (newState) { is CardScanState.NotFound, CardScanState.Found -> { - viewBinding.viewFinderBackground - .setBackgroundColor( - requireActivity().getColorByRes(R.color.stripeNotFoundBackground) - ) + context?.let { + viewBinding.viewFinderBackground + .setBackgroundColor( + it.getColorByRes(R.color.stripeNotFoundBackground) + ) + } viewBinding.viewFinderWindow .setBackgroundResource(R.drawable.stripe_card_background_not_found) viewBinding.viewFinderBorder .startAnimation(R.drawable.stripe_paymentsheet_card_border_not_found) } is CardScanState.Correct -> { - viewBinding.viewFinderBackground - .setBackgroundColor( - requireActivity().getColorByRes(R.color.stripeCorrectBackground) - ) + context?.let { + viewBinding.viewFinderBackground + .setBackgroundColor( + it.getColorByRes(R.color.stripeCorrectBackground) + ) + } viewBinding.viewFinderWindow .setBackgroundResource(R.drawable.stripe_card_background_correct) viewBinding.viewFinderBorder.startAnimation(R.drawable.stripe_card_border_correct) @@ -293,8 +299,8 @@ class CardScanFragment : ScanFragment(), SimpleScanStateful { stripePublishableKey = params.stripePublishableKey, instanceId = Stats.instanceId, scanId = Stats.scanId, - device = Device.fromContext(requireActivity()), - appDetails = AppDetails.fromContext(requireActivity()), + device = Device.fromContext(context), + appDetails = AppDetails.fromContext(context), scanStatistics = ScanStatistics.fromStats(), scanConfig = ScanConfig(0), ) diff --git a/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/AppDetails.kt b/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/AppDetails.kt index 7e3ebb0610b..e8864966190 100644 --- a/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/AppDetails.kt +++ b/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/AppDetails.kt @@ -14,7 +14,7 @@ internal data class AppDetails( ) { companion object { @JvmStatic - fun fromContext(context: Context) = AppDetails( + fun fromContext(context: Context?) = AppDetails( appPackageName = getAppPackageName(context), applicationId = getApplicationId(), libraryPackageName = getLibraryPackageName(), @@ -26,7 +26,7 @@ internal data class AppDetails( } } -internal fun getAppPackageName(context: Context): String? = context.applicationContext.packageName +internal fun getAppPackageName(context: Context?): String? = context?.applicationContext?.packageName private fun getApplicationId(): String = "" // no longer available in later versions of gradle. diff --git a/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/Device.kt b/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/Device.kt index 18775be6b3e..f62a27e9aeb 100644 --- a/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/Device.kt +++ b/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/Device.kt @@ -21,7 +21,7 @@ internal data class Device( val platform: String ) { companion object { - private val getDeviceDetails = cacheFirstResult { context: Context -> + private val getDeviceDetails = cacheFirstResult { context: Context? -> Device( android_id = getAndroidId(), name = getDeviceName(), @@ -37,7 +37,7 @@ internal data class Device( } @JvmStatic - fun fromContext(context: Context) = getDeviceDetails(context.applicationContext) + fun fromContext(context: Context?) = getDeviceDetails(context?.applicationContext) } } @@ -51,10 +51,12 @@ internal data class Device( @SuppressLint("HardwareIds") private fun getAndroidId() = "Redacted" -private fun getDeviceBootCount(context: Context): Int = +private fun getDeviceBootCount(context: Context?): Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { try { - Settings.Global.getInt(context.contentResolver, Settings.Global.BOOT_COUNT) + context?.let { + Settings.Global.getInt(it.contentResolver, Settings.Global.BOOT_COUNT) + } ?: -1 } catch (t: Throwable) { -1 } @@ -65,27 +67,27 @@ private fun getDeviceBootCount(context: Context): Int = private fun getDeviceLocale(): String = "${Locale.getDefault().isO3Language}_${Locale.getDefault().isO3Country}" -private fun getDeviceCarrier(context: Context) = try { - (context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager?)?.networkOperatorName +private fun getDeviceCarrier(context: Context?) = try { + (context?.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager?)?.networkOperatorName } catch (t: Throwable) { null } -private fun getDevicePhoneType(context: Context) = try { - (context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager?)?.phoneType +private fun getDevicePhoneType(context: Context?) = try { + (context?.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager?)?.phoneType } catch (t: Throwable) { null } -private fun getDevicePhoneCount(context: Context) = +private fun getDevicePhoneCount(context: Context?) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - (context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager?) + (context?.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager?) ?.activeModemCount ?: -1 } else { @Suppress("deprecation") - (context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager?) + (context?.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager?) ?.phoneCount ?: -1 } } catch (t: Throwable) { @@ -95,8 +97,8 @@ private fun getDevicePhoneCount(context: Context) = -1 } -private fun getNetworkOperator(context: Context) = - (context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager?)?.networkOperator +private fun getNetworkOperator(context: Context?) = + (context?.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager?)?.networkOperator internal fun getOsVersion() = Build.VERSION.SDK_INT diff --git a/stripecardscan/src/main/java/com/stripe/android/stripecardscan/scanui/ScanFragment.kt b/stripecardscan/src/main/java/com/stripe/android/stripecardscan/scanui/ScanFragment.kt index 637ac06f27b..14b927975a3 100644 --- a/stripecardscan/src/main/java/com/stripe/android/stripecardscan/scanui/ScanFragment.kt +++ b/stripecardscan/src/main/java/com/stripe/android/stripecardscan/scanui/ScanFragment.kt @@ -73,8 +73,10 @@ abstract class ScanFragment : Fragment(), CoroutineScope { super.onStart() Stats.startScan() - if (!CameraAdapter.isCameraSupported(requireActivity())) { - showCameraNotSupported() + context?.let { + if (!CameraAdapter.isCameraSupported(it)) { + showCameraNotSupported() + } } } @@ -94,7 +96,7 @@ abstract class ScanFragment : Fragment(), CoroutineScope { protected open fun hideSystemUi() { // Prevent screenshots and keep the screen on while scanning. - requireActivity().window.setFlags( + activity?.window?.setFlags( WindowManager.LayoutParams.FLAG_SECURE + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_SECURE + From 8b9fd9c1599bc38551b6050c64afef94cffa26b8 Mon Sep 17 00:00:00 2001 From: Adam Wushensky Date: Thu, 26 May 2022 13:43:03 -0400 Subject: [PATCH 2/3] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e94de48471..e8506ffe2ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ * [FIXED] [4861](https://github.com/stripe/stripe-android/pull/4861) Remove font resource to save space and default to system default * [CHANGED] [4855](https://github.com/stripe/stripe-android/pull/4855) Remove force portrait mode in PaymentLauncher. +### CardScan +* [FIXED] [5075](https://github.com/stripe/stripe-android/pull/5075) Prevent a crash when the fragment is detached. + ## 20.0.1 - 2022-04-11 This release includes several PaymentSheet bug fixes. From 2bf6c2beaa2405297c2236038409eb167be65b8c Mon Sep 17 00:00:00 2001 From: Adam Wushensky Date: Thu, 26 May 2022 13:47:35 -0400 Subject: [PATCH 3/3] Fix lint --- .../stripe/android/stripecardscan/framework/util/AppDetails.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/AppDetails.kt b/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/AppDetails.kt index e8864966190..082a5beed91 100644 --- a/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/AppDetails.kt +++ b/stripecardscan/src/main/java/com/stripe/android/stripecardscan/framework/util/AppDetails.kt @@ -26,7 +26,8 @@ internal data class AppDetails( } } -internal fun getAppPackageName(context: Context?): String? = context?.applicationContext?.packageName +internal fun getAppPackageName(context: Context?): String? = + context?.applicationContext?.packageName private fun getApplicationId(): String = "" // no longer available in later versions of gradle.