Merge pull request #3752 from nextcloud/new-result-apis

Use new Result APIs instead of startActivityForResult() and onActivityResult().
This commit is contained in:
Sowjanya Kota 2024-04-03 08:51:19 +02:00 committed by GitHub
commit 4c7ddae084
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 339 additions and 253 deletions

View file

@ -1,6 +1,7 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2024 Parneet Singh <gurayaparneet@gmail.com>
* SPDX-FileCopyrightText: 2024 Giacomo Pacini <giacomo@paciosoft.com>
* SPDX-FileCopyrightText: 2023 Ezhil Shanmugham <ezhil56x.contact@gmail.com>
* SPDX-FileCopyrightText: 2021-2022 Marcel Hibbe <dev@mhibbe.de>
@ -9,11 +10,13 @@
* SPDX-FileCopyrightText: 2017-2019 Mario Danic <mario@lovelyhq.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.talk.chat
import android.Manifest
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.ClipData
import android.content.ClipboardManager
@ -71,6 +74,8 @@ import android.widget.RelativeLayout.LayoutParams
import android.widget.SeekBar
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
@ -278,6 +283,46 @@ class ChatActivity :
private lateinit var editMessage: ChatMessage
private val startSelectContactForResult = registerForActivityResult(
ActivityResultContracts
.StartActivityForResult()
) {
executeIfResultOk(it) { intent ->
onSelectContactResult(intent)
}
}
private val startChooseFileIntentForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
executeIfResultOk(it) { intent ->
onChooseFileResult(intent)
}
}
private val startRemoteFileBrowsingForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
executeIfResultOk(it) { intent ->
onRemoteFileBrowsingResult(intent)
}
}
private val startMessageSearchForResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
executeIfResultOk(it) { intent ->
onMessageSearchResult(intent)
}
}
private val startPickCameraIntentForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
executeIfResultOk(it) { intent ->
onPickCameraResult(intent)
}
}
override val view: View
get() = binding.root
@ -2967,170 +3012,167 @@ class ChatActivity :
}
}
@Throws(IllegalStateException::class)
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
if (resultCode != RESULT_OK && (requestCode != REQUEST_CODE_MESSAGE_SEARCH)) {
Log.e(TAG, "resultCode for received intent was != ok")
return
private fun onRemoteFileBrowsingResult(intent: Intent?) {
val pathList = intent?.getStringArrayListExtra(RemoteFileBrowserActivity.EXTRA_SELECTED_PATHS)
if (pathList?.size!! >= 1) {
pathList
.chunked(CHUNK_SIZE)
.forEach { paths ->
val data = Data.Builder()
.putLong(KEY_INTERNAL_USER_ID, conversationUser!!.id!!)
.putString(KEY_ROOM_TOKEN, roomToken)
.putStringArray(KEY_FILE_PATHS, paths.toTypedArray())
.build()
val worker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java)
.setInputData(data)
.build()
WorkManager.getInstance().enqueue(worker)
}
}
}
when (requestCode) {
REQUEST_CODE_SELECT_REMOTE_FILES -> {
val pathList = intent?.getStringArrayListExtra(RemoteFileBrowserActivity.EXTRA_SELECTED_PATHS)
if (pathList?.size!! >= 1) {
pathList
.chunked(CHUNK_SIZE)
.forEach { paths ->
val data = Data.Builder()
.putLong(KEY_INTERNAL_USER_ID, conversationUser!!.id!!)
.putString(KEY_ROOM_TOKEN, roomToken)
.putStringArray(KEY_FILE_PATHS, paths.toTypedArray())
.build()
val worker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java)
.setInputData(data)
.build()
WorkManager.getInstance().enqueue(worker)
}
@Throws(IllegalStateException::class)
private fun onChooseFileResult(intent: Intent?) {
try {
checkNotNull(intent)
filesToUpload.clear()
intent.clipData?.let {
for (index in 0 until it.itemCount) {
filesToUpload.add(it.getItemAt(index).uri.toString())
}
} ?: run {
checkNotNull(intent.data)
intent.data.let {
filesToUpload.add(intent.data.toString())
}
}
require(filesToUpload.isNotEmpty())
REQUEST_CODE_CHOOSE_FILE -> {
try {
checkNotNull(intent)
filesToUpload.clear()
intent.clipData?.let {
for (index in 0 until it.itemCount) {
filesToUpload.add(it.getItemAt(index).uri.toString())
}
} ?: run {
checkNotNull(intent.data)
intent.data.let {
filesToUpload.add(intent.data.toString())
}
}
require(filesToUpload.isNotEmpty())
val filenamesWithLineBreaks = StringBuilder("\n")
val filenamesWithLineBreaks = StringBuilder("\n")
for (file in filesToUpload) {
val filename = FileUtils.getFileName(Uri.parse(file), context)
filenamesWithLineBreaks.append(filename).append("\n")
}
val newFragment = FileAttachmentPreviewFragment.newInstance(
filenamesWithLineBreaks.toString(),
filesToUpload
)
newFragment.setListener { files, caption ->
uploadFiles(files, caption)
}
newFragment.show(supportFragmentManager, FileAttachmentPreviewFragment.TAG)
} catch (e: IllegalStateException) {
context.resources?.getString(R.string.nc_upload_failed)?.let {
Snackbar.make(
binding.root,
it,
Snackbar.LENGTH_LONG
).show()
}
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
} catch (e: IllegalArgumentException) {
context.resources?.getString(R.string.nc_upload_failed)?.let {
Snackbar.make(
binding.root,
it,
Snackbar.LENGTH_LONG
).show()
}
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
}
for (file in filesToUpload) {
val filename = FileUtils.getFileName(Uri.parse(file), context)
filenamesWithLineBreaks.append(filename).append("\n")
}
REQUEST_CODE_SELECT_CONTACT -> {
val contactUri = intent?.data ?: return
val cursor: Cursor? = contentResolver!!.query(contactUri, null, null, null, null)
if (cursor != null && cursor.moveToFirst()) {
val id = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID))
val fileName = ContactUtils.getDisplayNameFromDeviceContact(context, id) + ".vcf"
val file = File(context.cacheDir, fileName)
writeContactToVcfFile(cursor, file)
val shareUri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID,
File(file.absolutePath)
)
uploadFile(shareUri.toString(), false)
}
cursor?.close()
val newFragment = FileAttachmentPreviewFragment.newInstance(
filenamesWithLineBreaks.toString(),
filesToUpload
)
newFragment.setListener { files, caption ->
uploadFiles(files, caption)
}
newFragment.show(supportFragmentManager, FileAttachmentPreviewFragment.TAG)
} catch (e: IllegalStateException) {
context.resources?.getString(R.string.nc_upload_failed)?.let {
Snackbar.make(
binding.root,
it,
Snackbar.LENGTH_LONG
).show()
}
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
} catch (e: IllegalArgumentException) {
context.resources?.getString(R.string.nc_upload_failed)?.let {
Snackbar.make(
binding.root,
it,
Snackbar.LENGTH_LONG
).show()
}
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
}
}
REQUEST_CODE_PICK_CAMERA -> {
if (resultCode == RESULT_OK) {
try {
filesToUpload.clear()
private fun onSelectContactResult(intent: Intent?) {
val contactUri = intent?.data ?: return
val cursor: Cursor? = contentResolver!!.query(contactUri, null, null, null, null)
if (intent != null && intent.data != null) {
run {
intent.data.let {
filesToUpload.add(intent.data.toString())
}
}
require(filesToUpload.isNotEmpty())
} else if (videoURI != null) {
filesToUpload.add(videoURI.toString())
videoURI = null
} else {
error("Failed to get data from intent and uri")
}
if (cursor != null && cursor.moveToFirst()) {
val id = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID))
val fileName = ContactUtils.getDisplayNameFromDeviceContact(context, id) + ".vcf"
val file = File(context.cacheDir, fileName)
writeContactToVcfFile(cursor, file)
if (permissionUtil.isFilesPermissionGranted()) {
val filenamesWithLineBreaks = StringBuilder("\n")
val shareUri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID,
File(file.absolutePath)
)
uploadFile(shareUri.toString(), false)
}
cursor?.close()
}
for (file in filesToUpload) {
val filename = FileUtils.getFileName(Uri.parse(file), context)
filenamesWithLineBreaks.append(filename).append("\n")
}
@Throws(IllegalStateException::class)
private fun onPickCameraResult(intent: Intent?) {
try {
filesToUpload.clear()
val newFragment = FileAttachmentPreviewFragment.newInstance(
filenamesWithLineBreaks.toString(),
filesToUpload
)
newFragment.setListener { files, caption -> uploadFiles(files, caption) }
newFragment.show(supportFragmentManager, FileAttachmentPreviewFragment.TAG)
} else {
UploadAndShareFilesWorker.requestStoragePermission(this)
}
} catch (e: IllegalStateException) {
Snackbar.make(
binding.root,
R.string.nc_upload_failed,
Snackbar.LENGTH_LONG
)
.show()
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
} catch (e: IllegalArgumentException) {
context.resources?.getString(R.string.nc_upload_failed)?.let {
Snackbar.make(
binding.root,
it,
Snackbar.LENGTH_LONG
)
.show()
}
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
if (intent != null && intent.data != null) {
run {
intent.data.let {
filesToUpload.add(intent.data.toString())
}
}
require(filesToUpload.isNotEmpty())
} else if (videoURI != null) {
filesToUpload.add(videoURI.toString())
videoURI = null
} else {
error("Failed to get data from intent and uri")
}
REQUEST_CODE_MESSAGE_SEARCH -> {
val messageId = intent?.getStringExtra(MessageSearchActivity.RESULT_KEY_MESSAGE_ID)
messageId?.let { id ->
scrollToMessageWithId(id)
if (permissionUtil.isFilesPermissionGranted()) {
val filenamesWithLineBreaks = StringBuilder("\n")
for (file in filesToUpload) {
val filename = FileUtils.getFileName(Uri.parse(file), context)
filenamesWithLineBreaks.append(filename).append("\n")
}
val newFragment = FileAttachmentPreviewFragment.newInstance(
filenamesWithLineBreaks.toString(),
filesToUpload
)
newFragment.setListener { files, caption -> uploadFiles(files, caption) }
newFragment.show(supportFragmentManager, FileAttachmentPreviewFragment.TAG)
} else {
UploadAndShareFilesWorker.requestStoragePermission(this)
}
} catch (e: IllegalStateException) {
Snackbar.make(
binding.root,
R.string.nc_upload_failed,
Snackbar.LENGTH_LONG
)
.show()
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
} catch (e: IllegalArgumentException) {
context.resources?.getString(R.string.nc_upload_failed)?.let {
Snackbar.make(
binding.root,
it,
Snackbar.LENGTH_LONG
)
.show()
}
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
}
}
private fun onMessageSearchResult(intent: Intent?) {
val messageId = intent?.getStringExtra(MessageSearchActivity.RESULT_KEY_MESSAGE_ID)
messageId?.let { id ->
scrollToMessageWithId(id)
}
}
private fun executeIfResultOk(result: ActivityResult, onResult: (intent: Intent?) -> Unit) {
if (result.resultCode == Activity.RESULT_OK) {
onResult(result.data)
} else {
Log.e(TAG, "resultCode for received intent was != ok")
}
}
@ -3202,7 +3244,7 @@ class ChatActivity :
} else if (requestCode == REQUEST_READ_CONTACT_PERMISSION) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
val intent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
startActivityForResult(intent, REQUEST_CODE_SELECT_CONTACT)
startSelectContactForResult.launch(intent)
} else {
Snackbar.make(
binding.root,
@ -3275,14 +3317,13 @@ class ChatActivity :
addCategory(Intent.CATEGORY_OPENABLE)
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
}
startActivityForResult(
startChooseFileIntentForResult.launch(
Intent.createChooser(
action,
context.resources?.getString(
R.string.nc_upload_choose_local_files
)
),
REQUEST_CODE_CHOOSE_FILE
)
)
}
@ -3328,7 +3369,7 @@ class ChatActivity :
fun showBrowserScreen() {
val sharingFileBrowserIntent = Intent(this, RemoteFileBrowserActivity::class.java)
startActivityForResult(sharingFileBrowserIntent, REQUEST_CODE_SELECT_REMOTE_FILES)
startRemoteFileBrowsingForResult.launch(sharingFileBrowserIntent)
}
fun showShareLocationScreen() {
@ -4095,7 +4136,7 @@ class ChatActivity :
val intent = Intent(this, MessageSearchActivity::class.java)
intent.putExtra(KEY_CONVERSATION_NAME, currentConversation?.displayName)
intent.putExtra(KEY_ROOM_TOKEN, roomToken)
startActivityForResult(intent, REQUEST_CODE_MESSAGE_SEARCH)
startMessageSearchForResult.launch(intent)
}
private fun handleSystemMessages(chatMessageList: List<ChatMessage>): List<ChatMessage> {
@ -4797,7 +4838,7 @@ class ChatActivity :
if (!permissionUtil.isCameraPermissionGranted()) {
requestCameraPermissions()
} else {
startActivityForResult(TakePhotoActivity.createIntent(context), REQUEST_CODE_PICK_CAMERA)
startPickCameraIntentForResult.launch(TakePhotoActivity.createIntent(context))
}
}
@ -4825,7 +4866,7 @@ class ChatActivity :
videoFile?.also {
videoURI = FileProvider.getUriForFile(context, context.packageName, it)
takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, videoURI)
startActivityForResult(takeVideoIntent, REQUEST_CODE_PICK_CAMERA)
startPickCameraIntentForResult.launch(takeVideoIntent)
}
}
}
@ -4897,15 +4938,10 @@ class ChatActivity :
private const val GET_ROOM_INFO_DELAY_LOBBY: Long = 5000
private const val HTTP_CODE_OK: Int = 200
private const val AGE_THRESHOLD_FOR_DELETE_MESSAGE: Int = 21600000 // (6 hours in millis = 6 * 3600 * 1000)
private const val REQUEST_CODE_CHOOSE_FILE: Int = 555
private const val REQUEST_CODE_SELECT_CONTACT: Int = 666
private const val REQUEST_CODE_MESSAGE_SEARCH: Int = 777
private const val REQUEST_SHARE_FILE_PERMISSION: Int = 221
private const val REQUEST_RECORD_AUDIO_PERMISSION = 222
private const val REQUEST_READ_CONTACT_PERMISSION = 234
private const val REQUEST_CAMERA_PERMISSION = 223
private const val REQUEST_CODE_PICK_CAMERA: Int = 333
private const val REQUEST_CODE_SELECT_REMOTE_FILES = 888
private const val OBJECT_MESSAGE: String = "{object}"
private const val MINIMUM_VOICE_RECORD_DURATION: Int = 1000
private const val MINIMUM_VOICE_RECORD_TO_STOP: Int = 200

