From 640007b421e681d8c5774f5fcd363dd739712c1e 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 - extract dialog to SaveToStorageDialogFragment - add ability to save files of other mimetypes than images - use MaterialAlertDialogBuilder - save files to matching folders depending on mimeType - show toast - change download icon Signed-off-by: Marcel Hibbe --- app/src/main/AndroidManifest.xml | 6 +- .../com/nextcloud/talk/chat/ChatActivity.kt | 69 +------- .../FullScreenImageActivity.kt | 75 ++------- .../FullScreenMediaActivity.kt | 17 +- .../FullScreenTextViewerActivity.kt | 57 ++++--- .../talk/jobs/SaveFileToStorageWorker.kt | 66 +++++++- .../ui/dialog/SaveToStorageDialogFragment.kt | 155 ++++++++++++++++++ .../nextcloud/talk/utils/FileViewerUtils.kt | 6 +- .../res/drawable/baseline_download_24.xml | 22 +++ .../res/layout/activity_full_screen_image.xml | 2 +- .../res/layout/activity_full_screen_media.xml | 2 +- .../res/layout/activity_full_screen_text.xml | 2 +- .../res/layout/dialog_message_actions.xml | 2 +- app/src/main/res/values-v27/styles.xml | 4 +- app/src/main/res/values/strings.xml | 9 +- app/src/main/res/values/styles.xml | 2 + 16 files changed, 332 insertions(+), 164 deletions(-) rename app/src/main/java/com/nextcloud/talk/{activities => fullscreenfile}/FullScreenImageActivity.kt (75%) rename app/src/main/java/com/nextcloud/talk/{activities => fullscreenfile}/FullScreenMediaActivity.kt (93%) rename app/src/main/java/com/nextcloud/talk/{activities => fullscreenfile}/FullScreenTextViewerActivity.kt (71%) create mode 100644 app/src/main/java/com/nextcloud/talk/ui/dialog/SaveToStorageDialogFragment.kt create mode 100644 app/src/main/res/drawable/baseline_download_24.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f3ab5958ca..89f0d25efa 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -152,17 +152,17 @@ android:theme="@style/AppTheme.CallLauncher" /> 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 22a88a8429..a6af87bdd4 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -34,7 +34,6 @@ import android.annotation.SuppressLint import android.content.ClipData import android.content.ClipboardManager import android.content.Context -import android.content.DialogInterface import android.content.Intent import android.content.pm.PackageManager import android.content.res.AssetFileDescriptor @@ -157,7 +156,6 @@ import com.nextcloud.talk.events.UserMentionClickEvent import com.nextcloud.talk.events.WebSocketCommunicationEvent import com.nextcloud.talk.extensions.loadAvatarOrImagePreview import com.nextcloud.talk.jobs.DownloadFileToCacheWorker -import com.nextcloud.talk.jobs.SaveFileToStorageWorker import com.nextcloud.talk.jobs.ShareOperationWorker import com.nextcloud.talk.jobs.UploadAndShareFilesWorker import com.nextcloud.talk.location.LocationPickerActivity @@ -191,6 +189,7 @@ import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet import com.nextcloud.talk.ui.dialog.AttachmentDialog import com.nextcloud.talk.ui.dialog.DateTimePickerFragment import com.nextcloud.talk.ui.dialog.MessageActionsDialog +import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment import com.nextcloud.talk.ui.dialog.ShowReactionsDialog import com.nextcloud.talk.ui.recyclerview.MessageSwipeActions import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback @@ -2020,44 +2019,6 @@ class ChatActivity : } } - @SuppressLint("LongLogTag") - private fun saveImageToStorage( - message: ChatMessage - ) { - message.openWhenDownloaded = false - adapter?.update(message) - - val fileName = message.selectedIndividualHashMap!!["name"] - val sourceFilePath = applicationContext.cacheDir.path - val fileId = message.selectedIndividualHashMap!!["id"] - - val workers = WorkManager.getInstance(context).getWorkInfosByTag(fileId!!) - try { - for (workInfo in workers.get()) { - if (workInfo.state == WorkInfo.State.RUNNING || workInfo.state == WorkInfo.State.ENQUEUED) { - Log.d(TAG, "SaveFileToStorageWorker for $fileId is already running or scheduled") - return - } - } - } catch (e: ExecutionException) { - Log.e(TAG, "Error when checking if worker already exists", e) - } catch (e: InterruptedException) { - Log.e(TAG, "Error when checking if worker already exists", e) - } - - val data: Data = Data.Builder() - .putString(SaveFileToStorageWorker.KEY_FILE_NAME, fileName) - .putString(SaveFileToStorageWorker.KEY_SOURCE_FILE_PATH, "$sourceFilePath/$fileName") - .build() - - val saveWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(SaveFileToStorageWorker::class.java) - .setInputData(data) - .addTag(fileId) - .build() - - WorkManager.getInstance().enqueue(saveWorker) - } - @SuppressLint("SimpleDateFormat") private fun setVoiceRecordFileName() { val simpleDateFormat = SimpleDateFormat(FILE_DATE_PATTERN) @@ -4147,27 +4108,14 @@ class ChatActivity : } } - private fun saveImage(message: ChatMessage) { - if (permissionUtil.isFilesPermissionGranted()) { - saveImageToStorage(message) - } else { - UploadAndShareFilesWorker.requestStoragePermission(this@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 saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance( + message.selectedIndividualHashMap!!["name"]!! + ) + saveFragment.show( + supportFragmentManager, + SaveToStorageDialogFragment.TAG + ) } fun checkIfSaveable(message: ChatMessage) { @@ -4608,5 +4556,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/activities/FullScreenImageActivity.kt b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenImageActivity.kt similarity index 75% rename from app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt rename to app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenImageActivity.kt index 6c6365e63c..ccdfec5a93 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenImageActivity.kt @@ -24,10 +24,8 @@ * along with this program. If not, see . */ -package com.nextcloud.talk.activities +package com.nextcloud.talk.fullscreenfile -import android.annotation.SuppressLint -import android.content.DialogInterface import android.content.Intent import android.os.Bundle import android.util.Log @@ -35,7 +33,6 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.view.ViewGroup.MarginLayoutParams -import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.content.FileProvider import androidx.core.view.ViewCompat @@ -44,28 +41,25 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding -import androidx.work.Data -import androidx.work.OneTimeWorkRequest -import androidx.work.WorkInfo -import androidx.work.WorkManager +import androidx.fragment.app.DialogFragment +import autodagger.AutoInjector 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 +import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment import com.nextcloud.talk.utils.BitmapShrinker import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX_GENERIC import pl.droidsonroids.gif.GifDrawable import java.io.File -import java.util.concurrent.ExecutionException +@AutoInjector(NextcloudTalkApplication::class) class FullScreenImageActivity : AppCompatActivity() { lateinit var binding: ActivityFullScreenImageBinding private lateinit var windowInsetsController: WindowInsetsControllerCompat private lateinit var path: String private var showFullscreen = false - lateinit var viewThemeUtils: ViewThemeUtils override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_preview, menu) @@ -98,7 +92,13 @@ class FullScreenImageActivity : AppCompatActivity() { } R.id.save -> { - showWarningDialog() + val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance( + intent.getStringExtra("FILE_NAME").toString() + ) + saveFragment.show( + supportFragmentManager, + SaveToStorageDialogFragment.TAG + ) true } @@ -108,24 +108,9 @@ 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() - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) binding = ActivityFullScreenImageBinding.inflate(layoutInflater) setContentView(binding.root) @@ -222,38 +207,6 @@ class FullScreenImageActivity : AppCompatActivity() { } } - @SuppressLint("LongLogTag") - private fun saveImageToStorage( - fileName: String - ) { - val sourceFilePath = applicationContext.cacheDir.path - - val workers = WorkManager.getInstance(this).getWorkInfosByTag(fileName) - try { - for (workInfo in workers.get()) { - if (workInfo.state == WorkInfo.State.RUNNING || workInfo.state == WorkInfo.State.ENQUEUED) { - return - } - } - } catch (e: ExecutionException) { - Log.e(TAG, "Error when checking if worker already exists", e) - } catch (e: InterruptedException) { - Log.e(TAG, "Error when checking if worker already exists", e) - } - - val data: Data = Data.Builder() - .putString(SaveFileToStorageWorker.KEY_FILE_NAME, fileName) - .putString(SaveFileToStorageWorker.KEY_SOURCE_FILE_PATH, "$sourceFilePath/$fileName") - .build() - - val saveWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(SaveFileToStorageWorker::class.java) - .setInputData(data) - .addTag(fileName) - .build() - - WorkManager.getInstance().enqueue(saveWorker) - } - companion object { private const val TAG = "FullScreenImageActivity" private const val HUNDRED_MB = 100 * 1024 * 1024 diff --git a/app/src/main/java/com/nextcloud/talk/activities/FullScreenMediaActivity.kt b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt similarity index 93% rename from app/src/main/java/com/nextcloud/talk/activities/FullScreenMediaActivity.kt rename to app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt index 6d9f9262db..1d6796b522 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/FullScreenMediaActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt @@ -24,7 +24,7 @@ * along with this program. If not, see . */ -package com.nextcloud.talk.activities +package com.nextcloud.talk.fullscreenfile import android.content.Intent import android.os.Bundle @@ -43,6 +43,7 @@ import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.marginBottom import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding +import androidx.fragment.app.DialogFragment import androidx.media3.common.MediaItem import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer @@ -53,6 +54,7 @@ import com.nextcloud.talk.BuildConfig import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.ActivityFullScreenMediaBinding +import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment import com.nextcloud.talk.utils.Mimetype.VIDEO_PREFIX_GENERIC import java.io.File @@ -78,6 +80,7 @@ class FullScreenMediaActivity : AppCompatActivity() { onBackPressedDispatcher.onBackPressed() true } + R.id.share -> { val shareUri = FileProvider.getUriForFile( this, @@ -95,6 +98,18 @@ class FullScreenMediaActivity : AppCompatActivity() { true } + + R.id.save -> { + val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance( + intent.getStringExtra("FILE_NAME").toString() + ) + saveFragment.show( + supportFragmentManager, + SaveToStorageDialogFragment.TAG + ) + true + } + else -> { super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/com/nextcloud/talk/activities/FullScreenTextViewerActivity.kt b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenTextViewerActivity.kt similarity index 71% rename from app/src/main/java/com/nextcloud/talk/activities/FullScreenTextViewerActivity.kt rename to app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenTextViewerActivity.kt index 00d17b7d52..d78ff54c38 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/FullScreenTextViewerActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenTextViewerActivity.kt @@ -22,7 +22,7 @@ * along with this program. If not, see . */ -package com.nextcloud.talk.activities +package com.nextcloud.talk.fullscreenfile import android.content.Intent import android.os.Bundle @@ -31,11 +31,13 @@ import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import androidx.core.content.FileProvider import androidx.core.content.res.ResourcesCompat +import androidx.fragment.app.DialogFragment import autodagger.AutoInjector import com.nextcloud.talk.BuildConfig import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.ActivityFullScreenTextBinding +import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.Mimetype.TEXT_PREFIX_GENERIC @@ -58,27 +60,44 @@ class FullScreenTextViewerActivity : AppCompatActivity() { } override fun onOptionsItemSelected(item: MenuItem): Boolean { - return if (item.itemId == android.R.id.home) { - onBackPressedDispatcher.onBackPressed() - true - } else if (item.itemId == R.id.share) { - val shareUri = FileProvider.getUriForFile( - this, - BuildConfig.APPLICATION_ID, - File(path) - ) + return when (item.itemId) { + android.R.id.home -> { + onBackPressedDispatcher.onBackPressed() + true + } - val shareIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_STREAM, shareUri) - type = TEXT_PREFIX_GENERIC - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + R.id.share -> { + val shareUri = FileProvider.getUriForFile( + this, + BuildConfig.APPLICATION_ID, + File(path) + ) + + val shareIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, shareUri) + type = TEXT_PREFIX_GENERIC + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.send_to))) + + true } - startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.send_to))) - true - } else { - super.onOptionsItemSelected(item) + R.id.save -> { + val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance( + intent.getStringExtra("FILE_NAME").toString() + ) + saveFragment.show( + supportFragmentManager, + SaveToStorageDialogFragment.TAG + ) + true + } + + else -> { + super.onOptionsItemSelected(item) + } } } 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 29f7029555..c2c90c551d 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(mimeType: String): Uri { + return when { + mimeType.startsWith(VIDEO_PREFIX) -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI + mimeType.startsWith(AUDIO_PREFIX) -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + mimeType.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/SaveToStorageDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SaveToStorageDialogFragment.kt new file mode 100644 index 0000000000..c429e94660 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SaveToStorageDialogFragment.kt @@ -0,0 +1,155 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * @author Fariba Khandani + * Copyright (C) 2023 Marcel Hibbe (dev@mhibbe.de) + * Copyright (C) 2023 Fariba Khandani + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.ui.dialog + +import android.annotation.SuppressLint +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +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.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication +import com.nextcloud.talk.databinding.DialogChooseAccountShareToBinding +import com.nextcloud.talk.jobs.SaveFileToStorageWorker +import com.nextcloud.talk.ui.theme.ViewThemeUtils +import java.util.concurrent.ExecutionException +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class SaveToStorageDialogFragment : DialogFragment() { + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + private var binding: DialogChooseAccountShareToBinding? = null + private var dialogView: View? = null + lateinit var fileName: String + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + sharedApplication!!.componentApplication.inject(this) + fileName = arguments?.getString(KEY_FILE_NAME)!! + } + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + 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(requireContext()) + .setTitle(R.string.nc_dialog_save_to_storage_title) + .setMessage(dialogText) + .setPositiveButton(R.string.nc_dialog_save_to_storage_yes) { _: DialogInterface?, _: Int -> + saveImageToStorage(fileName) + } + .setNegativeButton(R.string.nc_dialog_save_to_storage_no) { _: DialogInterface?, _: Int -> + } + viewThemeUtils.dialog.colorMaterialAlertDialogBackground( + requireContext(), + dialogBuilder + ) + val dialog = dialogBuilder.show() + viewThemeUtils.platform.colorTextButtons( + dialog.getButton(AlertDialog.BUTTON_POSITIVE), + dialog.getButton(AlertDialog.BUTTON_NEGATIVE) + ) + + return dialog + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + themeViews() + } + + private fun themeViews() { + viewThemeUtils.platform.themeDialog(binding!!.root) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return dialogView + } + + override fun onDestroyView() { + super.onDestroyView() + binding = null + } + + @SuppressLint("LongLogTag") + private fun saveImageToStorage( + fileName: String + ) { + val sourceFilePath = requireContext().cacheDir.path + val workerTag = SAVE_TO_STORAGE_WORKER_PREFIX + fileName + + val workers = WorkManager.getInstance(requireContext()).getWorkInfosByTag(workerTag) + try { + for (workInfo in workers.get()) { + if (workInfo.state == WorkInfo.State.RUNNING || workInfo.state == WorkInfo.State.ENQUEUED) { + return + } + } + } catch (e: ExecutionException) { + Log.e(TAG, "Error when checking if worker already exists", e) + } catch (e: InterruptedException) { + Log.e(TAG, "Error when checking if worker already exists", e) + } + + val data: Data = Data.Builder() + .putString(SaveFileToStorageWorker.KEY_FILE_NAME, fileName) + .putString(SaveFileToStorageWorker.KEY_SOURCE_FILE_PATH, "$sourceFilePath/$fileName") + .build() + + val saveWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(SaveFileToStorageWorker::class.java) + .setInputData(data) + .addTag(workerTag) + .build() + + WorkManager.getInstance().enqueue(saveWorker) + } + + companion object { + val TAG = SaveToStorageDialogFragment::class.java.simpleName + private const val KEY_FILE_NAME = "keyFileName" + private const val SAVE_TO_STORAGE_WORKER_PREFIX = "saveToStorage_" + + fun newInstance(fileName: String): SaveToStorageDialogFragment { + val args = Bundle() + args.putString(KEY_FILE_NAME, fileName) + val fragment = SaveToStorageDialogFragment() + fragment.arguments = args + return fragment + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt index af5f636ac2..caa66d9972 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt @@ -37,9 +37,9 @@ import androidx.work.WorkInfo import androidx.work.WorkManager import com.google.android.material.snackbar.Snackbar import com.nextcloud.talk.R -import com.nextcloud.talk.activities.FullScreenImageActivity -import com.nextcloud.talk.activities.FullScreenMediaActivity -import com.nextcloud.talk.activities.FullScreenTextViewerActivity +import com.nextcloud.talk.fullscreenfile.FullScreenImageActivity +import com.nextcloud.talk.fullscreenfile.FullScreenMediaActivity +import com.nextcloud.talk.fullscreenfile.FullScreenTextViewerActivity import com.nextcloud.talk.adapters.messages.PreviewMessageViewHolder import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.jobs.DownloadFileToCacheWorker diff --git a/app/src/main/res/drawable/baseline_download_24.xml b/app/src/main/res/drawable/baseline_download_24.xml new file mode 100644 index 0000000000..e5253209de --- /dev/null +++ b/app/src/main/res/drawable/baseline_download_24.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/app/src/main/res/layout/activity_full_screen_image.xml b/app/src/main/res/layout/activity_full_screen_image.xml index d7c954011f..5e304e925f 100644 --- a/app/src/main/res/layout/activity_full_screen_image.xml +++ b/app/src/main/res/layout/activity_full_screen_image.xml @@ -28,7 +28,7 @@ android:id="@+id/image_wrapper_view" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".activities.FullScreenImageActivity"> + tools:context=".fullscreenfile.FullScreenImageActivity"> + tools:context=".fullscreenfile.FullScreenMediaActivity"> + tools:context=".fullscreenfile.FullScreenTextViewerActivity"> 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 e673f0da9e..dd345ce04a 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,14 +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 0450185fe9..e5aed48791 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