diff --git a/apps/flipcash/app/src/main/AndroidManifest.xml b/apps/flipcash/app/src/main/AndroidManifest.xml index 361f0a0c3..525dd6a7c 100644 --- a/apps/flipcash/app/src/main/AndroidManifest.xml +++ b/apps/flipcash/app/src/main/AndroidManifest.xml @@ -9,8 +9,6 @@ - - diff --git a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/internal/extensions/Bitmap.kt b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/internal/extensions/Bitmap.kt index f6502ed16..fb5ce9e91 100644 --- a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/internal/extensions/Bitmap.kt +++ b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/internal/extensions/Bitmap.kt @@ -1,29 +1,45 @@ package com.flipcash.app.core.internal.extensions +import android.content.ContentResolver +import android.content.ContentValues import android.graphics.Bitmap +import android.os.Environment +import android.provider.MediaStore import com.getcode.utils.ErrorUtils import com.getcode.utils.timedTrace -import java.io.File -import java.io.FileOutputStream -fun Bitmap.save(destination: File, name: () -> String): Boolean { +fun Bitmap.save(contentResolver: ContentResolver, name: () -> String): Boolean { val filename = name() - if (!destination.exists()) { - if (!destination.mkdirs()) { - return false - } - } - val dest = File(destination, filename) return timedTrace("saving bitmap") { try { - FileOutputStream(dest).use { out -> + val contentValues = ContentValues().apply { + put(MediaStore.Images.Media.DISPLAY_NAME, filename) + put(MediaStore.Images.Media.MIME_TYPE, "image/png") + put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES) + put(MediaStore.Images.Media.IS_PENDING, 1) + } + + val uri = contentResolver.insert( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + contentValues + ) ?: return@timedTrace false + + contentResolver.openOutputStream(uri)?.use { out -> compress(Bitmap.CompressFormat.PNG, 90, out) + } ?: run { + contentResolver.delete(uri, null, null) + return@timedTrace false } + + contentValues.clear() + contentValues.put(MediaStore.Images.Media.IS_PENDING, 0) + contentResolver.update(uri, contentValues, null, null) + + return@timedTrace true } catch (e: Exception) { ErrorUtils.handleError(e) return@timedTrace false } - return@timedTrace true } } \ No newline at end of file diff --git a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/storage/MediaSaver.kt b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/storage/MediaSaver.kt new file mode 100644 index 000000000..19070c9b5 --- /dev/null +++ b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/storage/MediaSaver.kt @@ -0,0 +1,15 @@ +package com.flipcash.app.core.storage + +import android.content.Context +import android.graphics.Bitmap +import com.flipcash.app.core.internal.extensions.save +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject + +class MediaSaver @Inject constructor( + @ApplicationContext private val context: Context +) { + fun saveBitmap(bitmap: Bitmap, filename: String): Boolean { + return bitmap.save(context.contentResolver) { filename } + } +} diff --git a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/storage/MediaScanner.kt b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/storage/MediaScanner.kt deleted file mode 100644 index 2a82803b9..000000000 --- a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/storage/MediaScanner.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.flipcash.app.core.storage - -import android.content.Context -import android.media.MediaScannerConnection -import dagger.hilt.android.qualifiers.ApplicationContext -import java.io.File -import javax.inject.Inject - -class MediaScanner @Inject constructor( - @ApplicationContext private val context: Context -) { - fun scan(directory: File) { - MediaScannerConnection.scanFile(context, arrayOf(directory.toString()), null, null) - } -} \ No newline at end of file diff --git a/apps/flipcash/features/backupkey/src/main/kotlin/com/flipcash/app/backupkey/internal/BackupKeyScreenViewModel.kt b/apps/flipcash/features/backupkey/src/main/kotlin/com/flipcash/app/backupkey/internal/BackupKeyScreenViewModel.kt index cec48a104..7cca89286 100644 --- a/apps/flipcash/features/backupkey/src/main/kotlin/com/flipcash/app/backupkey/internal/BackupKeyScreenViewModel.kt +++ b/apps/flipcash/features/backupkey/src/main/kotlin/com/flipcash/app/backupkey/internal/BackupKeyScreenViewModel.kt @@ -1,7 +1,7 @@ package com.flipcash.app.backupkey.internal import com.flipcash.app.accesskey.BaseAccessKeyViewModel -import com.flipcash.app.core.storage.MediaScanner +import com.flipcash.app.core.storage.MediaSaver import com.flipcash.services.user.UserManager import com.getcode.libs.qr.QRCodeGenerator import com.getcode.opencode.managers.MnemonicManager @@ -17,13 +17,13 @@ import kotlin.time.Duration.Companion.seconds internal class BackupKeyScreenViewModel @Inject constructor( resources: ResourceHelper, mnemonicManager: MnemonicManager, - mediaScanner: MediaScanner, + mediaSaver: MediaSaver, userManager: UserManager, qrCodeGenerator: QRCodeGenerator, ) : BaseAccessKeyViewModel( resources, mnemonicManager, - mediaScanner, + mediaSaver, userManager, qrCodeGenerator ) { diff --git a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/accesskey/LoginAccessKeyViewModel.kt b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/accesskey/LoginAccessKeyViewModel.kt index 96c73a78c..7c545c92d 100644 --- a/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/accesskey/LoginAccessKeyViewModel.kt +++ b/apps/flipcash/features/login/src/main/kotlin/com/flipcash/app/login/accesskey/LoginAccessKeyViewModel.kt @@ -5,7 +5,7 @@ import com.flipcash.app.analytics.Action import com.flipcash.app.analytics.Button import com.flipcash.app.analytics.FlipcashAnalyticsService import com.flipcash.app.auth.AuthManager -import com.flipcash.app.core.storage.MediaScanner +import com.flipcash.app.core.storage.MediaSaver import com.flipcash.app.userflags.UserFlagsCoordinator import com.flipcash.services.user.UserManager import com.getcode.libs.qr.QRCodeGenerator @@ -22,12 +22,12 @@ class LoginAccessKeyViewModel @Inject constructor( resources: ResourceHelper, mnemonicManager: MnemonicManager, qrCodeGenerator: QRCodeGenerator, - mediaScanner: MediaScanner, + mediaSaver: MediaSaver, userManager: UserManager, private val userFlags: UserFlagsCoordinator, private val authManager: AuthManager, private val analytics: FlipcashAnalyticsService, -): BaseAccessKeyViewModel(resources, mnemonicManager, mediaScanner, userManager, qrCodeGenerator) { +): BaseAccessKeyViewModel(resources, mnemonicManager, mediaSaver, userManager, qrCodeGenerator) { suspend fun onWroteDownInstead(): Result { trackButton(Button.WroteAccessKey) diff --git a/apps/flipcash/shared/accesskey/src/main/kotlin/com/flipcash/app/accesskey/BaseAccessKeyViewModel.kt b/apps/flipcash/shared/accesskey/src/main/kotlin/com/flipcash/app/accesskey/BaseAccessKeyViewModel.kt index 42996fd37..10edc9b5f 100644 --- a/apps/flipcash/shared/accesskey/src/main/kotlin/com/flipcash/app/accesskey/BaseAccessKeyViewModel.kt +++ b/apps/flipcash/shared/accesskey/src/main/kotlin/com/flipcash/app/accesskey/BaseAccessKeyViewModel.kt @@ -8,13 +8,11 @@ import android.graphics.Path import android.graphics.RectF import android.graphics.Shader import android.graphics.Typeface -import android.os.Environment import androidx.core.graphics.applyCanvas import androidx.core.graphics.createBitmap import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.viewModelScope -import com.flipcash.app.core.internal.extensions.save -import com.flipcash.app.core.storage.MediaScanner +import com.flipcash.app.core.storage.MediaSaver import com.flipcash.app.theme.internal.Flipcash2ColorSpec import com.flipcash.services.user.UserManager import com.flipcash.shared.accesskey.R @@ -63,7 +61,7 @@ data class AccessKeyUiModel( abstract class BaseAccessKeyViewModel( private val resources: ResourceHelper, private val mnemonicManager: MnemonicManager, - private val mediaScanner: MediaScanner, + private val mediaSaver: MediaSaver, userManager: UserManager, private val qrCodeGenerator: QRCodeGenerator ) : ViewModel() { @@ -129,22 +127,12 @@ abstract class BaseAccessKeyViewModel( uiFlow.update { it.copy(exportState = LoadingSuccessState(loading = true)) } val bitmap = uiFlow.value.accessKeyBitmap ?: return Result.failure(IllegalStateException("No access key?")) - val destination = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) return withContext(Dispatchers.IO) { runCatching { - val result = bitmap.save( - destination = destination, - name = { - val date: DateFormat = SimpleDateFormat("yyy-MM-dd-h-mm", Locale.CANADA) - "Flipcash-Recovery-${date.format(Date())}.png" - } - ) - if (result) { - mediaScanner.scan(destination) - } - result + val date: DateFormat = SimpleDateFormat("yyy-MM-dd-h-mm", Locale.CANADA) + val filename = "Flipcash-Recovery-${date.format(Date())}.png" + mediaSaver.saveBitmap(bitmap, filename) } }.onFailure { getAccessKeySaveError()