From 59c2261e7c3dbc2e349b2bfbbbd98232ed1f9e96 Mon Sep 17 00:00:00 2001 From: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:25:40 -0400 Subject: [PATCH] [PM-24411] Add shareErrorReport to IntentManager (#5655) --- .../composition/LocalManagerProvider.kt | 9 ++++- .../manager/di/PlatformUiManagerModule.kt | 2 + .../platform/manager/intent/IntentManager.kt | 5 +++ .../manager/intent/IntentManagerImpl.kt | 39 +++++++++++++------ 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/composition/LocalManagerProvider.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/composition/LocalManagerProvider.kt index 097b7b8922..925c238ff7 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/composition/LocalManagerProvider.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/composition/LocalManagerProvider.kt @@ -11,12 +11,14 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.ProvidableCompositionLocal import androidx.compose.runtime.compositionLocalOf import com.bitwarden.annotation.OmitFromCoverage +import com.bitwarden.core.data.manager.BuildInfoManager import com.bitwarden.core.util.isBuildVersionAtLeast import com.x8bit.bitwarden.data.platform.manager.util.AppResumeStateManager import com.x8bit.bitwarden.data.platform.manager.util.AppResumeStateManagerImpl import com.x8bit.bitwarden.ui.credentials.manager.CredentialProviderCompletionManager import com.x8bit.bitwarden.ui.credentials.manager.CredentialProviderCompletionManagerImpl import com.x8bit.bitwarden.ui.credentials.manager.CredentialProviderCompletionManagerUnsupportedApiImpl +import com.x8bit.bitwarden.ui.platform.manager.BitwardenBuildInfoManagerImpl import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManagerImpl import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager @@ -46,7 +48,12 @@ fun LocalManagerProvider( biometricsManager: BiometricsManager = BiometricsManagerImpl(activity = activity), clock: Clock = Clock.systemDefaultZone(), exitManager: ExitManager = ExitManagerImpl(activity = activity), - intentManager: IntentManager = IntentManagerImpl(context = activity, clock = clock), + buildInfoManager: BuildInfoManager = BitwardenBuildInfoManagerImpl(), + intentManager: IntentManager = IntentManagerImpl( + context = activity, + clock = clock, + buildInfoManager = buildInfoManager, + ), credentialProviderCompletionManager: CredentialProviderCompletionManager = createCredentialProviderCompletionManager(activity = activity), keyChainManager: KeyChainManager = KeyChainManagerImpl(activity = activity), diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/di/PlatformUiManagerModule.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/di/PlatformUiManagerModule.kt index c371aa6451..27a1b45538 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/di/PlatformUiManagerModule.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/di/PlatformUiManagerModule.kt @@ -34,10 +34,12 @@ object PlatformUiManagerModule { fun provideIntentManager( @ApplicationContext context: Context, clock: Clock, + buildInfoManager: BuildInfoManager, ): IntentManager = IntentManagerImpl( context = context, clock = clock, + buildInfoManager = buildInfoManager, ) @Provides diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManager.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManager.kt index 2b7386cd31..2cd6e41c23 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManager.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManager.kt @@ -78,6 +78,11 @@ interface IntentManager { */ fun shareText(text: String) + /** + * Launches the share sheet with an error report based on the provided [throwable]. + */ + fun shareErrorReport(throwable: Throwable) + /** * Processes the [activityResult] and attempts to get the relevant file data from it. */ diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManagerImpl.kt index 73f57cb331..cd106b0750 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/manager/intent/IntentManagerImpl.kt @@ -23,22 +23,17 @@ import androidx.core.content.FileProvider import androidx.core.net.toUri import androidx.credentials.CredentialManager import com.bitwarden.annotation.OmitFromCoverage +import com.bitwarden.core.data.manager.BuildInfoManager import com.bitwarden.core.data.util.toFormattedPattern import com.bitwarden.core.util.isBuildVersionAtLeast +import com.bitwarden.ui.platform.manager.util.deviceData +import com.bitwarden.ui.platform.manager.util.fileProviderAuthority import com.bitwarden.ui.platform.model.FileData import com.bitwarden.ui.platform.resource.BitwardenString -import com.x8bit.bitwarden.BuildConfig import com.x8bit.bitwarden.data.autofill.model.browser.BrowserPackage import java.io.File import java.time.Clock -/** - * The authority used for pulling in photos from the camera. - * - * Note: This must match the file provider authority in the manifest. - */ -private const val FILE_PROVIDER_AUTHORITY: String = "${BuildConfig.APPLICATION_ID}.fileprovider" - /** * Temporary file name for a camera image. */ @@ -58,6 +53,7 @@ private const val TEMP_CAMERA_IMAGE_DIR: String = "camera_temp" class IntentManagerImpl( private val context: Context, private val clock: Clock, + private val buildInfoManager: BuildInfoManager, ) : IntentManager { override fun startActivity(intent: Intent) { try { @@ -155,7 +151,7 @@ class IntentManagerImpl( override fun shareFile(title: String?, fileUri: Uri) { val providedFile = FileProvider.getUriForFile( context, - FILE_PROVIDER_AUTHORITY, + buildInfoManager.fileProviderAuthority, File(fileUri.toString()), ) val sendIntent: Intent = Intent(Intent.ACTION_SEND).apply { @@ -174,6 +170,21 @@ class IntentManagerImpl( startActivity(Intent.createChooser(sendIntent, null)) } + override fun shareErrorReport(throwable: Throwable) { + shareText( + StringBuilder() + .append("Stacktrace:\n") + .append("$throwable\n") + .apply { throwable.stackTrace.forEach { append("\t$it\n") } } + .append("\n") + .append("Version: ${buildInfoManager.versionData}\n") + .append("Device: ${buildInfoManager.deviceData}\n") + .apply { buildInfoManager.ciBuildInfo?.let { append("CI: $it\n") } } + .append("\n") + .toString(), + ) + } + override fun getFileDataFromActivityResult( activityResult: ActivityResult, ): FileData? { @@ -192,7 +203,7 @@ class IntentManagerImpl( val uriString = uri.toString() context .packageManager - .getPackageInfo(BuildConfig.APPLICATION_ID, PackageManager.GET_PROVIDERS) + .getPackageInfo(buildInfoManager.applicationId, PackageManager.GET_PROVIDERS) .providers ?.any { uriString.contains(other = it.authority) } == true } @@ -234,7 +245,7 @@ class IntentManagerImpl( } val outputFileUri = FileProvider.getUriForFile( context, - FILE_PROVIDER_AUTHORITY, + buildInfoManager.fileProviderAuthority, file, ) @@ -279,7 +290,11 @@ class IntentManagerImpl( private fun getCameraFileData(): FileData { val tmpDir = File(context.filesDir, TEMP_CAMERA_IMAGE_DIR) val file = File(tmpDir, TEMP_CAMERA_IMAGE_NAME) - val uri = FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, file) + val uri = FileProvider.getUriForFile( + context, + buildInfoManager.fileProviderAuthority, + file, + ) val fileName = "photo_${clock.instant().toFormattedPattern(pattern = "yyyyMMddHHmmss")}.jpg" return FileData( fileName = fileName,