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.retrofitVersion = '2.9.0'
ext.okhttpVersion = '4.9.1'
@ -97,16 +97,16 @@ ext.materialdrawerVersion = '8.2.0'
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.3.2"
implementation "androidx.appcompat:appcompat:1.2.0"
implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation "androidx.core:core-ktx:1.5.0"
implementation "androidx.appcompat:appcompat:1.3.0"
implementation "androidx.fragment:fragment-ktx:1.3.3"
implementation "androidx.browser:browser:1.3.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.recyclerview:recyclerview:1.2.0"
implementation "androidx.exifinterface:exifinterface:1.3.2"
implementation "androidx.cardview:cardview:1.0.0"
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-appcompat:1.1.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
@ -116,7 +116,7 @@ dependencies {
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
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-rxjava3:$roomVersion"
kapt "androidx.room:room-compiler:$roomVersion"

View file

@ -199,6 +199,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requesters.containsKey(requestCode)) {
PermissionRequester requester = requesters.remove(requestCode);
requester.onRequestPermissionsResult(permissions, grantResults);

View file

@ -16,7 +16,6 @@
package com.keylesspalace.tusky.components.compose
import android.Manifest
import android.app.Activity
import android.app.ProgressDialog
import android.content.Context
import android.content.Intent
@ -28,13 +27,13 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.provider.MediaStore
import android.util.Log
import android.view.KeyEvent
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.annotation.ColorInt
import androidx.annotation.StringRes
@ -114,6 +113,21 @@ class ComposeActivity : BaseActivity(),
private val maxUploadMediaNumber = 4
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?) {
super.onCreate(savedInstanceState)
@ -605,7 +619,7 @@ class ComposeActivity : BaseActivity(),
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE)
} else {
initiateMediaPicking()
pickMediaFile.launch(true)
}
}
}
@ -755,20 +769,20 @@ class ComposeActivity : BaseActivity(),
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
grantResults: IntArray) {
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initiateMediaPicking()
pickMediaFile.launch(true)
} 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 {
}
bar.setAction(R.string.action_retry) { onMediaPick() }
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()
view.elevation = resources.getDimension(R.dimen.compose_activity_snackbar_elevation)
show()
}
}
}
}
@ -776,11 +790,6 @@ class ComposeActivity : BaseActivity(),
private fun initiateCameraApp() {
addMediaBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
// We don't need to ask for permission in this case, because the used calls require
// android.permission.WRITE_EXTERNAL_STORAGE only on SDKs *older* than Kitkat, which was
// way before permission dialogues have been introduced.
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (intent.resolveActivity(packageManager) != null) {
val photoFile: File = try {
createNewImageFile(this)
} catch (ex: IOException) {
@ -792,20 +801,7 @@ class ComposeActivity : BaseActivity(),
photoUploadUri = FileProvider.getUriForFile(this,
BuildConfig.APPLICATION_ID + ".fileprovider",
photoFile)
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUploadUri)
startActivityForResult(intent, MEDIA_TAKE_PHOTO_RESULT)
}
}
private fun initiateMediaPicking() {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
val mimeTypes = arrayOf("image/*", "video/*", "audio/*")
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
startActivityForResult(intent, MEDIA_PICK_RESULT)
takePicture.launch(photoUploadUri)
}
private fun enableButton(button: ImageButton, clickable: Boolean, colorActive: Boolean) {
@ -828,31 +824,6 @@ class ComposeActivity : BaseActivity(),
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) {
withLifecycleContext {
viewModel.pickMedia(uri).observe { exceptionOrItem ->
@ -1034,8 +1005,6 @@ class ComposeActivity : BaseActivity(),
companion object {
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
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()
}
}