From ac0d9b9954e45f7e77ad9f7e85f7b55600df85a4 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 7 Nov 2023 19:52:16 +0100 Subject: [PATCH] followup changes to save file feature - use MaterialAlertDialogBuilder - save files to matching folders depending on mimeType - show toast Signed-off-by: Marcel Hibbe --- .../activities/FullScreenImageActivity.kt | 42 ++++++++---- .../com/nextcloud/talk/chat/ChatActivity.kt | 41 +++++++----- .../talk/jobs/SaveFileToStorageWorker.kt | 66 +++++++++++++++++-- .../talk/ui/dialog/MessageActionsDialog.kt | 6 +- app/src/main/res/values-v27/styles.xml | 2 + app/src/main/res/values/strings.xml | 10 ++- app/src/main/res/values/styles.xml | 2 + 7 files changed, 123 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt index 6c6365e63cb..4589d3feca3 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt @@ -48,9 +48,12 @@ import androidx.work.Data import androidx.work.OneTimeWorkRequest import androidx.work.WorkInfo import androidx.work.WorkManager +import autodagger.AutoInjector +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import com.nextcloud.talk.BuildConfig import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.ActivityFullScreenImageBinding import com.nextcloud.talk.jobs.SaveFileToStorageWorker import com.nextcloud.talk.ui.theme.ViewThemeUtils @@ -59,12 +62,16 @@ import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX_GENERIC import pl.droidsonroids.gif.GifDrawable import java.io.File import java.util.concurrent.ExecutionException +import javax.inject.Inject +@AutoInjector(NextcloudTalkApplication::class) class FullScreenImageActivity : AppCompatActivity() { lateinit var binding: ActivityFullScreenImageBinding private lateinit var windowInsetsController: WindowInsetsControllerCompat private lateinit var path: String private var showFullscreen = false + + @Inject lateinit var viewThemeUtils: ViewThemeUtils override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -109,23 +116,32 @@ class FullScreenImageActivity : AppCompatActivity() { } private fun showWarningDialog() { - val builder = AlertDialog.Builder(this) - builder.setTitle(R.string.nc_dialog_save_to_storage_title) - builder.setMessage(R.string.nc_dialog_save_to_storage_content) - builder.setPositiveButton(R.string.nc_dialog_save_to_storage_yes) { dialog: DialogInterface, which: Int -> - val fileName = intent.getStringExtra("FILE_NAME").toString() - saveImageToStorage(fileName) - dialog.dismiss() - } - builder.setNegativeButton(R.string.nc_dialog_save_to_storage_no) { dialog: DialogInterface, which: Int -> - dialog.dismiss() - } - val dialog = builder.create() - dialog.show() + val dialogText = StringBuilder() + dialogText.append(resources.getString(R.string.nc_dialog_save_to_storage_content)) + dialogText.append("\n") + dialogText.append("\n") + dialogText.append(resources.getString(R.string.nc_dialog_save_to_storage_continue)) + + val dialogBuilder = MaterialAlertDialogBuilder(this) + .setTitle(R.string.nc_dialog_save_to_storage_title) + .setMessage(dialogText) + .setPositiveButton(R.string.nc_dialog_save_to_storage_yes) { _: DialogInterface?, _: Int -> + val fileName = intent.getStringExtra("FILE_NAME").toString() + saveImageToStorage(fileName) + } + .setNegativeButton(R.string.nc_dialog_save_to_storage_no) { _: DialogInterface?, _: Int -> + } + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(this, dialogBuilder) + val dialog = dialogBuilder.show() + viewThemeUtils.platform.colorTextButtons( + dialog.getButton(AlertDialog.BUTTON_POSITIVE), + dialog.getButton(AlertDialog.BUTTON_NEGATIVE) + ) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) binding = ActivityFullScreenImageBinding.inflate(layoutInflater) setContentView(binding.root) diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index 70e38703818..861d711ba26 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -899,7 +899,7 @@ class ChatActivity : } } else { Log.d(TAG, "Downloaded to cache") - downloadFileToCache(message,true ) { + downloadFileToCache(message, true) { setUpWaveform(message) } } @@ -4141,13 +4141,13 @@ class ChatActivity : if (file.exists()) { share(message) } else { - downloadFileToCache(message, false ) { + downloadFileToCache(message, false) { share(message) } } } - private fun saveImage(message: ChatMessage){ + private fun saveImage(message: ChatMessage) { if (permissionUtil.isFilesPermissionGranted()) { saveImageToStorage(message) } else { @@ -4156,18 +4156,26 @@ class ChatActivity : } private fun showSaveToStorageWarning(message: ChatMessage) { - val builder = AlertDialog.Builder(this) - builder.setTitle(R.string.nc_dialog_save_to_storage_title) - builder.setMessage(R.string.nc_dialog_save_to_storage_content) - builder.setPositiveButton(R.string.nc_dialog_save_to_storage_yes) { dialog: DialogInterface, _: Int -> - saveImage(message) - dialog.dismiss() - } - builder.setNegativeButton(R.string.nc_dialog_save_to_storage_no) { dialog: DialogInterface, _: Int -> - dialog.dismiss() - } - val dialog = builder.create() - dialog.show() + val dialogText = StringBuilder() + dialogText.append(resources.getString(R.string.nc_dialog_save_to_storage_content)) + dialogText.append(LINEBREAK) + dialogText.append(LINEBREAK) + dialogText.append(resources.getString(R.string.nc_dialog_save_to_storage_continue)) + + val dialogBuilder = MaterialAlertDialogBuilder(this) + .setTitle(R.string.nc_dialog_save_to_storage_title) + .setMessage(dialogText) + .setPositiveButton(R.string.nc_dialog_save_to_storage_yes) { _: DialogInterface?, _: Int -> + saveImage(message) + } + .setNegativeButton(R.string.nc_dialog_save_to_storage_no) { _: DialogInterface?, _: Int -> + } + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(this, dialogBuilder) + val dialog = dialogBuilder.show() + viewThemeUtils.platform.colorTextButtons( + dialog.getButton(AlertDialog.BUTTON_POSITIVE), + dialog.getButton(AlertDialog.BUTTON_NEGATIVE) + ) } fun checkIfSaveable(message: ChatMessage) { @@ -4177,7 +4185,7 @@ class ChatActivity : if (file.exists()) { showSaveToStorageWarning(message) } else { - downloadFileToCache(message ,false) { + downloadFileToCache(message, false) { showSaveToStorageWarning(message) } } @@ -4608,5 +4616,6 @@ class ChatActivity : private const val TYPING_STOPPED_SIGNALING_MESSAGE_TYPE = "stoppedTyping" private const val CALL_STARTED_ID = -2 private const val MILISEC_15: Long = 15 + private const val LINEBREAK = "\n" } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/SaveFileToStorageWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/SaveFileToStorageWorker.kt index 29f70295557..20ac65e739a 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/SaveFileToStorageWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/SaveFileToStorageWorker.kt @@ -1,10 +1,10 @@ /* * Nextcloud Talk application * - * @author Andy Scherzinger + * @author Fariba Khandani * @author Marcel Hibbe - * Copyright (C) 2022 Andy Scherzinger - * Copyright (C) 2021 Marcel Hibbe + * Copyright (C) 2023 Fariba Khandani + * Copyright (C) 2023 Marcel Hibbe * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,14 +25,23 @@ package com.nextcloud.talk.jobs import android.content.ContentValues import android.content.Context import android.media.MediaScannerConnection +import android.net.Uri +import android.os.Build import android.os.Environment +import android.os.Handler +import android.os.Looper import android.provider.MediaStore import android.provider.MediaStore.Files.FileColumns import android.util.Log +import android.widget.Toast import androidx.work.Worker import androidx.work.WorkerParameters import autodagger.AutoInjector +import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.utils.Mimetype.AUDIO_PREFIX +import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX +import com.nextcloud.talk.utils.Mimetype.VIDEO_PREFIX import java.io.File import java.io.IOException import java.io.OutputStream @@ -50,16 +59,22 @@ class SaveFileToStorageWorker(val context: Context, workerParameters: WorkerPara val contentResolver = context.contentResolver val mimeType = URLConnection.guessContentTypeFromName(cacheFile.name) + val appName = applicationContext.resources!!.getString(R.string.nc_app_product_name) + val values = ContentValues().apply { + if (mimeType.startsWith(IMAGE_PREFIX) || mimeType.startsWith(VIDEO_PREFIX)) { + put(FileColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/" + appName) + } put(FileColumns.DISPLAY_NAME, cacheFile.name) - put(FileColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) + if (mimeType != null) { - put(FileColumns.MIME_TYPE, URLConnection.guessContentTypeFromName(cacheFile.name)) + put(FileColumns.MIME_TYPE, mimeType) } } - val collection = MediaStore.Files.getContentUri("external") - val uri = contentResolver.insert(collection, values) + val collectionUri = getUriByType(mimeType) + + val uri = contentResolver.insert(collectionUri, values) uri?.let { fileUri -> try { @@ -79,16 +94,53 @@ class SaveFileToStorageWorker(val context: Context, workerParameters: WorkerPara // Notify the media scanner about the new file MediaScannerConnection.scanFile(context, arrayOf(cacheFile.absolutePath), null, null) + Handler(Looper.getMainLooper()).post { + Toast.makeText( + context, + context.resources.getString(R.string.nc_save_success), + Toast.LENGTH_SHORT + ).show() + } + return Result.success() } catch (e: IOException) { Log.e(TAG, "Something went wrong when trying to save file to internal storage", e) + Handler(Looper.getMainLooper()).post { + Toast.makeText( + context, + context.resources.getString(R.string.nc_common_error_sorry), + Toast.LENGTH_SHORT + ).show() + } + return Result.failure() } catch (e: NullPointerException) { Log.e(TAG, "Something went wrong when trying to save file to internal storage", e) + Handler(Looper.getMainLooper()).post { + Toast.makeText( + context, + context.resources.getString(R.string.nc_common_error_sorry), + Toast.LENGTH_SHORT + ).show() + } + return Result.failure() } } + private fun getUriByType(contentType: String): Uri { + return when { + contentType.startsWith(VIDEO_PREFIX) -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI + contentType.startsWith(AUDIO_PREFIX) -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + contentType.startsWith(IMAGE_PREFIX) -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI + else -> if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + Uri.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)) + } else { + MediaStore.Downloads.EXTERNAL_CONTENT_URI + } + } + } + companion object { private val TAG = SaveFileToStorageWorker::class.java.simpleName const val KEY_FILE_NAME = "KEY_FILE_NAME" diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt index 3258b64ee6a..1cd258656f3 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt @@ -170,8 +170,6 @@ class MessageActionsDialog( dialogMessageActionsBinding.emojiMore.installForceSingleEmoji() } - - /* This method is a hacky workaround to avoid bug #1914 As the bug happens only for the very first time when the popup is opened, @@ -355,8 +353,8 @@ class MessageActionsDialog( dialogMessageActionsBinding.menuOpenInNcApp.visibility = getVisibility(visible) } - private fun initMenuItemSave (visible: Boolean) { - if (visible){ + private fun initMenuItemSave(visible: Boolean) { + if (visible) { dialogMessageActionsBinding.menuSaveMessage.setOnClickListener { chatActivity.checkIfSaveable(message) dismiss() diff --git a/app/src/main/res/values-v27/styles.xml b/app/src/main/res/values-v27/styles.xml index f1c0083c1de..fc71af59aff 100644 --- a/app/src/main/res/values-v27/styles.xml +++ b/app/src/main/res/values-v27/styles.xml @@ -8,6 +8,7 @@ true true shortEdges + @color/bg_default \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2ed5d77d44e..dd345ce04a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -51,7 +51,6 @@ How to translate with transifex: Sorry, something went wrong! Create - Settings @@ -519,15 +518,14 @@ How to translate with transifex: Chat via %s Account not found - //save feature + Save Save to storage? - Saving this media to storage will allow any other apps on - your device to access it.\nContinue? + Saving this media to storage will allow any other apps on your device to access it. + Continue? Yes No - - + Saved successfully Favorite Status diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 0450185fe95..e5aed487917 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -221,6 +221,7 @@ @color/transparent true true + @color/bg_default