mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-22 21:19:31 +01:00
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:
parent
2f24c130ca
commit
640007b421
@ -152,17 +152,17 @@
|
|||||||
android:theme="@style/AppTheme.CallLauncher" />
|
android:theme="@style/AppTheme.CallLauncher" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.FullScreenImageActivity"
|
android:name=".fullscreenfile.FullScreenImageActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||||
android:theme="@style/FullScreenImageTheme"/>
|
android:theme="@style/FullScreenImageTheme"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.FullScreenMediaActivity"
|
android:name=".fullscreenfile.FullScreenMediaActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||||
android:theme="@style/FullScreenMediaTheme"/>
|
android:theme="@style/FullScreenMediaTheme"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.FullScreenTextViewerActivity"
|
android:name=".fullscreenfile.FullScreenTextViewerActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||||
android:theme="@style/FullScreenTextTheme"/>
|
android:theme="@style/FullScreenTextTheme"/>
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.AssetFileDescriptor
|
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.events.WebSocketCommunicationEvent
|
||||||
import com.nextcloud.talk.extensions.loadAvatarOrImagePreview
|
import com.nextcloud.talk.extensions.loadAvatarOrImagePreview
|
||||||
import com.nextcloud.talk.jobs.DownloadFileToCacheWorker
|
import com.nextcloud.talk.jobs.DownloadFileToCacheWorker
|
||||||
import com.nextcloud.talk.jobs.SaveFileToStorageWorker
|
|
||||||
import com.nextcloud.talk.jobs.ShareOperationWorker
|
import com.nextcloud.talk.jobs.ShareOperationWorker
|
||||||
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
|
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
|
||||||
import com.nextcloud.talk.location.LocationPickerActivity
|
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.AttachmentDialog
|
||||||
import com.nextcloud.talk.ui.dialog.DateTimePickerFragment
|
import com.nextcloud.talk.ui.dialog.DateTimePickerFragment
|
||||||
import com.nextcloud.talk.ui.dialog.MessageActionsDialog
|
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.dialog.ShowReactionsDialog
|
||||||
import com.nextcloud.talk.ui.recyclerview.MessageSwipeActions
|
import com.nextcloud.talk.ui.recyclerview.MessageSwipeActions
|
||||||
import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback
|
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")
|
@SuppressLint("SimpleDateFormat")
|
||||||
private fun setVoiceRecordFileName() {
|
private fun setVoiceRecordFileName() {
|
||||||
val simpleDateFormat = SimpleDateFormat(FILE_DATE_PATTERN)
|
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) {
|
private fun showSaveToStorageWarning(message: ChatMessage) {
|
||||||
val builder = AlertDialog.Builder(this)
|
val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance(
|
||||||
builder.setTitle(R.string.nc_dialog_save_to_storage_title)
|
message.selectedIndividualHashMap!!["name"]!!
|
||||||
builder.setMessage(R.string.nc_dialog_save_to_storage_content)
|
)
|
||||||
builder.setPositiveButton(R.string.nc_dialog_save_to_storage_yes) { dialog: DialogInterface, _: Int ->
|
saveFragment.show(
|
||||||
saveImage(message)
|
supportFragmentManager,
|
||||||
dialog.dismiss()
|
SaveToStorageDialogFragment.TAG
|
||||||
}
|
)
|
||||||
builder.setNegativeButton(R.string.nc_dialog_save_to_storage_no) { dialog: DialogInterface, _: Int ->
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
val dialog = builder.create()
|
|
||||||
dialog.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkIfSaveable(message: ChatMessage) {
|
fun checkIfSaveable(message: ChatMessage) {
|
||||||
@ -4608,5 +4556,6 @@ class ChatActivity :
|
|||||||
private const val TYPING_STOPPED_SIGNALING_MESSAGE_TYPE = "stoppedTyping"
|
private const val TYPING_STOPPED_SIGNALING_MESSAGE_TYPE = "stoppedTyping"
|
||||||
private const val CALL_STARTED_ID = -2
|
private const val CALL_STARTED_ID = -2
|
||||||
private const val MILISEC_15: Long = 15
|
private const val MILISEC_15: Long = 15
|
||||||
|
private const val LINEBREAK = "\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,8 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* 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.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@ -35,7 +33,6 @@ import android.view.Menu
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup.MarginLayoutParams
|
import android.view.ViewGroup.MarginLayoutParams
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
@ -44,28 +41,25 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.work.Data
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.work.OneTimeWorkRequest
|
import autodagger.AutoInjector
|
||||||
import androidx.work.WorkInfo
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.nextcloud.talk.BuildConfig
|
import com.nextcloud.talk.BuildConfig
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
import com.nextcloud.talk.databinding.ActivityFullScreenImageBinding
|
import com.nextcloud.talk.databinding.ActivityFullScreenImageBinding
|
||||||
import com.nextcloud.talk.jobs.SaveFileToStorageWorker
|
import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment
|
||||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
|
||||||
import com.nextcloud.talk.utils.BitmapShrinker
|
import com.nextcloud.talk.utils.BitmapShrinker
|
||||||
import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX_GENERIC
|
import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX_GENERIC
|
||||||
import pl.droidsonroids.gif.GifDrawable
|
import pl.droidsonroids.gif.GifDrawable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.ExecutionException
|
|
||||||
|
|
||||||
|
@AutoInjector(NextcloudTalkApplication::class)
|
||||||
class FullScreenImageActivity : AppCompatActivity() {
|
class FullScreenImageActivity : AppCompatActivity() {
|
||||||
lateinit var binding: ActivityFullScreenImageBinding
|
lateinit var binding: ActivityFullScreenImageBinding
|
||||||
private lateinit var windowInsetsController: WindowInsetsControllerCompat
|
private lateinit var windowInsetsController: WindowInsetsControllerCompat
|
||||||
private lateinit var path: String
|
private lateinit var path: String
|
||||||
private var showFullscreen = false
|
private var showFullscreen = false
|
||||||
lateinit var viewThemeUtils: ViewThemeUtils
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
menuInflater.inflate(R.menu.menu_preview, menu)
|
menuInflater.inflate(R.menu.menu_preview, menu)
|
||||||
@ -98,7 +92,13 @@ class FullScreenImageActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
R.id.save -> {
|
R.id.save -> {
|
||||||
showWarningDialog()
|
val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance(
|
||||||
|
intent.getStringExtra("FILE_NAME").toString()
|
||||||
|
)
|
||||||
|
saveFragment.show(
|
||||||
|
supportFragmentManager,
|
||||||
|
SaveToStorageDialogFragment.TAG
|
||||||
|
)
|
||||||
true
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||||
|
|
||||||
binding = ActivityFullScreenImageBinding.inflate(layoutInflater)
|
binding = ActivityFullScreenImageBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
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 {
|
companion object {
|
||||||
private const val TAG = "FullScreenImageActivity"
|
private const val TAG = "FullScreenImageActivity"
|
||||||
private const val HUNDRED_MB = 100 * 1024 * 1024
|
private const val HUNDRED_MB = 100 * 1024 * 1024
|
@ -24,7 +24,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* 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.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -43,6 +43,7 @@ import androidx.core.view.WindowInsetsControllerCompat
|
|||||||
import androidx.core.view.marginBottom
|
import androidx.core.view.marginBottom
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
@ -53,6 +54,7 @@ import com.nextcloud.talk.BuildConfig
|
|||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
import com.nextcloud.talk.databinding.ActivityFullScreenMediaBinding
|
import com.nextcloud.talk.databinding.ActivityFullScreenMediaBinding
|
||||||
|
import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment
|
||||||
import com.nextcloud.talk.utils.Mimetype.VIDEO_PREFIX_GENERIC
|
import com.nextcloud.talk.utils.Mimetype.VIDEO_PREFIX_GENERIC
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -78,6 +80,7 @@ class FullScreenMediaActivity : AppCompatActivity() {
|
|||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.share -> {
|
R.id.share -> {
|
||||||
val shareUri = FileProvider.getUriForFile(
|
val shareUri = FileProvider.getUriForFile(
|
||||||
this,
|
this,
|
||||||
@ -95,6 +98,18 @@ class FullScreenMediaActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
R.id.save -> {
|
||||||
|
val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance(
|
||||||
|
intent.getStringExtra("FILE_NAME").toString()
|
||||||
|
)
|
||||||
|
saveFragment.show(
|
||||||
|
supportFragmentManager,
|
||||||
|
SaveToStorageDialogFragment.TAG
|
||||||
|
)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
super.onOptionsItemSelected(item)
|
super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
@ -22,7 +22,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* 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.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -31,11 +31,13 @@ import android.view.MenuItem
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
import autodagger.AutoInjector
|
import autodagger.AutoInjector
|
||||||
import com.nextcloud.talk.BuildConfig
|
import com.nextcloud.talk.BuildConfig
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
import com.nextcloud.talk.databinding.ActivityFullScreenTextBinding
|
import com.nextcloud.talk.databinding.ActivityFullScreenTextBinding
|
||||||
|
import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment
|
||||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||||
import com.nextcloud.talk.utils.DisplayUtils
|
import com.nextcloud.talk.utils.DisplayUtils
|
||||||
import com.nextcloud.talk.utils.Mimetype.TEXT_PREFIX_GENERIC
|
import com.nextcloud.talk.utils.Mimetype.TEXT_PREFIX_GENERIC
|
||||||
@ -58,27 +60,44 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return if (item.itemId == android.R.id.home) {
|
return when (item.itemId) {
|
||||||
onBackPressedDispatcher.onBackPressed()
|
android.R.id.home -> {
|
||||||
true
|
onBackPressedDispatcher.onBackPressed()
|
||||||
} else if (item.itemId == R.id.share) {
|
true
|
||||||
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.share -> {
|
||||||
} else {
|
val shareUri = FileProvider.getUriForFile(
|
||||||
super.onOptionsItemSelected(item)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* Nextcloud Talk application
|
* Nextcloud Talk application
|
||||||
*
|
*
|
||||||
* @author Andy Scherzinger
|
* @author Fariba Khandani
|
||||||
* @author Marcel Hibbe
|
* @author Marcel Hibbe
|
||||||
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
* Copyright (C) 2023 Fariba Khandani <khandani@winworker.de>
|
||||||
* Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
|
* Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* 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.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.media.MediaScannerConnection
|
import android.media.MediaScannerConnection
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.provider.MediaStore.Files.FileColumns
|
import android.provider.MediaStore.Files.FileColumns
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import autodagger.AutoInjector
|
import autodagger.AutoInjector
|
||||||
|
import com.nextcloud.talk.R
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
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.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
@ -50,16 +59,22 @@ class SaveFileToStorageWorker(val context: Context, workerParameters: WorkerPara
|
|||||||
val contentResolver = context.contentResolver
|
val contentResolver = context.contentResolver
|
||||||
val mimeType = URLConnection.guessContentTypeFromName(cacheFile.name)
|
val mimeType = URLConnection.guessContentTypeFromName(cacheFile.name)
|
||||||
|
|
||||||
|
val appName = applicationContext.resources!!.getString(R.string.nc_app_product_name)
|
||||||
|
|
||||||
val values = ContentValues().apply {
|
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.DISPLAY_NAME, cacheFile.name)
|
||||||
put(FileColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
|
|
||||||
if (mimeType != null) {
|
if (mimeType != null) {
|
||||||
put(FileColumns.MIME_TYPE, URLConnection.guessContentTypeFromName(cacheFile.name))
|
put(FileColumns.MIME_TYPE, mimeType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val collection = MediaStore.Files.getContentUri("external")
|
val collectionUri = getUriByType(mimeType)
|
||||||
val uri = contentResolver.insert(collection, values)
|
|
||||||
|
val uri = contentResolver.insert(collectionUri, values)
|
||||||
|
|
||||||
uri?.let { fileUri ->
|
uri?.let { fileUri ->
|
||||||
try {
|
try {
|
||||||
@ -79,16 +94,53 @@ class SaveFileToStorageWorker(val context: Context, workerParameters: WorkerPara
|
|||||||
// Notify the media scanner about the new file
|
// Notify the media scanner about the new file
|
||||||
MediaScannerConnection.scanFile(context, arrayOf(cacheFile.absolutePath), null, null)
|
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()
|
return Result.success()
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e(TAG, "Something went wrong when trying to save file to internal storage", e)
|
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()
|
return Result.failure()
|
||||||
} catch (e: NullPointerException) {
|
} catch (e: NullPointerException) {
|
||||||
Log.e(TAG, "Something went wrong when trying to save file to internal storage", e)
|
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()
|
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 {
|
companion object {
|
||||||
private val TAG = SaveFileToStorageWorker::class.java.simpleName
|
private val TAG = SaveFileToStorageWorker::class.java.simpleName
|
||||||
const val KEY_FILE_NAME = "KEY_FILE_NAME"
|
const val KEY_FILE_NAME = "KEY_FILE_NAME"
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,9 +37,9 @@ import androidx.work.WorkInfo
|
|||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
import com.nextcloud.talk.activities.FullScreenImageActivity
|
import com.nextcloud.talk.fullscreenfile.FullScreenImageActivity
|
||||||
import com.nextcloud.talk.activities.FullScreenMediaActivity
|
import com.nextcloud.talk.fullscreenfile.FullScreenMediaActivity
|
||||||
import com.nextcloud.talk.activities.FullScreenTextViewerActivity
|
import com.nextcloud.talk.fullscreenfile.FullScreenTextViewerActivity
|
||||||
import com.nextcloud.talk.adapters.messages.PreviewMessageViewHolder
|
import com.nextcloud.talk.adapters.messages.PreviewMessageViewHolder
|
||||||
import com.nextcloud.talk.data.user.model.User
|
import com.nextcloud.talk.data.user.model.User
|
||||||
import com.nextcloud.talk.jobs.DownloadFileToCacheWorker
|
import com.nextcloud.talk.jobs.DownloadFileToCacheWorker
|
||||||
|
22
app/src/main/res/drawable/baseline_download_24.xml
Normal file
22
app/src/main/res/drawable/baseline_download_24.xml
Normal 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>
|
@ -28,7 +28,7 @@
|
|||||||
android:id="@+id/image_wrapper_view"
|
android:id="@+id/image_wrapper_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".activities.FullScreenImageActivity">
|
tools:context=".fullscreenfile.FullScreenImageActivity">
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@+id/imageview_toolbar"
|
android:id="@+id/imageview_toolbar"
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".activities.FullScreenMediaActivity">
|
tools:context=".fullscreenfile.FullScreenMediaActivity">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/mediaview_toolbar"
|
android:id="@+id/mediaview_toolbar"
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/bg_default"
|
android:background="@color/bg_default"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context=".activities.FullScreenTextViewerActivity">
|
tools:context=".fullscreenfile.FullScreenTextViewerActivity">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -468,7 +468,7 @@
|
|||||||
android:contentDescription="@null"
|
android:contentDescription="@null"
|
||||||
android:paddingStart="@dimen/standard_padding"
|
android:paddingStart="@dimen/standard_padding"
|
||||||
android:paddingEnd="@dimen/zero"
|
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" />
|
app:tint="@color/high_emphasis_menu_icon" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
<item name="android:windowActionBar">true</item>
|
<item name="android:windowActionBar">true</item>
|
||||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||||
|
<item name="colorSurface">@color/bg_default</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="FullScreenMediaTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
<style name="FullScreenMediaTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
@ -17,5 +18,6 @@
|
|||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
<item name="android:windowActionBar">true</item>
|
<item name="android:windowActionBar">true</item>
|
||||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||||
|
<item name="colorSurface">@color/bg_default</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
@ -51,7 +51,6 @@ How to translate with transifex:
|
|||||||
<string name="nc_common_error_sorry">Sorry, something went wrong!</string>
|
<string name="nc_common_error_sorry">Sorry, something went wrong!</string>
|
||||||
<string name="nc_common_create">Create</string>
|
<string name="nc_common_create">Create</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- Bottom Navigation -->
|
<!-- Bottom Navigation -->
|
||||||
<string name="nc_settings">Settings</string>
|
<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_chat_via">Chat via %s</string>
|
||||||
<string name="nc_phone_book_integration_account_not_found">Account not found</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_save_message">Save</string>
|
||||||
<string name="nc_dialog_save_to_storage_title">Save to storage?</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_yes">Yes</string>
|
||||||
<string name="nc_dialog_save_to_storage_no">No</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="starred">Favorite</string>
|
||||||
<string name="user_status">Status</string>
|
<string name="user_status">Status</string>
|
||||||
|
@ -221,6 +221,7 @@
|
|||||||
<item name="android:statusBarColor">@color/transparent</item>
|
<item name="android:statusBarColor">@color/transparent</item>
|
||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
<item name="android:windowActionBar">true</item>
|
<item name="android:windowActionBar">true</item>
|
||||||
|
<item name="colorSurface">@color/bg_default</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="FullScreenMediaTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
<style name="FullScreenMediaTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
@ -229,6 +230,7 @@
|
|||||||
<item name="android:statusBarColor">@color/transparent</item>
|
<item name="android:statusBarColor">@color/transparent</item>
|
||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
<item name="android:windowActionBar">true</item>
|
<item name="android:windowActionBar">true</item>
|
||||||
|
<item name="colorSurface">@color/bg_default</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="TextInputLayoutTheme" parent="Theme.AppCompat">
|
<style name="TextInputLayoutTheme" parent="Theme.AppCompat">
|
||||||
|
Loading…
Reference in New Issue
Block a user