update AndroidX, use ActivityResultContracts (#2170)
* update AndroidX, use ActivityResultContracts * make allowMultiple setable in PickMediaFiles * add license headers to PickMediaFiles
This commit is contained in:
parent
d2cdaae129
commit
ca5c455881
4 changed files with 165 additions and 143 deletions
|
@ -85,7 +85,7 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ext.lifecycleVersion = "2.2.0"
|
ext.lifecycleVersion = "2.3.1"
|
||||||
ext.roomVersion = '2.3.0'
|
ext.roomVersion = '2.3.0'
|
||||||
ext.retrofitVersion = '2.9.0'
|
ext.retrofitVersion = '2.9.0'
|
||||||
ext.okhttpVersion = '4.9.1'
|
ext.okhttpVersion = '4.9.1'
|
||||||
|
@ -97,16 +97,16 @@ ext.materialdrawerVersion = '8.2.0'
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
|
|
||||||
implementation "androidx.core:core-ktx:1.3.2"
|
implementation "androidx.core:core-ktx:1.5.0"
|
||||||
implementation "androidx.appcompat:appcompat:1.2.0"
|
implementation "androidx.appcompat:appcompat:1.3.0"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.2.5"
|
implementation "androidx.fragment:fragment-ktx:1.3.3"
|
||||||
implementation "androidx.browser:browser:1.3.0"
|
implementation "androidx.browser:browser:1.3.0"
|
||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||||
implementation "androidx.recyclerview:recyclerview:1.2.0"
|
implementation "androidx.recyclerview:recyclerview:1.2.0"
|
||||||
implementation "androidx.exifinterface:exifinterface:1.3.2"
|
implementation "androidx.exifinterface:exifinterface:1.3.2"
|
||||||
implementation "androidx.cardview:cardview:1.0.0"
|
implementation "androidx.cardview:cardview:1.0.0"
|
||||||
implementation "androidx.preference:preference-ktx:1.1.1"
|
implementation "androidx.preference:preference-ktx:1.1.1"
|
||||||
implementation "androidx.sharetarget:sharetarget:1.0.0"
|
implementation "androidx.sharetarget:sharetarget:1.1.0"
|
||||||
implementation "androidx.emoji:emoji:1.1.0"
|
implementation "androidx.emoji:emoji:1.1.0"
|
||||||
implementation "androidx.emoji:emoji-appcompat:1.1.0"
|
implementation "androidx.emoji:emoji-appcompat:1.1.0"
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
|
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
|
||||||
|
@ -116,7 +116,7 @@ dependencies {
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||||
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
|
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
|
||||||
implementation "androidx.viewpager2:viewpager2:1.0.0"
|
implementation "androidx.viewpager2:viewpager2:1.0.0"
|
||||||
implementation "androidx.work:work-runtime:2.4.0"
|
implementation "androidx.work:work-runtime:2.5.0"
|
||||||
implementation "androidx.room:room-runtime:$roomVersion"
|
implementation "androidx.room:room-runtime:$roomVersion"
|
||||||
implementation "androidx.room:room-rxjava3:$roomVersion"
|
implementation "androidx.room:room-rxjava3:$roomVersion"
|
||||||
kapt "androidx.room:room-compiler:$roomVersion"
|
kapt "androidx.room:room-compiler:$roomVersion"
|
||||||
|
|
|
@ -199,6 +199,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
if (requesters.containsKey(requestCode)) {
|
if (requesters.containsKey(requestCode)) {
|
||||||
PermissionRequester requester = requesters.remove(requestCode);
|
PermissionRequester requester = requesters.remove(requestCode);
|
||||||
requester.onRequestPermissionsResult(permissions, grantResults);
|
requester.onRequestPermissionsResult(permissions, grantResults);
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
package com.keylesspalace.tusky.components.compose
|
package com.keylesspalace.tusky.components.compose
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.Activity
|
|
||||||
import android.app.ProgressDialog
|
import android.app.ProgressDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -28,13 +27,13 @@ import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.provider.MediaStore
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
@ -85,12 +84,12 @@ import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class ComposeActivity : BaseActivity(),
|
class ComposeActivity : BaseActivity(),
|
||||||
ComposeOptionsListener,
|
ComposeOptionsListener,
|
||||||
ComposeAutoCompleteAdapter.AutocompletionProvider,
|
ComposeAutoCompleteAdapter.AutocompletionProvider,
|
||||||
OnEmojiSelectedListener,
|
OnEmojiSelectedListener,
|
||||||
Injectable,
|
Injectable,
|
||||||
InputConnectionCompat.OnCommitContentListener,
|
InputConnectionCompat.OnCommitContentListener,
|
||||||
ComposeScheduleView.OnTimeSetListener {
|
ComposeScheduleView.OnTimeSetListener {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var viewModelFactory: ViewModelFactory
|
lateinit var viewModelFactory: ViewModelFactory
|
||||||
|
@ -114,6 +113,21 @@ class ComposeActivity : BaseActivity(),
|
||||||
private val maxUploadMediaNumber = 4
|
private val maxUploadMediaNumber = 4
|
||||||
private var mediaCount = 0
|
private var mediaCount = 0
|
||||||
|
|
||||||
|
private val takePicture = registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
|
||||||
|
if (success) {
|
||||||
|
pickMedia(photoUploadUri!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private val pickMediaFile = registerForActivityResult(PickMediaFiles()) { uris ->
|
||||||
|
if (mediaCount + uris.size > maxUploadMediaNumber) {
|
||||||
|
Toast.makeText(this, resources.getQuantityString(R.plurals.error_upload_max_media_reached, maxUploadMediaNumber, maxUploadMediaNumber), Toast.LENGTH_SHORT).show()
|
||||||
|
} else {
|
||||||
|
uris.forEach { uri ->
|
||||||
|
pickMedia(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@ -130,16 +144,16 @@ class ComposeActivity : BaseActivity(),
|
||||||
|
|
||||||
setupAvatar(preferences, activeAccount)
|
setupAvatar(preferences, activeAccount)
|
||||||
val mediaAdapter = MediaPreviewAdapter(
|
val mediaAdapter = MediaPreviewAdapter(
|
||||||
this,
|
this,
|
||||||
onAddCaption = { item ->
|
onAddCaption = { item ->
|
||||||
makeCaptionDialog(item.description, item.uri) { newDescription ->
|
makeCaptionDialog(item.description, item.uri) { newDescription ->
|
||||||
viewModel.updateDescription(item.localId, newDescription)
|
viewModel.updateDescription(item.localId, newDescription)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRemove = this::removeMediaFromQueue
|
onRemove = this::removeMediaFromQueue
|
||||||
)
|
)
|
||||||
binding.composeMediaPreviewBar.layoutManager =
|
binding.composeMediaPreviewBar.layoutManager =
|
||||||
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
|
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
|
||||||
binding.composeMediaPreviewBar.adapter = mediaAdapter
|
binding.composeMediaPreviewBar.adapter = mediaAdapter
|
||||||
binding.composeMediaPreviewBar.itemAnimator = null
|
binding.composeMediaPreviewBar.itemAnimator = null
|
||||||
|
|
||||||
|
@ -255,11 +269,11 @@ class ComposeActivity : BaseActivity(),
|
||||||
binding.composeEditField.setOnKeyListener { _, keyCode, event -> this.onKeyDown(keyCode, event) }
|
binding.composeEditField.setOnKeyListener { _, keyCode, event -> this.onKeyDown(keyCode, event) }
|
||||||
|
|
||||||
binding.composeEditField.setAdapter(
|
binding.composeEditField.setAdapter(
|
||||||
ComposeAutoCompleteAdapter(
|
ComposeAutoCompleteAdapter(
|
||||||
this,
|
this,
|
||||||
preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
|
preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
|
||||||
preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
binding.composeEditField.setTokenizer(ComposeTokenizer())
|
binding.composeEditField.setTokenizer(ComposeTokenizer())
|
||||||
|
|
||||||
|
@ -275,7 +289,7 @@ class ComposeActivity : BaseActivity(),
|
||||||
|
|
||||||
// work around Android platform bug -> https://issuetracker.google.com/issues/67102093
|
// work around Android platform bug -> https://issuetracker.google.com/issues/67102093
|
||||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O
|
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O
|
||||||
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1) {
|
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1) {
|
||||||
binding.composeEditField.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
|
binding.composeEditField.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -390,13 +404,13 @@ class ComposeActivity : BaseActivity(),
|
||||||
|
|
||||||
val animateAvatars = preferences.getBoolean("animateGifAvatars", false)
|
val animateAvatars = preferences.getBoolean("animateGifAvatars", false)
|
||||||
loadAvatar(
|
loadAvatar(
|
||||||
activeAccount.profilePictureUrl,
|
activeAccount.profilePictureUrl,
|
||||||
binding.composeAvatar,
|
binding.composeAvatar,
|
||||||
avatarSize / 8,
|
avatarSize / 8,
|
||||||
animateAvatars
|
animateAvatars
|
||||||
)
|
)
|
||||||
binding.composeAvatar.contentDescription = getString(R.string.compose_active_account_description,
|
binding.composeAvatar.contentDescription = getString(R.string.compose_active_account_description,
|
||||||
activeAccount.fullName)
|
activeAccount.fullName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun replaceTextAtCaret(text: CharSequence) {
|
private fun replaceTextAtCaret(text: CharSequence) {
|
||||||
|
@ -602,10 +616,10 @@ class ComposeActivity : BaseActivity(),
|
||||||
addMediaBehavior.removeBottomSheetCallback(this)
|
addMediaBehavior.removeBottomSheetCallback(this)
|
||||||
if (ContextCompat.checkSelfPermission(this@ComposeActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
if (ContextCompat.checkSelfPermission(this@ComposeActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||||
ActivityCompat.requestPermissions(this@ComposeActivity,
|
ActivityCompat.requestPermissions(this@ComposeActivity,
|
||||||
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
|
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
|
||||||
PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE)
|
PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE)
|
||||||
} else {
|
} else {
|
||||||
initiateMediaPicking()
|
pickMediaFile.launch(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -620,7 +634,7 @@ class ComposeActivity : BaseActivity(),
|
||||||
addMediaBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
addMediaBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||||
val instanceParams = viewModel.instanceParams.value!!
|
val instanceParams = viewModel.instanceParams.value!!
|
||||||
showAddPollDialog(this, viewModel.poll.value, instanceParams.pollMaxOptions,
|
showAddPollDialog(this, viewModel.poll.value, instanceParams.pollMaxOptions,
|
||||||
instanceParams.pollMaxLength, viewModel::updatePoll)
|
instanceParams.pollMaxLength, viewModel::updatePoll)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupPollView() {
|
private fun setupPollView() {
|
||||||
|
@ -740,8 +754,8 @@ class ComposeActivity : BaseActivity(),
|
||||||
} else if (characterCount <= maximumTootCharacters) {
|
} else if (characterCount <= maximumTootCharacters) {
|
||||||
if (viewModel.media.value!!.isNotEmpty()) {
|
if (viewModel.media.value!!.isNotEmpty()) {
|
||||||
finishingUploadDialog = ProgressDialog.show(
|
finishingUploadDialog = ProgressDialog.show(
|
||||||
this, getString(R.string.dialog_title_finishing_media_upload),
|
this, getString(R.string.dialog_title_finishing_media_upload),
|
||||||
getString(R.string.dialog_message_uploading_media), true, true)
|
getString(R.string.dialog_message_uploading_media), true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.sendStatus(contentText, spoilerText).observe(this, {
|
viewModel.sendStatus(contentText, spoilerText).observe(this, {
|
||||||
|
@ -755,20 +769,20 @@ class ComposeActivity : BaseActivity(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||||
grantResults: IntArray) {
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
|
||||||
if (requestCode == PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) {
|
if (requestCode == PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) {
|
||||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
initiateMediaPicking()
|
pickMediaFile.launch(true)
|
||||||
} else {
|
} else {
|
||||||
val bar = Snackbar.make(binding.activityCompose, R.string.error_media_upload_permission,
|
Snackbar.make(binding.activityCompose, R.string.error_media_upload_permission,
|
||||||
Snackbar.LENGTH_SHORT).apply {
|
Snackbar.LENGTH_SHORT).apply {
|
||||||
|
setAction(R.string.action_retry) { onMediaPick() }
|
||||||
|
//necessary so snackbar is shown over everything
|
||||||
|
view.elevation = resources.getDimension(R.dimen.compose_activity_snackbar_elevation)
|
||||||
|
show()
|
||||||
}
|
}
|
||||||
bar.setAction(R.string.action_retry) { onMediaPick() }
|
|
||||||
//necessary so snackbar is shown over everything
|
|
||||||
bar.view.elevation = resources.getDimension(R.dimen.compose_activity_snackbar_elevation)
|
|
||||||
bar.show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -776,50 +790,32 @@ class ComposeActivity : BaseActivity(),
|
||||||
private fun initiateCameraApp() {
|
private fun initiateCameraApp() {
|
||||||
addMediaBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
addMediaBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||||
|
|
||||||
// We don't need to ask for permission in this case, because the used calls require
|
val photoFile: File = try {
|
||||||
// android.permission.WRITE_EXTERNAL_STORAGE only on SDKs *older* than Kitkat, which was
|
createNewImageFile(this)
|
||||||
// way before permission dialogues have been introduced.
|
} catch (ex: IOException) {
|
||||||
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
displayTransientError(R.string.error_media_upload_opening)
|
||||||
if (intent.resolveActivity(packageManager) != null) {
|
return
|
||||||
val photoFile: File = try {
|
|
||||||
createNewImageFile(this)
|
|
||||||
} catch (ex: IOException) {
|
|
||||||
displayTransientError(R.string.error_media_upload_opening)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue only if the File was successfully created
|
|
||||||
photoUploadUri = FileProvider.getUriForFile(this,
|
|
||||||
BuildConfig.APPLICATION_ID + ".fileprovider",
|
|
||||||
photoFile)
|
|
||||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUploadUri)
|
|
||||||
startActivityForResult(intent, MEDIA_TAKE_PHOTO_RESULT)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun initiateMediaPicking() {
|
// Continue only if the File was successfully created
|
||||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
photoUploadUri = FileProvider.getUriForFile(this,
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
BuildConfig.APPLICATION_ID + ".fileprovider",
|
||||||
|
photoFile)
|
||||||
val mimeTypes = arrayOf("image/*", "video/*", "audio/*")
|
takePicture.launch(photoUploadUri)
|
||||||
intent.type = "*/*"
|
|
||||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
|
|
||||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
|
||||||
startActivityForResult(intent, MEDIA_PICK_RESULT)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enableButton(button: ImageButton, clickable: Boolean, colorActive: Boolean) {
|
private fun enableButton(button: ImageButton, clickable: Boolean, colorActive: Boolean) {
|
||||||
button.isEnabled = clickable
|
button.isEnabled = clickable
|
||||||
ThemeUtils.setDrawableTint(this, button.drawable,
|
ThemeUtils.setDrawableTint(this, button.drawable,
|
||||||
if (colorActive) android.R.attr.textColorTertiary
|
if (colorActive) android.R.attr.textColorTertiary
|
||||||
else R.attr.textColorDisabled)
|
else R.attr.textColorDisabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enablePollButton(enable: Boolean) {
|
private fun enablePollButton(enable: Boolean) {
|
||||||
binding.addPollTextActionTextView.isEnabled = enable
|
binding.addPollTextActionTextView.isEnabled = enable
|
||||||
val textColor = ThemeUtils.getColor(this,
|
val textColor = ThemeUtils.getColor(this,
|
||||||
if (enable) android.R.attr.textColorTertiary
|
if (enable) android.R.attr.textColorTertiary
|
||||||
else R.attr.textColorDisabled)
|
else R.attr.textColorDisabled)
|
||||||
binding.addPollTextActionTextView.setTextColor(textColor)
|
binding.addPollTextActionTextView.setTextColor(textColor)
|
||||||
binding.addPollTextActionTextView.compoundDrawablesRelative[0].colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)
|
binding.addPollTextActionTextView.compoundDrawablesRelative[0].colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)
|
||||||
}
|
}
|
||||||
|
@ -828,31 +824,6 @@ class ComposeActivity : BaseActivity(),
|
||||||
viewModel.removeMediaFromQueue(item)
|
viewModel.removeMediaFromQueue(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, intent)
|
|
||||||
if (resultCode == Activity.RESULT_OK && requestCode == MEDIA_PICK_RESULT && intent != null) {
|
|
||||||
if (intent.data != null) {
|
|
||||||
// Single media, upload it and done.
|
|
||||||
pickMedia(intent.data!!)
|
|
||||||
} else if (intent.clipData != null) {
|
|
||||||
val clipData = intent.clipData!!
|
|
||||||
val count = clipData.itemCount
|
|
||||||
if (mediaCount + count > maxUploadMediaNumber) {
|
|
||||||
// check if exist media + upcoming media > 4, then prob error message.
|
|
||||||
Toast.makeText(this, resources.getQuantityString(R.plurals.error_upload_max_media_reached, maxUploadMediaNumber, maxUploadMediaNumber), Toast.LENGTH_SHORT).show()
|
|
||||||
} else {
|
|
||||||
// if not grater then 4, upload all multiple media.
|
|
||||||
for (i in 0 until count) {
|
|
||||||
val imageUri = clipData.getItemAt(i).getUri()
|
|
||||||
pickMedia(imageUri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (resultCode == Activity.RESULT_OK && requestCode == MEDIA_TAKE_PHOTO_RESULT) {
|
|
||||||
pickMedia(photoUploadUri!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun pickMedia(uri: Uri, contentInfoCompat: InputContentInfoCompat? = null) {
|
private fun pickMedia(uri: Uri, contentInfoCompat: InputContentInfoCompat? = null) {
|
||||||
withLifecycleContext {
|
withLifecycleContext {
|
||||||
viewModel.pickMedia(uri).observe { exceptionOrItem ->
|
viewModel.pickMedia(uri).observe { exceptionOrItem ->
|
||||||
|
@ -908,9 +879,9 @@ class ComposeActivity : BaseActivity(),
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
// Acting like a teen: deliberately ignoring parent.
|
// Acting like a teen: deliberately ignoring parent.
|
||||||
if (composeOptionsBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
|
if (composeOptionsBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
|
||||||
addMediaBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
|
addMediaBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
|
||||||
emojiBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
|
emojiBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
|
||||||
scheduleBehavior.state == BottomSheetBehavior.STATE_EXPANDED) {
|
scheduleBehavior.state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||||
composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||||
addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||||
emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||||
|
@ -945,12 +916,12 @@ class ComposeActivity : BaseActivity(),
|
||||||
val contentWarning = binding.composeContentWarningField.text.toString()
|
val contentWarning = binding.composeContentWarningField.text.toString()
|
||||||
if (viewModel.didChange(contentText, contentWarning)) {
|
if (viewModel.didChange(contentText, contentWarning)) {
|
||||||
AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
.setMessage(R.string.compose_save_draft)
|
.setMessage(R.string.compose_save_draft)
|
||||||
.setPositiveButton(R.string.action_save) { _, _ ->
|
.setPositiveButton(R.string.action_save) { _, _ ->
|
||||||
saveDraftAndFinish(contentText, contentWarning)
|
saveDraftAndFinish(contentText, contentWarning)
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.action_delete) { _, _ -> deleteDraftAndFinish() }
|
.setNegativeButton(R.string.action_delete) { _, _ -> deleteDraftAndFinish() }
|
||||||
.show()
|
.show()
|
||||||
} else {
|
} else {
|
||||||
finishWithoutSlideOutAnimation()
|
finishWithoutSlideOutAnimation()
|
||||||
}
|
}
|
||||||
|
@ -982,13 +953,13 @@ class ComposeActivity : BaseActivity(),
|
||||||
}
|
}
|
||||||
|
|
||||||
data class QueuedMedia(
|
data class QueuedMedia(
|
||||||
val localId: Long,
|
val localId: Long,
|
||||||
val uri: Uri,
|
val uri: Uri,
|
||||||
val type: Type,
|
val type: Type,
|
||||||
val mediaSize: Long,
|
val mediaSize: Long,
|
||||||
val uploadPercent: Int = 0,
|
val uploadPercent: Int = 0,
|
||||||
val id: String? = null,
|
val id: String? = null,
|
||||||
val description: String? = null
|
val description: String? = null
|
||||||
) {
|
) {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
IMAGE, VIDEO, AUDIO;
|
IMAGE, VIDEO, AUDIO;
|
||||||
|
@ -1011,31 +982,29 @@ class ComposeActivity : BaseActivity(),
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class ComposeOptions(
|
data class ComposeOptions(
|
||||||
// Let's keep fields var until all consumers are Kotlin
|
// Let's keep fields var until all consumers are Kotlin
|
||||||
var scheduledTootId: String? = null,
|
var scheduledTootId: String? = null,
|
||||||
var draftId: Int? = null,
|
var draftId: Int? = null,
|
||||||
var tootText: String? = null,
|
var tootText: String? = null,
|
||||||
var mediaUrls: List<String>? = null,
|
var mediaUrls: List<String>? = null,
|
||||||
var mediaDescriptions: List<String>? = null,
|
var mediaDescriptions: List<String>? = null,
|
||||||
var mentionedUsernames: Set<String>? = null,
|
var mentionedUsernames: Set<String>? = null,
|
||||||
var inReplyToId: String? = null,
|
var inReplyToId: String? = null,
|
||||||
var replyVisibility: Status.Visibility? = null,
|
var replyVisibility: Status.Visibility? = null,
|
||||||
var visibility: Status.Visibility? = null,
|
var visibility: Status.Visibility? = null,
|
||||||
var contentWarning: String? = null,
|
var contentWarning: String? = null,
|
||||||
var replyingStatusAuthor: String? = null,
|
var replyingStatusAuthor: String? = null,
|
||||||
var replyingStatusContent: String? = null,
|
var replyingStatusContent: String? = null,
|
||||||
var mediaAttachments: List<Attachment>? = null,
|
var mediaAttachments: List<Attachment>? = null,
|
||||||
var draftAttachments: List<DraftAttachment>? = null,
|
var draftAttachments: List<DraftAttachment>? = null,
|
||||||
var scheduledAt: String? = null,
|
var scheduledAt: String? = null,
|
||||||
var sensitive: Boolean? = null,
|
var sensitive: Boolean? = null,
|
||||||
var poll: NewPoll? = null,
|
var poll: NewPoll? = null,
|
||||||
var modifiedInitialState: Boolean? = null
|
var modifiedInitialState: Boolean? = null
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "ComposeActivity" // logging tag
|
private const val TAG = "ComposeActivity" // logging tag
|
||||||
private const val MEDIA_PICK_RESULT = 1
|
|
||||||
private const val MEDIA_TAKE_PHOTO_RESULT = 2
|
|
||||||
private const val PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1
|
private const val PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1
|
||||||
|
|
||||||
internal const val COMPOSE_OPTIONS_EXTRA = "COMPOSE_OPTIONS"
|
internal const val COMPOSE_OPTIONS_EXTRA = "COMPOSE_OPTIONS"
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/* Copyright 2021 Tusky Contributors
|
||||||
|
*
|
||||||
|
* This file is a part of Tusky.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Tusky 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 Tusky; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.util
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
|
|
||||||
|
class PickMediaFiles : ActivityResultContract<Boolean, List<Uri>>() {
|
||||||
|
override fun createIntent(context: Context, allowMultiple: Boolean): Intent {
|
||||||
|
return Intent(Intent.ACTION_GET_CONTENT)
|
||||||
|
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
.setType("*/*")
|
||||||
|
.apply {
|
||||||
|
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*", "audio/*"))
|
||||||
|
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseResult(resultCode: Int, intent: Intent?): List<Uri> {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
val intentData = intent?.data
|
||||||
|
val clipData = intent?.clipData
|
||||||
|
if (intentData != null) {
|
||||||
|
// Single media, upload it and done.
|
||||||
|
return listOf(intentData)
|
||||||
|
} else if (clipData != null) {
|
||||||
|
val result: MutableList<Uri> = mutableListOf()
|
||||||
|
for (i in 0 until clipData.itemCount) {
|
||||||
|
result.add(clipData.getItemAt(i).uri)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue