diff --git a/vector/src/main/java/im/vector/riotx/features/media/BigImageViewerActivity.kt b/vector/src/main/java/im/vector/riotx/features/media/BigImageViewerActivity.kt index 8513d64da9..9262fd3051 100644 --- a/vector/src/main/java/im/vector/riotx/features/media/BigImageViewerActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/media/BigImageViewerActivity.kt @@ -17,6 +17,7 @@ package im.vector.riotx.features.media import android.app.Activity +import android.app.AlertDialog import android.content.Context import android.content.Intent import android.net.Uri @@ -31,19 +32,19 @@ import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.resources.ColorProvider -import im.vector.riotx.features.roomprofile.AvatarSelectorView +import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.multipicker.MultiPicker import im.vector.riotx.multipicker.entity.MultiPickerImageType import kotlinx.android.synthetic.main.activity_big_image_viewer.* import java.io.File import javax.inject.Inject -class BigImageViewerActivity : VectorBaseActivity(), AvatarSelectorView.Callback { +class BigImageViewerActivity : VectorBaseActivity() { @Inject lateinit var sessionHolder: ActiveSessionHolder @Inject lateinit var colorProvider: ColorProvider + @Inject lateinit var stringProvider: StringProvider private var uri: Uri? = null - private lateinit var avatarSelector: AvatarSelectorView override fun getMenuRes() = R.menu.vector_big_avatar_viewer @@ -91,23 +92,24 @@ class BigImageViewerActivity : VectorBaseActivity(), AvatarSelectorView.Callback return uri != null && intent.getBooleanExtra(EXTRA_CAN_EDIT_IMAGE, false) } - private fun showAvatarSelector() { - if (!::avatarSelector.isInitialized) { - avatarSelector = AvatarSelectorView(this, layoutInflater, this) - } - avatarSelector.show(bigImageViewerToolbar, false) - } - private var avatarCameraUri: Uri? = null - override fun onTypeSelected(type: AvatarSelectorView.Type) { - when (type) { - AvatarSelectorView.Type.CAMERA -> { - avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) - } - AvatarSelectorView.Type.GALLERY -> { - MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) - } - } + private fun showAvatarSelector() { + AlertDialog + .Builder(this) + .setItems(arrayOf( + stringProvider.getString(R.string.attachment_type_camera), + stringProvider.getString(R.string.attachment_type_gallery) + )) { dialog, which -> + dialog.cancel() + when (which) { + 0 -> { + avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) + } + 1 -> { + MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) + } + } + }.show() } private fun onRoomAvatarSelected(image: MultiPickerImageType) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/AvatarSelectorView.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/AvatarSelectorView.kt deleted file mode 100644 index 6636d533f1..0000000000 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/AvatarSelectorView.kt +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2020 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.riotx.features.roomprofile - -import android.animation.Animator -import android.animation.AnimatorListenerAdapter -import android.annotation.TargetApi -import android.content.Context -import android.graphics.drawable.BitmapDrawable -import android.os.Build -import android.util.Pair -import android.view.Gravity -import android.view.LayoutInflater -import android.view.View -import android.view.ViewAnimationUtils -import android.view.animation.Animation -import android.view.animation.AnimationSet -import android.view.animation.OvershootInterpolator -import android.view.animation.ScaleAnimation -import android.view.animation.TranslateAnimation -import android.widget.FrameLayout -import android.widget.ImageButton -import android.widget.LinearLayout -import android.widget.PopupWindow -import androidx.core.view.doOnNextLayout -import com.amulyakhare.textdrawable.TextDrawable -import com.amulyakhare.textdrawable.util.ColorGenerator -import im.vector.riotx.R -import im.vector.riotx.core.extensions.getMeasurements -import im.vector.riotx.core.utils.PERMISSIONS_FOR_TAKING_PHOTO -import im.vector.riotx.core.utils.PERMISSIONS_FOR_WRITING_FILES -import im.vector.riotx.features.roomprofile.AvatarSelectorView.Callback -import kotlin.math.max - -private const val ANIMATION_DURATION = 250 - -/** - * This class is the view presenting choices for picking avatar. - * It will return result through [Callback]. - */ -class AvatarSelectorView(context: Context, - inflater: LayoutInflater, - var callback: Callback?) - : PopupWindow(context) { - - interface Callback { - fun onTypeSelected(type: Type) - } - - private val iconColorGenerator = ColorGenerator.MATERIAL - - private var galleryButton: ImageButton - private var cameraButton: ImageButton - - private var anchor: View? = null - - init { - val root = FrameLayout(context) - val layout = inflater.inflate(R.layout.view_avatar_selector, root, true) - galleryButton = layout.findViewById(R.id.avatarGalleryButton).configure(Type.GALLERY) - cameraButton = layout.findViewById(R.id.avatarCameraButton).configure(Type.CAMERA) - contentView = root - width = LinearLayout.LayoutParams.MATCH_PARENT - height = LinearLayout.LayoutParams.WRAP_CONTENT - animationStyle = 0 - @Suppress("DEPRECATION") - setBackgroundDrawable(BitmapDrawable()) - inputMethodMode = INPUT_METHOD_NOT_NEEDED - isFocusable = true - isTouchable = true - } - - fun show(anchor: View, isKeyboardOpen: Boolean) { - this.anchor = anchor - val anchorCoordinates = IntArray(2) - anchor.getLocationOnScreen(anchorCoordinates) - if (isKeyboardOpen) { - showAtLocation(anchor, Gravity.NO_GRAVITY, 0, anchorCoordinates[1] + anchor.height) - } else { - val contentViewHeight = if (contentView.height == 0) { - contentView.getMeasurements().second - } else { - contentView.height - } - showAtLocation(anchor, Gravity.NO_GRAVITY, 0, anchorCoordinates[1] - contentViewHeight) - } - contentView.doOnNextLayout { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - animateWindowInCircular(anchor, contentView) - } else { - animateWindowInTranslate(contentView) - } - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - animateButtonIn(galleryButton, ANIMATION_DURATION / 2) - animateButtonIn(cameraButton, ANIMATION_DURATION / 2) - } - } - - override fun dismiss() { - val capturedAnchor = anchor - if (capturedAnchor != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - animateWindowOutCircular(capturedAnchor, contentView) - } else { - animateWindowOutTranslate(contentView) - } - } - - private fun animateButtonIn(button: View, delay: Int) { - val animation = AnimationSet(true) - val scale = ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.0f) - animation.addAnimation(scale) - animation.interpolator = OvershootInterpolator(1f) - animation.duration = ANIMATION_DURATION.toLong() - animation.startOffset = delay.toLong() - button.startAnimation(animation) - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private fun animateWindowInCircular(anchor: View, contentView: View) { - val coordinates = getClickCoordinates(anchor, contentView) - val animator = ViewAnimationUtils.createCircularReveal(contentView, - coordinates.first, - coordinates.second, - 0f, - max(contentView.width, contentView.height).toFloat()) - animator.duration = ANIMATION_DURATION.toLong() - animator.start() - } - - private fun animateWindowInTranslate(contentView: View) { - val animation = TranslateAnimation(0f, 0f, contentView.height.toFloat(), 0f) - animation.duration = ANIMATION_DURATION.toLong() - getContentView().startAnimation(animation) - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private fun animateWindowOutCircular(anchor: View, contentView: View) { - val coordinates = getClickCoordinates(anchor, contentView) - val animator = ViewAnimationUtils.createCircularReveal(getContentView(), - coordinates.first, - coordinates.second, - max(getContentView().width, getContentView().height).toFloat(), - 0f) - - animator.duration = ANIMATION_DURATION.toLong() - animator.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - super@AvatarSelectorView.dismiss() - } - }) - animator.start() - } - - private fun animateWindowOutTranslate(contentView: View) { - val animation = TranslateAnimation(0f, 0f, 0f, (contentView.top + contentView.height).toFloat()) - animation.duration = ANIMATION_DURATION.toLong() - animation.setAnimationListener(object : Animation.AnimationListener { - override fun onAnimationStart(animation: Animation) {} - - override fun onAnimationEnd(animation: Animation) { - super@AvatarSelectorView.dismiss() - } - - override fun onAnimationRepeat(animation: Animation) {} - }) - - getContentView().startAnimation(animation) - } - - private fun getClickCoordinates(anchor: View, contentView: View): Pair { - val anchorCoordinates = IntArray(2) - anchor.getLocationOnScreen(anchorCoordinates) - val contentCoordinates = IntArray(2) - contentView.getLocationOnScreen(contentCoordinates) - val x = anchorCoordinates[0] - contentCoordinates[0] + anchor.width / 2 - val y = anchorCoordinates[1] - contentCoordinates[1] - return Pair(x, y) - } - - private fun ImageButton.configure(type: Type): ImageButton { - this.background = TextDrawable.builder().buildRound("", iconColorGenerator.getColor(type.ordinal)) - this.setOnClickListener(TypeClickListener(type)) - return this - } - - private inner class TypeClickListener(private val type: Type) : View.OnClickListener { - - override fun onClick(v: View) { - dismiss() - callback?.onTypeSelected(type) - } - } - - /** - * The all possible types to pick with their required permissions. - */ - enum class Type(val permissionsBit: Int) { - CAMERA(PERMISSIONS_FOR_TAKING_PHOTO), - GALLERY(PERMISSIONS_FOR_WRITING_FILES), - } -} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index 2558c2bd32..0b5025e086 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -47,6 +47,9 @@ import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.intent.getFilenameFromUri import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.resources.ColorProvider +import im.vector.riotx.core.utils.PERMISSIONS_FOR_TAKING_PHOTO +import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA +import im.vector.riotx.core.utils.checkPermissions import im.vector.riotx.core.utils.copyToClipboard import im.vector.riotx.core.utils.startSharePlainTextIntent import im.vector.riotx.features.crypto.util.toImageRes @@ -76,7 +79,7 @@ class RoomProfileFragment @Inject constructor( private val avatarRenderer: AvatarRenderer, val roomProfileViewModelFactory: RoomProfileViewModel.Factory, val colorProvider: ColorProvider -) : VectorBaseFragment(), RoomProfileController.Callback, AvatarSelectorView.Callback { +) : VectorBaseFragment(), RoomProfileController.Callback { private val roomProfileArgs: RoomProfileArgs by args() private lateinit var roomListQuickActionsSharedActionViewModel: RoomListQuickActionsSharedActionViewModel @@ -85,8 +88,6 @@ class RoomProfileFragment @Inject constructor( private var appBarStateChangeListener: AppBarStateChangeListener? = null - private lateinit var avatarSelector: AvatarSelectorView - override fun getLayoutResId() = R.layout.fragment_matrix_profile override fun getMenuRes() = R.menu.vector_room_profile @@ -251,21 +252,25 @@ class RoomProfileFragment @Inject constructor( } private fun showAvatarSelector() { - if (!::avatarSelector.isInitialized) { - avatarSelector = AvatarSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@RoomProfileFragment) - } - avatarSelector.show(vector_coordinator_layout, false) + AlertDialog + .Builder(requireContext()) + .setItems(arrayOf( + getString(R.string.attachment_type_camera), + getString(R.string.attachment_type_gallery) + )) { dialog, which -> + dialog.cancel() + onAvatarTypeSelected(isCamera = (which == 0)) + }.show() } private var avatarCameraUri: Uri? = null - override fun onTypeSelected(type: AvatarSelectorView.Type) { - when (type) { - AvatarSelectorView.Type.CAMERA -> { + private fun onAvatarTypeSelected(isCamera: Boolean) { + if (isCamera) { + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) } - AvatarSelectorView.Type.GALLERY -> { - MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) - } + } else { + MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) } } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt index 69ca6f56ca..17739c2503 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsGeneralFragment.kt @@ -64,7 +64,6 @@ import im.vector.riotx.core.utils.toast import im.vector.riotx.features.MainActivity import im.vector.riotx.features.MainActivityArgs import im.vector.riotx.features.media.createUCropWithDefaultSettings -import im.vector.riotx.features.roomprofile.AvatarSelectorView import im.vector.riotx.features.themes.ThemeUtils import im.vector.riotx.features.workers.signout.SignOutUiWorker import im.vector.riotx.multipicker.MultiPicker @@ -76,7 +75,7 @@ import kotlinx.coroutines.withContext import java.io.File import java.util.UUID -class VectorSettingsGeneralFragment : VectorSettingsBaseFragment(), AvatarSelectorView.Callback { +class VectorSettingsGeneralFragment : VectorSettingsBaseFragment() { override var titleRes = R.string.settings_general_title override val preferenceXmlRes = R.xml.vector_settings_general @@ -84,7 +83,6 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment(), AvatarSelect private var mDisplayedEmails = ArrayList() private var mDisplayedPhoneNumber = ArrayList() - private lateinit var avatarSelector: AvatarSelectorView private var avatarCameraUri: Uri? = null private val mUserSettingsCategory by lazy { @@ -296,7 +294,7 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment(), AvatarSelect override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { if (allGranted(grantResults)) { if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA) { - onTypeSelected(AvatarSelectorView.Type.CAMERA) + onAvatarTypeSelected(true) } } } @@ -404,24 +402,24 @@ class VectorSettingsGeneralFragment : VectorSettingsBaseFragment(), AvatarSelect * Update the avatar. */ private fun onUpdateAvatarClick() { - if (!::avatarSelector.isInitialized) { - avatarSelector = AvatarSelectorView(activity!!, activity!!.layoutInflater, this) - } - mUserAvatarPreference.mAvatarView?.let { - avatarSelector.show(it, false) - } + AlertDialog + .Builder(requireContext()) + .setItems(arrayOf( + getString(R.string.attachment_type_camera), + getString(R.string.attachment_type_gallery) + )) { dialog, which -> + dialog.cancel() + onAvatarTypeSelected(isCamera = (which == 0)) + }.show() } - override fun onTypeSelected(type: AvatarSelectorView.Type) { - when (type) { - AvatarSelectorView.Type.CAMERA -> { - if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { - avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) - } - } - AvatarSelectorView.Type.GALLERY -> { - MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) + private fun onAvatarTypeSelected(isCamera: Boolean) { + if (isCamera) { + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { + avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(this) } + } else { + MultiPicker.get(MultiPicker.IMAGE).single().startWith(this) } }