View file

@ -8,7 +8,6 @@
package com.nextcloud.talk.conversationinfoedit
import android.app.Activity
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.text.TextUtils
@ -16,6 +15,8 @@ import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.net.toFile
import androidx.core.view.ViewCompat
import androidx.lifecycle.ViewModelProvider
@ -49,8 +50,7 @@ import java.io.File
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class ConversationInfoEditActivity :
BaseActivity() {
class ConversationInfoEditActivity : BaseActivity() {
private lateinit var binding: ActivityConversationInfoEditBinding
@ -75,6 +75,31 @@ class ConversationInfoEditActivity :
private lateinit var spreedCapabilities: SpreedCapability
private val startImagePickerForResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
handleResult(it) { result ->
pickImage.onImagePickerResult(result.data) { uri ->
uploadAvatar(uri.toFile())
}
}
}
private val startSelectRemoteFilesIntentForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
handleResult(it) { result ->
pickImage.onSelectRemoteFilesResult(startImagePickerForResult, result.data)
}
}
private val startTakePictureIntentForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
handleResult(it) { result ->
pickImage.onTakePictureResult(startImagePickerForResult, result.data)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
@ -158,9 +183,18 @@ class ConversationInfoEditActivity :
}
private fun setupAvatarOptions() {
binding.avatarUpload.setOnClickListener { pickImage.selectLocal() }
binding.avatarChoose.setOnClickListener { pickImage.selectRemote() }
binding.avatarCamera.setOnClickListener { pickImage.takePicture() }
binding.avatarUpload.setOnClickListener {
pickImage.selectLocal(startImagePickerForResult = startImagePickerForResult)
}
binding.avatarChoose.setOnClickListener {
pickImage.selectRemote(startSelectRemoteFilesIntentForResult = startSelectRemoteFilesIntentForResult)
}
binding.avatarCamera.setOnClickListener {
pickImage.takePicture(startTakePictureIntentForResult = startTakePictureIntentForResult)
}
if (conversation?.hasCustomAvatar == true) {
binding.avatarDelete.visibility = View.VISIBLE
binding.avatarDelete.setOnClickListener { deleteAvatar() }
@ -293,19 +327,12 @@ class ConversationInfoEditActivity :
})
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (resultCode) {
Activity.RESULT_OK -> {
pickImage.handleActivityResult(
requestCode,
resultCode,
data
) { uploadAvatar(it.toFile()) }
}
private fun handleResult(result: ActivityResult, onResult: (result: ActivityResult) -> Unit) {
when (result.resultCode) {
Activity.RESULT_OK -> onResult(result)
ImagePicker.RESULT_ERROR -> {
Snackbar.make(binding.root, ImagePicker.getError(data), Snackbar.LENGTH_SHORT).show()
Snackbar.make(binding.root, ImagePicker.getError(result.data), Snackbar.LENGTH_SHORT).show()
}
else -> {

View file

@ -11,9 +11,10 @@ package com.nextcloud.talk.lock
import android.app.Activity
import android.app.KeyguardManager
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricPrompt
import autodagger.AutoInjector
@ -35,6 +36,13 @@ class LockedActivity : AppCompatActivity() {
@Inject
lateinit var appPreferences: AppPreferences
private val startForCredentialsResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult
()
) {
onConfirmDeviceCredentials(it)
}
private lateinit var binding: ActivityLockedBinding
override fun onCreate(savedInstanceState: Bundle?) {
@ -111,27 +119,23 @@ class LockedActivity : AppCompatActivity() {
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager?
val intent = keyguardManager?.createConfirmDeviceCredentialIntent(null, null)
if (intent != null) {
startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS)
startForCredentialsResult.launch(intent)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) {
if (resultCode == Activity.RESULT_OK) {
if (
SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)
) {
finish()
}
} else {
Log.d(TAG, "Authorization failed")
private fun onConfirmDeviceCredentials(result: ActivityResult) {
if (result.resultCode == Activity.RESULT_OK) {
if (
SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)
) {
finish()
}
} else {
Log.d(TAG, "Authorization failed")
}
}
companion object {
private val TAG = LockedActivity::class.java.simpleName
private const val REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 112
}
}

View file

@ -10,7 +10,6 @@
package com.nextcloud.talk.profile
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.drawable.ColorDrawable
import android.net.Uri
@ -24,6 +23,8 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.core.net.toFile
@ -50,13 +51,13 @@ import com.nextcloud.talk.ui.dialog.ScopeDialog
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.Mimetype.IMAGE_JPG
import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX_GENERIC
import com.nextcloud.talk.utils.PickImage
import com.nextcloud.talk.utils.PickImage.Companion.REQUEST_PERMISSION_CAMERA
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.SpreedFeatures
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
@ -87,6 +88,31 @@ class ProfileActivity : BaseActivity() {
private lateinit var pickImage: PickImage
private val startImagePickerForResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
handleResult(it) { result ->
pickImage.onImagePickerResult(result.data) { uri ->
uploadAvatar(uri.toFile())
}
}
}
private val startSelectRemoteFilesIntentForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
handleResult(it) { result ->
pickImage.onSelectRemoteFilesResult(startImagePickerForResult, result.data)
}
}
private val startTakePictureIntentForResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
handleResult(it) { result ->
pickImage.onTakePictureResult(startImagePickerForResult, result.data)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
@ -106,9 +132,15 @@ class ProfileActivity : BaseActivity() {
val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
pickImage = PickImage(this, currentUser)
binding.avatarUpload.setOnClickListener { pickImage.selectLocal() }
binding.avatarChoose.setOnClickListener { pickImage.selectRemote() }
binding.avatarCamera.setOnClickListener { pickImage.takePicture() }
binding.avatarUpload.setOnClickListener {
pickImage.selectLocal(startImagePickerForResult = startImagePickerForResult)
}
binding.avatarChoose.setOnClickListener {
pickImage.selectRemote(startSelectRemoteFilesIntentForResult = startSelectRemoteFilesIntentForResult)
}
binding.avatarCamera.setOnClickListener {
pickImage.takePicture(startTakePictureIntentForResult = startTakePictureIntentForResult)
}
binding.avatarDelete.setOnClickListener {
ncApi.deleteAvatar(
credentials,
@ -479,7 +511,7 @@ class ProfileActivity : BaseActivity() {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_PERMISSION_CAMERA) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
pickImage.takePicture()
pickImage.takePicture(startTakePictureIntentForResult = startTakePictureIntentForResult)
} else {
Snackbar
.make(binding.root, context.getString(R.string.take_photo_permission), Snackbar.LENGTH_LONG)
@ -488,19 +520,14 @@ class ProfileActivity : BaseActivity() {
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (resultCode) {
Activity.RESULT_OK -> {
pickImage.handleActivityResult(
requestCode,
resultCode,
data
) { uploadAvatar(it.toFile()) }
}
private fun handleResult(result: ActivityResult, onResult: (result: ActivityResult) -> Unit) {
when (result.resultCode) {
Activity.RESULT_OK -> onResult(result)
ImagePicker.RESULT_ERROR -> {
Snackbar.make(binding.root, getError(data), Snackbar.LENGTH_SHORT).show()
Snackbar.make(binding.root, getError(result.data), Snackbar.LENGTH_SHORT).show()
}
else -> {
Log.i(TAG, "Task Cancelled")
}

View file

@ -1,9 +1,11 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2024 Parneet Singh <gurayaparneet@gmail.com>
* SPDX-FileCopyrightText: 2022 Tim Krüger <t@timkrueger.me>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.talk.utils
import android.app.Activity
@ -13,6 +15,7 @@ import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import autodagger.AutoInjector
import com.github.dhaval2404.imagepicker.ImagePicker
import com.github.dhaval2404.imagepicker.constant.ImageProvider
@ -48,17 +51,19 @@ class PickImage(
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
}
fun selectLocal() {
fun selectLocal(startImagePickerForResult: ActivityResultLauncher<Intent>) {
ImagePicker.Companion.with(activity)
.provider(ImageProvider.GALLERY)
.crop()
.cropSquare()
.compress(MAX_SIZE)
.maxResultSize(MAX_SIZE, MAX_SIZE)
.createIntent { intent -> this.activity.startActivityForResult(intent, REQUEST_CODE_IMAGE_PICKER) }
.createIntent { intent ->
startImagePickerForResult.launch(intent)
}
}
private fun selectLocal(file: File) {
private fun selectLocal(startImagePickerForResult: ActivityResultLauncher<Intent>, file: File) {
ImagePicker.Companion.with(activity)
.provider(ImageProvider.URI)
.crop()
@ -66,25 +71,23 @@ class PickImage(
.compress(MAX_SIZE)
.maxResultSize(MAX_SIZE, MAX_SIZE)
.setUri(Uri.fromFile(file))
.createIntent { intent -> this.activity.startActivityForResult(intent, REQUEST_CODE_IMAGE_PICKER) }
.createIntent { intent ->
startImagePickerForResult.launch(intent)
}
}
fun selectRemote() {
fun selectRemote(startSelectRemoteFilesIntentForResult: ActivityResultLauncher<Intent>) {
val bundle = Bundle()
bundle.putString(BundleKeys.KEY_MIME_TYPE_FILTER, Mimetype.IMAGE_PREFIX)
val avatarIntent = Intent(activity, RemoteFileBrowserActivity::class.java)
avatarIntent.putExtras(bundle)
this.activity.startActivityForResult(avatarIntent, REQUEST_CODE_SELECT_REMOTE_FILES)
startSelectRemoteFilesIntentForResult.launch(avatarIntent)
}
fun takePicture() {
fun takePicture(startTakePictureIntentForResult: ActivityResultLauncher<Intent>) {
if (permissionUtil.isCameraPermissionGranted()) {
activity.startActivityForResult(
TakePhotoActivity.createIntent(activity),
REQUEST_CODE_TAKE_PICTURE
)
startTakePictureIntentForResult.launch(TakePhotoActivity.createIntent(activity))
} else {
activity.requestPermissions(
arrayOf(android.Manifest.permission.CAMERA),
@ -93,7 +96,7 @@ class PickImage(
}
}
private fun handleAvatar(remotePath: String?) {
private fun handleAvatar(startImagePickerForResult: ActivityResultLauncher<Intent>, remotePath: String?) {
val uri = currentUser!!.baseUrl + "/index.php/apps/files/api/v1/thumbnail/512/512/" +
Uri.encode(remotePath, "/")
val downloadCall = ncApi.downloadResizedImage(
@ -102,7 +105,10 @@ class PickImage(
)
downloadCall.enqueue(object : Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
saveBitmapAndPassToImagePicker(BitmapFactory.decodeStream(response.body()!!.byteStream()))
saveBitmapAndPassToImagePicker(
startImagePickerForResult,
BitmapFactory.decodeStream(response.body()!!.byteStream())
)
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
@ -112,9 +118,12 @@ class PickImage(
}
// only possible with API26
private fun saveBitmapAndPassToImagePicker(bitmap: Bitmap) {
private fun saveBitmapAndPassToImagePicker(
startImagePickerForResult: ActivityResultLauncher<Intent>,
bitmap: Bitmap
) {
val file: File = saveBitmapToTempFile(bitmap) ?: return
selectLocal(file)
selectLocal(startImagePickerForResult, file)
}
private fun saveBitmapToTempFile(bitmap: Bitmap): File? {
@ -145,35 +154,21 @@ class PickImage(
)
}
fun handleActivityResult(requestCode: Int, resultCode: Int, data: Intent?, handleImage: (uri: Uri) -> Unit) {
if (resultCode != Activity.RESULT_OK) {
Log.w(
TAG,
"Check result code before calling " +
"'PickImage#handleActivtyResult'. It should be ${Activity.RESULT_OK}, but it is $resultCode!"
)
return
}
fun onImagePickerResult(data: Intent?, handleImage: (uri: Uri) -> Unit) {
val uri: Uri = data?.data!!
handleImage(uri)
}
when (requestCode) {
REQUEST_CODE_IMAGE_PICKER -> {
val uri: Uri = data?.data!!
handleImage(uri)
}
REQUEST_CODE_SELECT_REMOTE_FILES -> {
val pathList = data?.getStringArrayListExtra(RemoteFileBrowserActivity.EXTRA_SELECTED_PATHS)
if (pathList?.size!! >= 1) {
handleAvatar(pathList[0])
}
}
REQUEST_CODE_TAKE_PICTURE -> {
data?.data?.path?.let {
selectLocal(File(it))
}
}
else -> {
Log.w(TAG, "Unknown intent request code")
}
fun onSelectRemoteFilesResult(startImagePickerForResult: ActivityResultLauncher<Intent>, data: Intent?) {
val pathList = data?.getStringArrayListExtra(RemoteFileBrowserActivity.EXTRA_SELECTED_PATHS)
if (pathList?.size!! >= 1) {
handleAvatar(startImagePickerForResult, pathList[0])
}
}
fun onTakePictureResult(startImagePickerForResult: ActivityResultLauncher<Intent>, data: Intent?) {
data?.data?.path?.let {
selectLocal(startImagePickerForResult, File(it))
}
}
@ -182,9 +177,6 @@ class PickImage(
private const val MAX_SIZE: Int = 1024
private const val AVATAR_PATH = "photos/avatar.png"
private const val FULL_QUALITY: Int = 100
const val REQUEST_CODE_IMAGE_PICKER: Int = 1
const val REQUEST_CODE_TAKE_PICTURE: Int = 2
const val REQUEST_PERMISSION_CAMERA: Int = 1
const val REQUEST_CODE_SELECT_REMOTE_FILES = 22
}
}