From 631a93f687ad4123289f3850cdeafd50caeabc62 Mon Sep 17 00:00:00 2001 From: Parneet Singh Date: Fri, 22 Mar 2024 17:19:24 +0530 Subject: [PATCH] use new result api Signed-off-by: Parneet Singh --- .../com/nextcloud/talk/chat/ChatActivity.kt | 350 ++++++++++-------- .../ConversationInfoEditActivity.kt | 61 ++- .../com/nextcloud/talk/lock/LockedActivity.kt | 32 +- .../nextcloud/talk/profile/ProfileActivity.kt | 63 +++- .../com/nextcloud/talk/utils/PickImage.kt | 86 ++--- 5 files changed, 339 insertions(+), 253 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index 507eb905b..899b9bbee 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -1,6 +1,7 @@ /* * Nextcloud Talk - Android Client * + * SPDX-FileCopyrightText: 2024 Parneet Singh * SPDX-FileCopyrightText: 2024 Giacomo Pacini * SPDX-FileCopyrightText: 2023 Ezhil Shanmugham * SPDX-FileCopyrightText: 2021-2022 Marcel Hibbe @@ -9,11 +10,13 @@ * SPDX-FileCopyrightText: 2017-2019 Mario Danic * 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): List { @@ -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 diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt index 8c78d4d8d..6464c9be6 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt @@ -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 -> { diff --git a/app/src/main/java/com/nextcloud/talk/lock/LockedActivity.kt b/app/src/main/java/com/nextcloud/talk/lock/LockedActivity.kt index 9c0e62aab..75a6af27f 100644 --- a/app/src/main/java/com/nextcloud/talk/lock/LockedActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/lock/LockedActivity.kt @@ -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 } } diff --git a/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt b/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt index 9678e9dac..a9a0e8a13 100644 --- a/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt @@ -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") } diff --git a/app/src/main/java/com/nextcloud/talk/utils/PickImage.kt b/app/src/main/java/com/nextcloud/talk/utils/PickImage.kt index 3f5e12e9b..22b9216de 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/PickImage.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/PickImage.kt @@ -1,9 +1,11 @@ /* * Nextcloud Talk - Android Client * + * SPDX-FileCopyrightText: 2024 Parneet Singh * SPDX-FileCopyrightText: 2022 Tim Krüger * 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) { 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, 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) { 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) { 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, 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 { override fun onResponse(call: Call, response: Response) { - saveBitmapAndPassToImagePicker(BitmapFactory.decodeStream(response.body()!!.byteStream())) + saveBitmapAndPassToImagePicker( + startImagePickerForResult, + BitmapFactory.decodeStream(response.body()!!.byteStream()) + ) } override fun onFailure(call: Call, t: Throwable) { @@ -112,9 +118,12 @@ class PickImage( } // only possible with API26 - private fun saveBitmapAndPassToImagePicker(bitmap: Bitmap) { + private fun saveBitmapAndPassToImagePicker( + startImagePickerForResult: ActivityResultLauncher, + 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, data: Intent?) { + val pathList = data?.getStringArrayListExtra(RemoteFileBrowserActivity.EXTRA_SELECTED_PATHS) + if (pathList?.size!! >= 1) { + handleAvatar(startImagePickerForResult, pathList[0]) + } + } + + fun onTakePictureResult(startImagePickerForResult: ActivityResultLauncher, 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 } }