mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-15 01:35:07 +08:00
Room profile: BigImageViewerActivity now only display the image. Use the room setting to change or delete the room Avatar
This commit is contained in:
parent
89fa345140
commit
b78dac20c0
@ -10,6 +10,7 @@ Improvements 🙌:
|
|||||||
- Add option to send with enter (#1195)
|
- Add option to send with enter (#1195)
|
||||||
- Use Hardware keyboard enter to send message (use shift-enter for new line) (#1881, #1440)
|
- Use Hardware keyboard enter to send message (use shift-enter for new line) (#1881, #1440)
|
||||||
- Edit and remove icons are now visible on image attachment preview screen (#2294)
|
- Edit and remove icons are now visible on image attachment preview screen (#2294)
|
||||||
|
- Room profile: BigImageViewerActivity now only display the image. Use the room setting to change or delete the room Avatar
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Messages encrypted with no way to decrypt after SDK update from 0.18 to 1.0.0 (#2252)
|
- Messages encrypted with no way to decrypt after SDK update from 0.18 to 1.0.0 (#2252)
|
||||||
|
@ -142,6 +142,10 @@ class RxRoom(private val room: Room) {
|
|||||||
fun updateAvatar(avatarUri: Uri, fileName: String): Completable = completableBuilder<Unit> {
|
fun updateAvatar(avatarUri: Uri, fileName: String): Completable = completableBuilder<Unit> {
|
||||||
room.updateAvatar(avatarUri, fileName, it)
|
room.updateAvatar(avatarUri, fileName, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteAvatar(): Completable = completableBuilder<Unit> {
|
||||||
|
room.deleteAvatar(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Room.rx(): RxRoom {
|
fun Room.rx(): RxRoom {
|
||||||
|
@ -58,6 +58,11 @@ interface StateService {
|
|||||||
*/
|
*/
|
||||||
fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback<Unit>): Cancelable
|
fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the avatar of the room
|
||||||
|
*/
|
||||||
|
fun deleteAvatar(callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
fun sendStateEvent(eventType: String, stateKey: String?, body: JsonDict, callback: MatrixCallback<Unit>): Cancelable
|
fun sendStateEvent(eventType: String, stateKey: String?, body: JsonDict, callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
fun getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event?
|
fun getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event?
|
||||||
|
@ -140,4 +140,15 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun deleteAvatar(callback: MatrixCallback<Unit>): Cancelable {
|
||||||
|
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||||
|
sendStateEvent(
|
||||||
|
eventType = EventType.STATE_ROOM_AVATAR,
|
||||||
|
body = emptyMap(),
|
||||||
|
callback = callback,
|
||||||
|
stateKey = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,16 @@ import im.vector.app.core.epoxy.VectorEpoxyHolder
|
|||||||
import im.vector.app.core.epoxy.onClick
|
import im.vector.app.core.epoxy.onClick
|
||||||
import im.vector.app.core.glide.GlideApp
|
import im.vector.app.core.glide.GlideApp
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_editable_avatar)
|
@EpoxyModelClass(layout = R.layout.item_editable_avatar)
|
||||||
abstract class FormEditableAvatarItem : EpoxyModelWithHolder<FormEditableAvatarItem.Holder>() {
|
abstract class FormEditableAvatarItem : EpoxyModelWithHolder<FormEditableAvatarItem.Holder>() {
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
lateinit var avatarRenderer: AvatarRenderer
|
var avatarRenderer: AvatarRenderer? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var matrixItem: MatrixItem? = null
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var enabled: Boolean = true
|
var enabled: Boolean = true
|
||||||
@ -51,11 +55,15 @@ abstract class FormEditableAvatarItem : EpoxyModelWithHolder<FormEditableAvatarI
|
|||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
holder.imageContainer.onClick(clickListener?.takeIf { enabled })
|
holder.imageContainer.onClick(clickListener?.takeIf { enabled })
|
||||||
GlideApp.with(holder.image)
|
if (matrixItem != null) {
|
||||||
.load(imageUri)
|
avatarRenderer?.render(matrixItem!!, holder.image)
|
||||||
.apply(RequestOptions.circleCropTransform())
|
} else {
|
||||||
.into(holder.image)
|
GlideApp.with(holder.image)
|
||||||
holder.delete.isVisible = imageUri != null
|
.load(imageUri)
|
||||||
|
.apply(RequestOptions.circleCropTransform())
|
||||||
|
.into(holder.image)
|
||||||
|
}
|
||||||
|
holder.delete.isVisible = imageUri != null || matrixItem?.avatarUrl != null
|
||||||
holder.delete.onClick(deleteListener?.takeIf { enabled })
|
holder.delete.onClick(deleteListener?.takeIf { enabled })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,41 +16,25 @@
|
|||||||
|
|
||||||
package im.vector.app.features.media
|
package im.vector.app.features.media
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import com.yalantis.ucrop.UCrop
|
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.di.ScreenComponent
|
import im.vector.app.core.di.ScreenComponent
|
||||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
|
||||||
import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
|
|
||||||
import im.vector.app.core.utils.allGranted
|
|
||||||
import im.vector.app.core.utils.checkPermissions
|
|
||||||
import im.vector.lib.multipicker.MultiPicker
|
|
||||||
import im.vector.lib.multipicker.entity.MultiPickerImageType
|
|
||||||
import kotlinx.android.synthetic.main.activity_big_image_viewer.*
|
import kotlinx.android.synthetic.main.activity_big_image_viewer.*
|
||||||
import java.io.File
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple Activity to display an avatar in fullscreen
|
||||||
|
*/
|
||||||
class BigImageViewerActivity : VectorBaseActivity() {
|
class BigImageViewerActivity : VectorBaseActivity() {
|
||||||
@Inject lateinit var sessionHolder: ActiveSessionHolder
|
@Inject lateinit var sessionHolder: ActiveSessionHolder
|
||||||
@Inject lateinit var colorProvider: ColorProvider
|
@Inject lateinit var colorProvider: ColorProvider
|
||||||
|
|
||||||
private var uri: Uri? = null
|
|
||||||
|
|
||||||
override fun getMenuRes() = R.menu.vector_big_avatar_viewer
|
|
||||||
|
|
||||||
override fun injectWith(injector: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
@ -66,7 +50,7 @@ class BigImageViewerActivity : VectorBaseActivity() {
|
|||||||
setDisplayHomeAsUpEnabled(true)
|
setDisplayHomeAsUpEnabled(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
uri = sessionHolder.getSafeActiveSession()
|
val uri = sessionHolder.getSafeActiveSession()
|
||||||
?.contentUrlResolver()
|
?.contentUrlResolver()
|
||||||
?.resolveFullSize(intent.getStringExtra(EXTRA_IMAGE_URL))
|
?.resolveFullSize(intent.getStringExtra(EXTRA_IMAGE_URL))
|
||||||
?.toUri()
|
?.toUri()
|
||||||
@ -78,117 +62,14 @@ class BigImageViewerActivity : VectorBaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
|
||||||
menu.findItem(R.id.bigAvatarEditAction).isVisible = shouldShowEditAction()
|
|
||||||
return super.onPrepareOptionsMenu(menu)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
if (item.itemId == R.id.bigAvatarEditAction) {
|
|
||||||
showAvatarSelector()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun shouldShowEditAction(): Boolean {
|
|
||||||
return uri != null && intent.getBooleanExtra(EXTRA_CAN_EDIT_IMAGE, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showAvatarSelector() {
|
|
||||||
AlertDialog.Builder(this)
|
|
||||||
.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
|
|
||||||
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, takePhotoActivityResultLauncher)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onRoomAvatarSelected(image: MultiPickerImageType) {
|
|
||||||
val destinationFile = File(cacheDir, "${image.displayName}_edited_image_${System.currentTimeMillis()}")
|
|
||||||
val uri = image.contentUri
|
|
||||||
createUCropWithDefaultSettings(this, uri, destinationFile.toUri(), image.displayName)
|
|
||||||
.withAspectRatio(1f, 1f)
|
|
||||||
.start(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val takePhotoActivityResultLauncher = registerStartForActivityResult { activityResult ->
|
|
||||||
if (activityResult.resultCode == Activity.RESULT_OK) {
|
|
||||||
avatarCameraUri?.let { uri ->
|
|
||||||
MultiPicker.get(MultiPicker.CAMERA)
|
|
||||||
.getTakenPhoto(this, uri)
|
|
||||||
?.let {
|
|
||||||
onRoomAvatarSelected(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val pickImageActivityResultLauncher = registerStartForActivityResult { activityResult ->
|
|
||||||
if (activityResult.resultCode == Activity.RESULT_OK) {
|
|
||||||
MultiPicker
|
|
||||||
.get(MultiPicker.IMAGE)
|
|
||||||
.getSelectedFiles(this, activityResult.data)
|
|
||||||
.firstOrNull()?.let {
|
|
||||||
onRoomAvatarSelected(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
// TODO handle this one (Ucrop lib)
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
|
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
|
||||||
when (requestCode) {
|
|
||||||
UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
||||||
if (allGranted(grantResults)) {
|
|
||||||
when (requestCode) {
|
|
||||||
PERMISSION_REQUEST_CODE_LAUNCH_CAMERA -> onAvatarTypeSelected(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onAvatarCropped(uri: Uri?) {
|
|
||||||
if (uri != null) {
|
|
||||||
setResult(Activity.RESULT_OK, Intent().setData(uri))
|
|
||||||
this@BigImageViewerActivity.finish()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "Cannot retrieve cropped value", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val EXTRA_TITLE = "EXTRA_TITLE"
|
private const val EXTRA_TITLE = "EXTRA_TITLE"
|
||||||
private const val EXTRA_IMAGE_URL = "EXTRA_IMAGE_URL"
|
private const val EXTRA_IMAGE_URL = "EXTRA_IMAGE_URL"
|
||||||
private const val EXTRA_CAN_EDIT_IMAGE = "EXTRA_CAN_EDIT_IMAGE"
|
|
||||||
|
|
||||||
fun newIntent(context: Context, title: String?, imageUrl: String, canEditImage: Boolean = false): Intent {
|
fun newIntent(context: Context, title: String?, imageUrl: String): Intent {
|
||||||
return Intent(context, BigImageViewerActivity::class.java).apply {
|
return Intent(context, BigImageViewerActivity::class.java).apply {
|
||||||
putExtra(EXTRA_TITLE, title)
|
putExtra(EXTRA_TITLE, title)
|
||||||
putExtra(EXTRA_IMAGE_URL, imageUrl)
|
putExtra(EXTRA_IMAGE_URL, imageUrl)
|
||||||
putExtra(EXTRA_CAN_EDIT_IMAGE, canEditImage)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,12 @@
|
|||||||
|
|
||||||
package im.vector.app.features.roomprofile
|
package im.vector.app.features.roomprofile
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import im.vector.app.core.platform.VectorViewModelAction
|
import im.vector.app.core.platform.VectorViewModelAction
|
||||||
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||||
|
|
||||||
sealed class RoomProfileAction : VectorViewModelAction {
|
sealed class RoomProfileAction : VectorViewModelAction {
|
||||||
object LeaveRoom : RoomProfileAction()
|
object LeaveRoom : RoomProfileAction()
|
||||||
data class ChangeRoomNotificationState(val notificationState: RoomNotificationState) : RoomProfileAction()
|
data class ChangeRoomNotificationState(val notificationState: RoomNotificationState) : RoomProfileAction()
|
||||||
data class ChangeRoomAvatar(val uri: Uri, val fileName: String?) : RoomProfileAction()
|
|
||||||
object ShareRoomProfile : RoomProfileAction()
|
object ShareRoomProfile : RoomProfileAction()
|
||||||
object CreateShortcut : RoomProfileAction()
|
object CreateShortcut : RoomProfileAction()
|
||||||
}
|
}
|
||||||
|
@ -17,35 +17,26 @@
|
|||||||
|
|
||||||
package im.vector.app.features.roomprofile
|
package im.vector.app.features.roomprofile
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.app.ActivityOptionsCompat
|
import androidx.core.app.ActivityOptionsCompat
|
||||||
import androidx.core.content.pm.ShortcutManagerCompat
|
import androidx.core.content.pm.ShortcutManagerCompat
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import com.yalantis.ucrop.UCrop
|
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.animations.AppBarStateChangeListener
|
import im.vector.app.core.animations.AppBarStateChangeListener
|
||||||
import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener
|
import im.vector.app.core.animations.MatrixItemAppBarStateChangeListener
|
||||||
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
|
|
||||||
import im.vector.app.core.extensions.cleanup
|
import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.extensions.configureWith
|
import im.vector.app.core.extensions.configureWith
|
||||||
import im.vector.app.core.extensions.copyOnLongClick
|
import im.vector.app.core.extensions.copyOnLongClick
|
||||||
import im.vector.app.core.extensions.exhaustive
|
import im.vector.app.core.extensions.exhaustive
|
||||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
|
||||||
import im.vector.app.core.extensions.setTextOrHide
|
import im.vector.app.core.extensions.setTextOrHide
|
||||||
import im.vector.app.core.intent.getFilenameFromUri
|
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.utils.copyToClipboard
|
import im.vector.app.core.utils.copyToClipboard
|
||||||
import im.vector.app.core.utils.startSharePlainTextIntent
|
import im.vector.app.core.utils.startSharePlainTextIntent
|
||||||
@ -56,8 +47,6 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomS
|
|||||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
|
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
|
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
|
||||||
import im.vector.app.features.media.BigImageViewerActivity
|
import im.vector.app.features.media.BigImageViewerActivity
|
||||||
import im.vector.app.features.media.createUCropWithDefaultSettings
|
|
||||||
import im.vector.lib.multipicker.entity.MultiPickerImageType
|
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.fragment_matrix_profile.*
|
import kotlinx.android.synthetic.main.fragment_matrix_profile.*
|
||||||
import kotlinx.android.synthetic.main.view_stub_room_profile_header.*
|
import kotlinx.android.synthetic.main.view_stub_room_profile_header.*
|
||||||
@ -65,7 +54,6 @@ import org.matrix.android.sdk.api.session.room.notification.RoomNotificationStat
|
|||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@ -78,8 +66,7 @@ class RoomProfileFragment @Inject constructor(
|
|||||||
private val avatarRenderer: AvatarRenderer,
|
private val avatarRenderer: AvatarRenderer,
|
||||||
val roomProfileViewModelFactory: RoomProfileViewModel.Factory
|
val roomProfileViewModelFactory: RoomProfileViewModel.Factory
|
||||||
) : VectorBaseFragment(),
|
) : VectorBaseFragment(),
|
||||||
RoomProfileController.Callback,
|
RoomProfileController.Callback {
|
||||||
GalleryOrCameraDialogHelper.Listener {
|
|
||||||
|
|
||||||
private val roomProfileArgs: RoomProfileArgs by args()
|
private val roomProfileArgs: RoomProfileArgs by args()
|
||||||
private lateinit var roomListQuickActionsSharedActionViewModel: RoomListQuickActionsSharedActionViewModel
|
private lateinit var roomListQuickActionsSharedActionViewModel: RoomListQuickActionsSharedActionViewModel
|
||||||
@ -92,8 +79,6 @@ class RoomProfileFragment @Inject constructor(
|
|||||||
|
|
||||||
override fun getMenuRes() = R.menu.vector_room_profile
|
override fun getMenuRes() = R.menu.vector_room_profile
|
||||||
|
|
||||||
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this)
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
roomListQuickActionsSharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
|
roomListQuickActionsSharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
|
||||||
@ -113,11 +98,10 @@ class RoomProfileFragment @Inject constructor(
|
|||||||
matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
|
matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
|
||||||
roomProfileViewModel.observeViewEvents {
|
roomProfileViewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
is RoomProfileViewEvents.Loading -> showLoading(it.message)
|
is RoomProfileViewEvents.Loading -> showLoading(it.message)
|
||||||
is RoomProfileViewEvents.Failure -> showFailure(it.throwable)
|
is RoomProfileViewEvents.Failure -> showFailure(it.throwable)
|
||||||
is RoomProfileViewEvents.ShareRoomProfile -> onShareRoomProfile(it.permalink)
|
is RoomProfileViewEvents.ShareRoomProfile -> onShareRoomProfile(it.permalink)
|
||||||
RoomProfileViewEvents.OnChangeAvatarSuccess -> dismissLoadingDialog()
|
is RoomProfileViewEvents.OnShortcutReady -> addShortcut(it)
|
||||||
is RoomProfileViewEvents.OnShortcutReady -> addShortcut(it)
|
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
roomListQuickActionsSharedActionViewModel
|
roomListQuickActionsSharedActionViewModel
|
||||||
@ -158,14 +142,6 @@ class RoomProfileFragment @Inject constructor(
|
|||||||
else -> Timber.v("$action not handled")
|
else -> Timber.v("$action not handled")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onLeaveRoom() {
|
|
||||||
vectorBaseActivity.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showError(throwable: Throwable) {
|
|
||||||
showErrorInSnackbar(throwable)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
roomProfileController.callback = this
|
roomProfileController.callback = this
|
||||||
matrixProfileRecyclerView.configureWith(roomProfileController, hasFixedSize = true, disableItemAnimation = true)
|
matrixProfileRecyclerView.configureWith(roomProfileController, hasFixedSize = true, disableItemAnimation = true)
|
||||||
@ -269,45 +245,9 @@ class RoomProfileFragment @Inject constructor(
|
|||||||
|
|
||||||
private fun onAvatarClicked(view: View, matrixItem: MatrixItem.RoomItem) = withState(roomProfileViewModel) {
|
private fun onAvatarClicked(view: View, matrixItem: MatrixItem.RoomItem) = withState(roomProfileViewModel) {
|
||||||
if (matrixItem.avatarUrl?.isNotEmpty() == true) {
|
if (matrixItem.avatarUrl?.isNotEmpty() == true) {
|
||||||
val intent = BigImageViewerActivity.newIntent(requireContext(), matrixItem.getBestName(), matrixItem.avatarUrl!!, it.canChangeAvatar)
|
val intent = BigImageViewerActivity.newIntent(requireContext(), matrixItem.getBestName(), matrixItem.avatarUrl!!)
|
||||||
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), view, ViewCompat.getTransitionName(view) ?: "")
|
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), view, ViewCompat.getTransitionName(view) ?: "")
|
||||||
bigImageStartForActivityResult.launch(intent, options)
|
startActivity(intent, options.toBundle())
|
||||||
} else if (it.canChangeAvatar) {
|
|
||||||
galleryOrCameraDialogHelper.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onImageReady(image: MultiPickerImageType) {
|
|
||||||
val destinationFile = File(requireContext().cacheDir, "${image.displayName}_edited_image_${System.currentTimeMillis()}")
|
|
||||||
val uri = image.contentUri
|
|
||||||
createUCropWithDefaultSettings(requireContext(), uri, destinationFile.toUri(), image.displayName)
|
|
||||||
.withAspectRatio(1f, 1f)
|
|
||||||
.start(requireContext(), this)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val bigImageStartForActivityResult = registerStartForActivityResult { activityResult ->
|
|
||||||
if (activityResult.resultCode == Activity.RESULT_OK) {
|
|
||||||
activityResult.data?.let { onAvatarCropped(it.data) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
// TODO handle this one (Ucrop lib)
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
|
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
|
||||||
when (requestCode) {
|
|
||||||
UCrop.REQUEST_CROP -> data?.let { onAvatarCropped(UCrop.getOutput(it)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onAvatarCropped(uri: Uri?) {
|
|
||||||
if (uri != null) {
|
|
||||||
roomProfileViewModel.handle(RoomProfileAction.ChangeRoomAvatar(uri, getFilenameFromUri(context, uri)))
|
|
||||||
} else {
|
|
||||||
Toast.makeText(requireContext(), "Cannot retrieve cropped value", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ sealed class RoomProfileViewEvents : VectorViewEvents {
|
|||||||
data class Loading(val message: CharSequence? = null) : RoomProfileViewEvents()
|
data class Loading(val message: CharSequence? = null) : RoomProfileViewEvents()
|
||||||
data class Failure(val throwable: Throwable) : RoomProfileViewEvents()
|
data class Failure(val throwable: Throwable) : RoomProfileViewEvents()
|
||||||
|
|
||||||
object OnChangeAvatarSuccess : RoomProfileViewEvents()
|
|
||||||
data class ShareRoomProfile(val permalink: String) : RoomProfileViewEvents()
|
data class ShareRoomProfile(val permalink: String) : RoomProfileViewEvents()
|
||||||
data class OnShortcutReady(val shortcutInfo: ShortcutInfoCompat) : RoomProfileViewEvents()
|
data class OnShortcutReady(val shortcutInfo: ShortcutInfoCompat) : RoomProfileViewEvents()
|
||||||
}
|
}
|
||||||
|
@ -28,18 +28,15 @@ import im.vector.app.core.extensions.exhaustive
|
|||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.home.ShortcutCreator
|
import im.vector.app.features.home.ShortcutCreator
|
||||||
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
|
||||||
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
|
import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
import org.matrix.android.sdk.rx.RxRoom
|
||||||
import org.matrix.android.sdk.rx.rx
|
import org.matrix.android.sdk.rx.rx
|
||||||
import org.matrix.android.sdk.rx.unwrap
|
import org.matrix.android.sdk.rx.unwrap
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
class RoomProfileViewModel @AssistedInject constructor(
|
class RoomProfileViewModel @AssistedInject constructor(
|
||||||
@Assisted private val initialState: RoomProfileViewState,
|
@Assisted private val initialState: RoomProfileViewState,
|
||||||
@ -65,33 +62,23 @@ class RoomProfileViewModel @AssistedInject constructor(
|
|||||||
private val room = session.getRoom(initialState.roomId)!!
|
private val room = session.getRoom(initialState.roomId)!!
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observeRoomSummary()
|
val rxRoom = room.rx()
|
||||||
|
observeRoomSummary(rxRoom)
|
||||||
|
observeBannedRoomMembers(rxRoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeRoomSummary() {
|
private fun observeRoomSummary(rxRoom: RxRoom) {
|
||||||
val rxRoom = room.rx()
|
|
||||||
rxRoom.liveRoomSummary()
|
rxRoom.liveRoomSummary()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.execute {
|
.execute {
|
||||||
copy(roomSummary = it)
|
copy(roomSummary = it)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val powerLevelsContentLive = PowerLevelsObservableFactory(room).createObservable()
|
private fun observeBannedRoomMembers(rxRoom: RxRoom) {
|
||||||
|
|
||||||
powerLevelsContentLive
|
|
||||||
.subscribe {
|
|
||||||
val powerLevelsHelper = PowerLevelsHelper(it)
|
|
||||||
setState {
|
|
||||||
copy(canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.disposeOnClear()
|
|
||||||
|
|
||||||
rxRoom.liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) })
|
rxRoom.liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) })
|
||||||
.execute {
|
.execute {
|
||||||
copy(
|
copy(bannedMembership = it)
|
||||||
bannedMembership = it
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +87,6 @@ class RoomProfileViewModel @AssistedInject constructor(
|
|||||||
RoomProfileAction.LeaveRoom -> handleLeaveRoom()
|
RoomProfileAction.LeaveRoom -> handleLeaveRoom()
|
||||||
is RoomProfileAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action)
|
is RoomProfileAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action)
|
||||||
is RoomProfileAction.ShareRoomProfile -> handleShareRoomProfile()
|
is RoomProfileAction.ShareRoomProfile -> handleShareRoomProfile()
|
||||||
is RoomProfileAction.ChangeRoomAvatar -> handleChangeAvatar(action)
|
|
||||||
RoomProfileAction.CreateShortcut -> handleCreateShortcut()
|
RoomProfileAction.CreateShortcut -> handleCreateShortcut()
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
@ -142,18 +128,4 @@ class RoomProfileViewModel @AssistedInject constructor(
|
|||||||
_viewEvents.post(RoomProfileViewEvents.ShareRoomProfile(permalink))
|
_viewEvents.post(RoomProfileViewEvents.ShareRoomProfile(permalink))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleChangeAvatar(action: RoomProfileAction.ChangeRoomAvatar) {
|
|
||||||
_viewEvents.post(RoomProfileViewEvents.Loading())
|
|
||||||
room.rx().updateAvatar(action.uri, action.fileName ?: UUID.randomUUID().toString())
|
|
||||||
.subscribe(
|
|
||||||
{
|
|
||||||
_viewEvents.post(RoomProfileViewEvents.OnChangeAvatarSuccess)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_viewEvents.post(RoomProfileViewEvents.Failure(it))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.disposeOnClear()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
|||||||
data class RoomProfileViewState(
|
data class RoomProfileViewState(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val roomSummary: Async<RoomSummary> = Uninitialized,
|
val roomSummary: Async<RoomSummary> = Uninitialized,
|
||||||
val bannedMembership: Async<List<RoomMemberSummary>> = Uninitialized,
|
val bannedMembership: Async<List<RoomMemberSummary>> = Uninitialized
|
||||||
val canChangeAvatar: Boolean = false
|
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
|
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
|
||||||
|
@ -20,6 +20,7 @@ import im.vector.app.core.platform.VectorViewModelAction
|
|||||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||||
|
|
||||||
sealed class RoomSettingsAction : VectorViewModelAction {
|
sealed class RoomSettingsAction : VectorViewModelAction {
|
||||||
|
data class SetAvatarAction(val avatarAction: RoomSettingsViewState.AvatarAction) : RoomSettingsAction()
|
||||||
data class SetRoomName(val newName: String) : RoomSettingsAction()
|
data class SetRoomName(val newName: String) : RoomSettingsAction()
|
||||||
data class SetRoomTopic(val newTopic: String) : RoomSettingsAction()
|
data class SetRoomTopic(val newTopic: String) : RoomSettingsAction()
|
||||||
data class SetRoomHistoryVisibility(val visibility: RoomHistoryVisibility) : RoomSettingsAction()
|
data class SetRoomHistoryVisibility(val visibility: RoomHistoryVisibility) : RoomSettingsAction()
|
||||||
|
@ -23,20 +23,27 @@ import im.vector.app.core.epoxy.profiles.buildProfileSection
|
|||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.form.formEditTextItem
|
import im.vector.app.features.form.formEditTextItem
|
||||||
|
import im.vector.app.features.form.formEditableAvatarItem
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter
|
import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent
|
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RoomSettingsController @Inject constructor(
|
class RoomSettingsController @Inject constructor(
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
|
private val avatarRenderer: AvatarRenderer,
|
||||||
private val roomHistoryVisibilityFormatter: RoomHistoryVisibilityFormatter,
|
private val roomHistoryVisibilityFormatter: RoomHistoryVisibilityFormatter,
|
||||||
colorProvider: ColorProvider
|
colorProvider: ColorProvider
|
||||||
) : TypedEpoxyController<RoomSettingsViewState>() {
|
) : TypedEpoxyController<RoomSettingsViewState>() {
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
|
// Delete the avatar, or cancel an avatar change
|
||||||
|
fun onAvatarDelete()
|
||||||
|
fun onAvatarChange()
|
||||||
fun onEnableEncryptionClicked()
|
fun onEnableEncryptionClicked()
|
||||||
fun onNameChanged(name: String)
|
fun onNameChanged(name: String)
|
||||||
fun onTopicChanged(topic: String)
|
fun onTopicChanged(topic: String)
|
||||||
@ -58,6 +65,24 @@ class RoomSettingsController @Inject constructor(
|
|||||||
val historyVisibility = data.historyVisibilityEvent?.let { formatRoomHistoryVisibilityEvent(it) } ?: ""
|
val historyVisibility = data.historyVisibilityEvent?.let { formatRoomHistoryVisibilityEvent(it) } ?: ""
|
||||||
val newHistoryVisibility = data.newHistoryVisibility?.let { roomHistoryVisibilityFormatter.format(it) }
|
val newHistoryVisibility = data.newHistoryVisibility?.let { roomHistoryVisibilityFormatter.format(it) }
|
||||||
|
|
||||||
|
formEditableAvatarItem {
|
||||||
|
id("avatar")
|
||||||
|
enabled(data.actionPermissions.canChangeAvatar)
|
||||||
|
when (val avatarAction = data.avatarAction) {
|
||||||
|
RoomSettingsViewState.AvatarAction.None -> {
|
||||||
|
// Use the current value
|
||||||
|
avatarRenderer(avatarRenderer)
|
||||||
|
matrixItem(roomSummary.toMatrixItem())
|
||||||
|
}
|
||||||
|
RoomSettingsViewState.AvatarAction.DeleteAvatar ->
|
||||||
|
imageUri(null)
|
||||||
|
is RoomSettingsViewState.AvatarAction.UpdateAvatar ->
|
||||||
|
imageUri(avatarAction.newAvatarUri)
|
||||||
|
}
|
||||||
|
clickListener { callback?.onAvatarChange() }
|
||||||
|
deleteListener { callback?.onAvatarDelete() }
|
||||||
|
}
|
||||||
|
|
||||||
buildProfileSection(
|
buildProfileSection(
|
||||||
stringProvider.getString(R.string.settings)
|
stringProvider.getString(R.string.settings)
|
||||||
)
|
)
|
||||||
|
@ -16,30 +16,40 @@
|
|||||||
|
|
||||||
package im.vector.app.features.roomprofile.settings
|
package im.vector.app.features.roomprofile.settings
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
|
import com.yalantis.ucrop.UCrop
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
|
||||||
import im.vector.app.core.extensions.cleanup
|
import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.extensions.configureWith
|
import im.vector.app.core.extensions.configureWith
|
||||||
import im.vector.app.core.extensions.exhaustive
|
import im.vector.app.core.extensions.exhaustive
|
||||||
|
import im.vector.app.core.intent.getFilenameFromUri
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.utils.toast
|
import im.vector.app.core.utils.toast
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter
|
import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter
|
||||||
|
import im.vector.app.features.media.createUCropWithDefaultSettings
|
||||||
import im.vector.app.features.roomprofile.RoomProfileArgs
|
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||||
|
import im.vector.lib.multipicker.entity.MultiPickerImageType
|
||||||
import kotlinx.android.synthetic.main.fragment_room_setting_generic.*
|
import kotlinx.android.synthetic.main.fragment_room_setting_generic.*
|
||||||
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
|
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent
|
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import java.io.File
|
||||||
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RoomSettingsFragment @Inject constructor(
|
class RoomSettingsFragment @Inject constructor(
|
||||||
@ -47,10 +57,14 @@ class RoomSettingsFragment @Inject constructor(
|
|||||||
private val controller: RoomSettingsController,
|
private val controller: RoomSettingsController,
|
||||||
private val roomHistoryVisibilityFormatter: RoomHistoryVisibilityFormatter,
|
private val roomHistoryVisibilityFormatter: RoomHistoryVisibilityFormatter,
|
||||||
private val avatarRenderer: AvatarRenderer
|
private val avatarRenderer: AvatarRenderer
|
||||||
) : VectorBaseFragment(), RoomSettingsController.Callback {
|
) :
|
||||||
|
VectorBaseFragment(),
|
||||||
|
RoomSettingsController.Callback,
|
||||||
|
GalleryOrCameraDialogHelper.Listener {
|
||||||
|
|
||||||
private val viewModel: RoomSettingsViewModel by fragmentViewModel()
|
private val viewModel: RoomSettingsViewModel by fragmentViewModel()
|
||||||
private val roomProfileArgs: RoomProfileArgs by args()
|
private val roomProfileArgs: RoomProfileArgs by args()
|
||||||
|
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this)
|
||||||
|
|
||||||
override fun getLayoutResId() = R.layout.fragment_room_setting_generic
|
override fun getLayoutResId() = R.layout.fragment_room_setting_generic
|
||||||
|
|
||||||
@ -161,4 +175,56 @@ class RoomSettingsFragment @Inject constructor(
|
|||||||
override fun onAliasChanged(alias: String) {
|
override fun onAliasChanged(alias: String) {
|
||||||
viewModel.handle(RoomSettingsAction.SetRoomCanonicalAlias(alias))
|
viewModel.handle(RoomSettingsAction.SetRoomCanonicalAlias(alias))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onImageReady(image: MultiPickerImageType) {
|
||||||
|
val destinationFile = File(requireContext().cacheDir, "${image.displayName}_edited_image_${System.currentTimeMillis()}")
|
||||||
|
val uri = image.contentUri
|
||||||
|
createUCropWithDefaultSettings(requireContext(), uri, destinationFile.toUri(), image.displayName)
|
||||||
|
.withAspectRatio(1f, 1f)
|
||||||
|
.start(requireContext(), this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAvatarDelete() {
|
||||||
|
withState(viewModel) {
|
||||||
|
when (it.avatarAction) {
|
||||||
|
RoomSettingsViewState.AvatarAction.None -> {
|
||||||
|
viewModel.handle(RoomSettingsAction.SetAvatarAction(RoomSettingsViewState.AvatarAction.DeleteAvatar))
|
||||||
|
}
|
||||||
|
RoomSettingsViewState.AvatarAction.DeleteAvatar -> {
|
||||||
|
/* Should not happen */
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
is RoomSettingsViewState.AvatarAction.UpdateAvatar -> {
|
||||||
|
// Cancel the update of the avatar
|
||||||
|
viewModel.handle(RoomSettingsAction.SetAvatarAction(RoomSettingsViewState.AvatarAction.None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAvatarChange() {
|
||||||
|
galleryOrCameraDialogHelper.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
// TODO handle this one (Ucrop lib)
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
when (requestCode) {
|
||||||
|
UCrop.REQUEST_CROP -> {
|
||||||
|
val uri = data?.let { UCrop.getOutput(it) } ?: return
|
||||||
|
viewModel.handle(RoomSettingsAction.SetAvatarAction(
|
||||||
|
RoomSettingsViewState.AvatarAction.UpdateAvatar(
|
||||||
|
newAvatarUri = uri,
|
||||||
|
newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO BMA Handle Back with unsaved data
|
||||||
}
|
}
|
||||||
|
@ -60,11 +60,13 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
|
|
||||||
private fun observeState() {
|
private fun observeState() {
|
||||||
selectSubscribe(
|
selectSubscribe(
|
||||||
|
RoomSettingsViewState::avatarAction,
|
||||||
RoomSettingsViewState::newName,
|
RoomSettingsViewState::newName,
|
||||||
RoomSettingsViewState::newCanonicalAlias,
|
RoomSettingsViewState::newCanonicalAlias,
|
||||||
RoomSettingsViewState::newTopic,
|
RoomSettingsViewState::newTopic,
|
||||||
RoomSettingsViewState::newHistoryVisibility,
|
RoomSettingsViewState::newHistoryVisibility,
|
||||||
RoomSettingsViewState::roomSummary) { newName,
|
RoomSettingsViewState::roomSummary) { avatarAction,
|
||||||
|
newName,
|
||||||
newCanonicalAlias,
|
newCanonicalAlias,
|
||||||
newTopic,
|
newTopic,
|
||||||
newHistoryVisibility,
|
newHistoryVisibility,
|
||||||
@ -72,7 +74,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
val summary = asyncSummary()
|
val summary = asyncSummary()
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
showSaveAction = summary?.name != newName
|
showSaveAction = avatarAction !is RoomSettingsViewState.AvatarAction.None
|
||||||
|
|| summary?.name != newName
|
||||||
|| summary?.topic != newTopic
|
|| summary?.topic != newTopic
|
||||||
|| summary?.canonicalAlias != newCanonicalAlias?.takeIf { it.isNotEmpty() }
|
|| summary?.canonicalAlias != newCanonicalAlias?.takeIf { it.isNotEmpty() }
|
||||||
|| newHistoryVisibility != null
|
|| newHistoryVisibility != null
|
||||||
@ -101,6 +104,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
.subscribe {
|
.subscribe {
|
||||||
val powerLevelsHelper = PowerLevelsHelper(it)
|
val powerLevelsHelper = PowerLevelsHelper(it)
|
||||||
val permissions = RoomSettingsViewState.ActionPermissions(
|
val permissions = RoomSettingsViewState.ActionPermissions(
|
||||||
|
canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR),
|
||||||
canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME),
|
canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME),
|
||||||
canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC),
|
canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC),
|
||||||
canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true,
|
canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true,
|
||||||
@ -117,6 +121,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
override fun handle(action: RoomSettingsAction) {
|
override fun handle(action: RoomSettingsAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomSettingsAction.EnableEncryption -> handleEnableEncryption()
|
is RoomSettingsAction.EnableEncryption -> handleEnableEncryption()
|
||||||
|
is RoomSettingsAction.SetAvatarAction -> setState { copy(avatarAction = action.avatarAction) }
|
||||||
is RoomSettingsAction.SetRoomName -> setState { copy(newName = action.newName) }
|
is RoomSettingsAction.SetRoomName -> setState { copy(newName = action.newName) }
|
||||||
is RoomSettingsAction.SetRoomTopic -> setState { copy(newTopic = action.newTopic) }
|
is RoomSettingsAction.SetRoomTopic -> setState { copy(newTopic = action.newTopic) }
|
||||||
is RoomSettingsAction.SetRoomHistoryVisibility -> setState { copy(newHistoryVisibility = action.visibility) }
|
is RoomSettingsAction.SetRoomHistoryVisibility -> setState { copy(newHistoryVisibility = action.visibility) }
|
||||||
@ -132,6 +137,15 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
|
|
||||||
val summary = state.roomSummary.invoke()
|
val summary = state.roomSummary.invoke()
|
||||||
|
|
||||||
|
when (val avatarAction = state.avatarAction) {
|
||||||
|
RoomSettingsViewState.AvatarAction.None -> Unit
|
||||||
|
RoomSettingsViewState.AvatarAction.DeleteAvatar -> {
|
||||||
|
operationList.add(room.rx().deleteAvatar())
|
||||||
|
}
|
||||||
|
is RoomSettingsViewState.AvatarAction.UpdateAvatar -> {
|
||||||
|
operationList.add(room.rx().updateAvatar(avatarAction.newAvatarUri, avatarAction.newAvatarFileName))
|
||||||
|
}
|
||||||
|
}
|
||||||
if (summary?.name != state.newName) {
|
if (summary?.name != state.newName) {
|
||||||
operationList.add(room.rx().updateName(state.newName ?: ""))
|
operationList.add(room.rx().updateName(state.newName ?: ""))
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package im.vector.app.features.roomprofile.settings
|
package im.vector.app.features.roomprofile.settings
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
@ -29,6 +30,7 @@ data class RoomSettingsViewState(
|
|||||||
val historyVisibilityEvent: Event? = null,
|
val historyVisibilityEvent: Event? = null,
|
||||||
val roomSummary: Async<RoomSummary> = Uninitialized,
|
val roomSummary: Async<RoomSummary> = Uninitialized,
|
||||||
val isLoading: Boolean = false,
|
val isLoading: Boolean = false,
|
||||||
|
val avatarAction: AvatarAction = AvatarAction.None,
|
||||||
val newName: String? = null,
|
val newName: String? = null,
|
||||||
val newTopic: String? = null,
|
val newTopic: String? = null,
|
||||||
val newHistoryVisibility: RoomHistoryVisibility? = null,
|
val newHistoryVisibility: RoomHistoryVisibility? = null,
|
||||||
@ -40,10 +42,18 @@ data class RoomSettingsViewState(
|
|||||||
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
|
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
|
||||||
|
|
||||||
data class ActionPermissions(
|
data class ActionPermissions(
|
||||||
|
val canChangeAvatar: Boolean = false,
|
||||||
val canChangeName: Boolean = false,
|
val canChangeName: Boolean = false,
|
||||||
val canChangeTopic: Boolean = false,
|
val canChangeTopic: Boolean = false,
|
||||||
val canChangeCanonicalAlias: Boolean = false,
|
val canChangeCanonicalAlias: Boolean = false,
|
||||||
val canChangeHistoryReadability: Boolean = false,
|
val canChangeHistoryReadability: Boolean = false,
|
||||||
val canEnableEncryption: Boolean = false
|
val canEnableEncryption: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sealed class AvatarAction {
|
||||||
|
object None : AvatarAction()
|
||||||
|
object DeleteAvatar : AvatarAction()
|
||||||
|
data class UpdateAvatar(val newAvatarUri: Uri,
|
||||||
|
val newAvatarFileName: String) : AvatarAction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<item
|
|
||||||
android:id="@+id/bigAvatarEditAction"
|
|
||||||
android:title="@string/edit"
|
|
||||||
android:icon="@drawable/ic_edit"
|
|
||||||
app:iconTint="?attr/colorAccent"
|
|
||||||
app:showAsAction="ifRoom" />
|
|
||||||
</menu>
|
|
Loading…
Reference in New Issue
Block a user