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 <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2023-11-07 19:52:16 +01:00
parent 2f24c130ca
commit 640007b421
No known key found for this signature in database
GPG Key ID: C793F8B59F43CE7B
16 changed files with 333 additions and 165 deletions

View File

@ -152,17 +152,17 @@
android:theme="@style/AppTheme.CallLauncher" />
<activity
android:name=".activities.FullScreenImageActivity"
android:name=".fullscreenfile.FullScreenImageActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/FullScreenImageTheme"/>
<activity
android:name=".activities.FullScreenMediaActivity"
android:name=".fullscreenfile.FullScreenMediaActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/FullScreenMediaTheme"/>
<activity
android:name=".activities.FullScreenTextViewerActivity"
android:name=".fullscreenfile.FullScreenTextViewerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/FullScreenTextTheme"/>

View File

@ -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"
}
}

View File

@ -24,10 +24,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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

View File

@ -24,7 +24,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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)
}

View File

@ -22,7 +22,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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)
)
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)
return when (item.itemId) {
android.R.id.home -> {
onBackPressedDispatcher.onBackPressed()
true
}
startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.send_to)))
true
} else {
super.onOptionsItemSelected(item)
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
}
R.id.save -> {
val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance(
intent.getStringExtra("FILE_NAME").toString()
)
saveFragment.show(
supportFragmentManager,
SaveToStorageDialogFragment.TAG
)
true
}
else -> {
super.onOptionsItemSelected(item)
}
}
}

View File

@ -1,10 +1,10 @@
/*
* Nextcloud Talk application
*
* @author Andy Scherzinger
* @author Fariba Khandani
* @author Marcel Hibbe
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
* Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
* Copyright (C) 2023 Fariba Khandani <khandani@winworker.de>
* Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
*
* 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"

View File

@ -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 <khandani@winworker.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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
}
}
}

View File

@ -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

View File

@ -0,0 +1,22 @@
<!--
@author Google LLC
Copyright (C) 2023 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M5,20h14v-2H5V20zM19,9h-4V3H9v6H5l7,7L19,9z"/>
</vector>

View File

@ -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">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/imageview_toolbar"

View File

@ -25,7 +25,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.FullScreenMediaActivity">
tools:context=".fullscreenfile.FullScreenMediaActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/mediaview_toolbar"

View File

@ -27,7 +27,7 @@
android:layout_height="match_parent"
android:background="@color/bg_default"
android:orientation="vertical"
tools:context=".activities.FullScreenTextViewerActivity">
tools:context=".fullscreenfile.FullScreenTextViewerActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"

View File

@ -468,7 +468,7 @@
android:contentDescription="@null"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/zero"
android:src="@drawable/ic_baseline_arrow_downward_24px"
android:src="@drawable/baseline_download_24"
app:tint="@color/high_emphasis_menu_icon" />
<androidx.appcompat.widget.AppCompatTextView

View File

@ -8,6 +8,7 @@
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="colorSurface">@color/bg_default</item>
</style>
<style name="FullScreenMediaTheme" parent="Theme.AppCompat.Light.NoActionBar">
@ -17,5 +18,6 @@
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="colorSurface">@color/bg_default</item>
</style>
</resources>
</resources>

View File

@ -51,7 +51,6 @@ How to translate with transifex:
<string name="nc_common_error_sorry">Sorry, something went wrong!</string>
<string name="nc_common_create">Create</string>
<!-- Bottom Navigation -->
<string name="nc_settings">Settings</string>
@ -519,14 +518,14 @@ How to translate with transifex:
<string name="nc_phone_book_integration_chat_via">Chat via %s</string>
<string name="nc_phone_book_integration_account_not_found">Account not found</string>
//save feature
<!-- save feature -->
<string name="nc_save_message">Save</string>
<string name="nc_dialog_save_to_storage_title">Save to storage?</string>
<string name="nc_dialog_save_to_storage_content">Saving this media to storage will allow any other apps on your device to access it.\nContinue?</string>
<string name="nc_dialog_save_to_storage_content">Saving this media to storage will allow any other apps on your device to access it.</string>
<string name="nc_dialog_save_to_storage_continue">Continue?</string>
<string name="nc_dialog_save_to_storage_yes">Yes</string>
<string name="nc_dialog_save_to_storage_no">No</string>
<string name="nc_save_success">Saved successfully</string>
<string name="starred">Favorite</string>
<string name="user_status">Status</string>

View File

@ -221,6 +221,7 @@
<item name="android:statusBarColor">@color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">true</item>
<item name="colorSurface">@color/bg_default</item>
</style>
<style name="FullScreenMediaTheme" parent="Theme.AppCompat.Light.NoActionBar">
@ -229,6 +230,7 @@
<item name="android:statusBarColor">@color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">true</item>
<item name="colorSurface">@color/bg_default</item>
</style>
<style name="TextInputLayoutTheme" parent="Theme.AppCompat">