update AndroidX, use ActivityResultContracts (#2170)

* update AndroidX, use ActivityResultContracts

* make allowMultiple setable in PickMediaFiles

* add license headers to PickMediaFiles
This commit is contained in:
Konrad Pozniak 2021-05-22 17:50:08 +02:00 committed by GitHub
parent d2cdaae129
commit ca5c455881
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 165 additions and 143 deletions

View file

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

View file

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

View file

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

View file

@ -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()
}
}