Fix issues with matrix.to deep linking

This commit is contained in:
Valere 2020-11-25 15:19:49 +01:00
parent bcd86977d2
commit 804afc9a1d
11 changed files with 511 additions and 58 deletions

View File

@ -81,7 +81,8 @@
android:resource="@xml/shortcuts" />
</activity-alias>
<activity android:name=".features.home.HomeActivity" />
<activity android:name=".features.home.HomeActivity"
android:launchMode="singleTask"/>
<activity
android:name=".features.login.LoginActivity"
android:launchMode="singleTask"
@ -189,10 +190,9 @@
<activity
android:name=".features.signout.soft.SoftLogoutActivity"
android:windowSoftInputMode="adjustResize" />
<activity android:name=".features.permalink.PermalinkHandlerActivity">
<activity android:name=".features.permalink.PermalinkHandlerActivity" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

View File

@ -38,8 +38,12 @@ import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.platform.ToolbarConfigurable
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.utils.toast
import im.vector.app.features.disclaimer.showDisclaimerDialog
import im.vector.app.features.matrixto.MatrixToBottomSheet
import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.permalink.NavigationInterceptor
import im.vector.app.features.permalink.PermalinkHandler
import im.vector.app.features.popup.DefaultVectorAlert
import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.popup.VerificationVectorAlert
@ -50,10 +54,12 @@ import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
import im.vector.app.features.workers.signout.ServerBackupStatusViewState
import im.vector.app.push.fcm.FcmHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import org.matrix.android.sdk.api.session.InitialSyncProgressService
import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import org.matrix.android.sdk.api.util.MatrixItem
import timber.log.Timber
import javax.inject.Inject
@ -64,7 +70,8 @@ data class HomeActivityArgs(
val accountCreation: Boolean
) : Parcelable
class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDetectorSharedViewModel.Factory, ServerBackupStatusViewModel.Factory {
class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDetectorSharedViewModel.Factory, ServerBackupStatusViewModel.Factory,
NavigationInterceptor {
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
@ -82,6 +89,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
@Inject lateinit var popupAlertManager: PopupAlertManager
@Inject lateinit var shortcutsHandler: ShortcutsHandler
@Inject lateinit var unknownDeviceViewModelFactory: UnknownDeviceDetectorSharedViewModel.Factory
@Inject lateinit var permalinkHandler: PermalinkHandler
private val drawerListener = object : DrawerLayout.SimpleDrawerListener() {
override fun onDrawerStateChanged(newState: Int) {
@ -117,9 +125,9 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
.observe()
.subscribe { sharedAction ->
when (sharedAction) {
is HomeActivitySharedAction.OpenDrawer -> drawerLayout.openDrawer(GravityCompat.START)
is HomeActivitySharedAction.OpenDrawer -> drawerLayout.openDrawer(GravityCompat.START)
is HomeActivitySharedAction.CloseDrawer -> drawerLayout.closeDrawer(GravityCompat.START)
is HomeActivitySharedAction.OpenGroup -> {
is HomeActivitySharedAction.OpenGroup -> {
drawerLayout.closeDrawer(GravityCompat.START)
replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true)
}
@ -136,20 +144,42 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
homeActivityViewModel.observeViewEvents {
when (it) {
is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it)
is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
}.exhaustive
}
homeActivityViewModel.subscribe(this) { renderState(it) }
shortcutsHandler.observeRoomsAndBuildShortcuts()
.disposeOnDestroy()
if (isFirstCreation()) {
handleIntent(intent)
}
}
private fun handleIntent(intent: Intent?) {
intent?.dataString?.let { deepLink ->
if (!deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE)) return@let
permalinkHandler.launch(this, deepLink,
navigationInterceptor = this,
buildTask = true)
// .delay(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { isHandled ->
if (!isHandled) {
toast(R.string.permalink_malformed)
}
}
.disposeOnDestroy()
}
}
private fun renderState(state: HomeActivityViewState) {
when (val status = state.initialSyncProgressServiceStatus) {
is InitialSyncProgressService.Status.Idle -> {
is InitialSyncProgressService.Status.Idle -> {
waiting_view.isVisible = false
}
is InitialSyncProgressService.Status.Progressing -> {
@ -270,6 +300,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
if (intent?.getParcelableExtra<HomeActivityArgs>(MvRx.KEY_ARG)?.clearNotification == true) {
notificationDrawerManager.clearAllEvents()
}
handleIntent(intent)
}
override fun onDestroy() {
@ -313,11 +344,11 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
bugReporter.openBugReportScreen(this, false)
return true
}
R.id.menu_home_filter -> {
R.id.menu_home_filter -> {
navigator.openRoomsFiltering(this)
return true
}
R.id.menu_home_setting -> {
R.id.menu_home_setting -> {
navigator.openSettings(this)
return true
}
@ -334,6 +365,18 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
}
}
override fun navToMemberProfile(userId: String): Boolean {
val listener = object : MatrixToBottomSheet.InteractionListener {
override fun navigateToRoom(roomId: String) {
navigator.openRoom(this@HomeActivity, roomId)
}
}
// TODO check if there is already one??
MatrixToBottomSheet.withUserId(userId, listener)
.show(supportFragmentManager, "HA#MatrixToBottomSheet")
return true
}
companion object {
fun newIntent(context: Context, clearNotification: Boolean = false, accountCreation: Boolean = false): Intent {
val args = HomeActivityArgs(

View File

@ -0,0 +1,24 @@
/*
* 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.app.features.matrixto
import im.vector.app.core.platform.VectorViewModelAction
import org.matrix.android.sdk.api.util.MatrixItem
sealed class MatrixToAction : VectorViewModelAction {
data class StartChattingWithUser(val matrixItem: MatrixItem) : MatrixToAction()
}

View File

@ -17,23 +17,38 @@
package im.vector.app.features.matrixto
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.R
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.features.home.AvatarRenderer
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.bottom_sheet_matrix_to_card.*
import org.matrix.android.sdk.api.util.MatrixItem
import javax.inject.Inject
class MatrixToBottomSheet(private val matrixItem: MatrixItem) : VectorBaseBottomSheetDialogFragment() {
class MatrixToBottomSheet : VectorBaseBottomSheetDialogFragment() {
@Parcelize
data class MatrixToArgs(
val matrixToLink: String?,
val userId: String?
) : Parcelable
@Inject lateinit var avatarRenderer: AvatarRenderer
interface InteractionListener {
fun didTapStartMessage(matrixItem: MatrixItem)
}
@Inject
lateinit var matrixToBottomSheetViewModelFactory: MatrixToBottomSheetViewModel.Factory
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
@ -43,21 +58,100 @@ class MatrixToBottomSheet(private val matrixItem: MatrixItem) : VectorBaseBottom
override fun getLayoutResId() = R.layout.bottom_sheet_matrix_to_card
private val viewModel by fragmentViewModel(MatrixToBottomSheetViewModel::class)
interface InteractionListener {
fun navigateToRoom(roomId: String)
}
override fun invalidate() = withState(viewModel) { state ->
super.invalidate()
when (val item = state.matrixItem) {
Uninitialized -> {
matrixToCardContentLoading.isVisible = false
matrixToCardUserContentVisibility.isVisible = false
}
is Loading -> {
matrixToCardContentLoading.isVisible = true
matrixToCardUserContentVisibility.isVisible = false
}
is Success -> {
matrixToCardContentLoading.isVisible = false
matrixToCardUserContentVisibility.isVisible = true
matrixToCardNameText.setTextOrHide(item.invoke().displayName)
matrixToCardUserIdText.setTextOrHide(item.invoke().id)
avatarRenderer.render(item.invoke(), matrixToCardAvatar)
}
is Fail -> {
// TODO display some error copy?
dismiss()
}
}
when (state.startChattingState) {
Uninitialized -> {
matrixToCardButtonLoading.isVisible = false
matrixToCardSendMessageButton.isVisible = false
}
is Success -> {
matrixToCardButtonLoading.isVisible = false
matrixToCardSendMessageButton.isVisible = true
}
is Fail -> {
matrixToCardButtonLoading.isVisible = false
matrixToCardSendMessageButton.isVisible = true
// TODO display some error copy?
dismiss()
}
is Loading -> {
matrixToCardButtonLoading.isVisible = true
matrixToCardSendMessageButton.isInvisible = true
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
matrixToCardSendMessageButton.debouncedClicks {
interactionListener?.didTapStartMessage(matrixItem)
dismiss()
withState(viewModel) {
it.matrixItem.invoke()?.let { item ->
viewModel.handle(MatrixToAction.StartChattingWithUser(item))
}
}
}
matrixToCardNameText.setTextOrHide(matrixItem.displayName)
matrixToCardUserIdText.setTextOrHide(matrixItem.id)
avatarRenderer.render(matrixItem, matrixToCardAvatar)
viewModel.observeViewEvents {
when (it) {
is MatrixToViewEvents.NavigateToRoom -> {
interactionListener?.navigateToRoom(it.roomId)
dismiss()
}
MatrixToViewEvents.Dismiss -> dismiss()
}
}
}
companion object {
fun create(matrixItem: MatrixItem, listener: InteractionListener?): MatrixToBottomSheet {
return MatrixToBottomSheet(matrixItem).apply {
fun withLink(matrixToLink: String, listener: InteractionListener?): MatrixToBottomSheet {
return MatrixToBottomSheet().apply {
arguments = Bundle().apply {
putParcelable(MvRx.KEY_ARG, MatrixToBottomSheet.MatrixToArgs(
matrixToLink = matrixToLink,
userId = null
))
}
interactionListener = listener
}
}
fun withUserId(userId: String, listener: InteractionListener?): MatrixToBottomSheet {
return MatrixToBottomSheet().apply {
arguments = Bundle().apply {
putParcelable(MvRx.KEY_ARG, MatrixToBottomSheet.MatrixToArgs(
matrixToLink = null,
userId = userId
))
}
interactionListener = listener
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.app.features.matrixto
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import org.matrix.android.sdk.api.util.MatrixItem
data class MatrixToBottomSheetState(
val matrixItem: Async<MatrixItem> = Uninitialized,
val startChattingState: Async<Unit> = Uninitialized
) : MvRxState

View File

@ -0,0 +1,192 @@
/*
* 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.app.features.matrixto
import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.raw.wellknown.getElementWellknown
import im.vector.app.features.raw.wellknown.isE2EByDefault
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.permalinks.PermalinkData
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
import org.matrix.android.sdk.api.session.profile.ProfileService
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.user.model.User
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.internal.util.awaitCallback
class MatrixToBottomSheetViewModel @AssistedInject constructor(
@Assisted initialState: MatrixToBottomSheetState,
@Assisted val args: MatrixToBottomSheet.MatrixToArgs,
private val session: Session,
private val stringProvider: StringProvider,
private val rawService: RawService) : VectorViewModel<MatrixToBottomSheetState, MatrixToAction, MatrixToViewEvents>(initialState) {
@AssistedInject.Factory
interface Factory {
fun create(initialState: MatrixToBottomSheetState,
args: MatrixToBottomSheet.MatrixToArgs): MatrixToBottomSheetViewModel
}
init {
setState {
copy(matrixItem = Loading())
}
viewModelScope.launch(Dispatchers.IO) {
resolveLink()
}
}
private suspend fun resolveLink() {
when {
args.matrixToLink != null -> {
val linkedId = PermalinkParser.parse(args.matrixToLink)
if (linkedId is PermalinkData.FallbackLink) {
setState {
copy(
matrixItem = Fail(IllegalArgumentException(stringProvider.getString(R.string.permalink_malformed))),
startChattingState = Uninitialized
)
}
return
}
when (linkedId) {
is PermalinkData.UserLink -> {
val user = resolveUser(linkedId.userId)
setState {
copy(
matrixItem = Success(user.toMatrixItem()),
startChattingState = Success(Unit)
)
}
}
is PermalinkData.RoomLink -> TODO()
is PermalinkData.GroupLink -> {
// not yet supported
}
is PermalinkData.FallbackLink -> {
}
}
}
args.userId != null -> {
val user = resolveUser(args.userId)
setState {
copy(
matrixItem = Success(user.toMatrixItem()),
startChattingState = Success(Unit)
)
}
}
else -> {
setState {
copy(
matrixItem = Fail(IllegalArgumentException(stringProvider.getString(R.string.unexpected_error))),
startChattingState = Uninitialized
)
}
}
}
}
private suspend fun resolveUser(userId: String): User {
return (session.getUser(userId)
?: tryOrNull {
awaitCallback<JsonDict> {
session.getProfile(userId, it)
}
}?.let {
User(userId, it[ProfileService.DISPLAY_NAME_KEY] as? String, it[ProfileService.AVATAR_URL_KEY] as? String)
}
// Create raw Uxid in case the user is not searchable
?: User(userId, null, null))
}
companion object : MvRxViewModelFactory<MatrixToBottomSheetViewModel, MatrixToBottomSheetState> {
override fun create(viewModelContext: ViewModelContext, state: MatrixToBottomSheetState): MatrixToBottomSheetViewModel? {
val fragment: MatrixToBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
val args: MatrixToBottomSheet.MatrixToArgs = viewModelContext.args()
return fragment.matrixToBottomSheetViewModelFactory.create(state, args)
}
}
override fun handle(action: MatrixToAction) {
when (action) {
is MatrixToAction.StartChattingWithUser -> handleStartChatting(action)
}.exhaustive
}
private fun handleStartChatting(action: MatrixToAction.StartChattingWithUser) {
val mxId = action.matrixItem.id
val existing = session.getExistingDirectRoomWithUser(mxId)
if (existing != null) {
// navigate to this room
_viewEvents.post(MatrixToViewEvents.NavigateToRoom(existing))
} else {
setState {
copy(startChattingState = Loading())
}
// we should create the room then navigate
viewModelScope.launch(Dispatchers.IO) {
val adminE2EByDefault = rawService.getElementWellknown(session.myUserId)
?.isE2EByDefault()
?: true
val roomParams = CreateRoomParams()
.apply {
invitedUserIds.add(mxId)
setDirectMessage()
enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault
}
val roomId =
try {
awaitCallback<String> { session.createRoom(roomParams, it) }.also {
setState {
copy(startChattingState = Success(Unit))
}
}
} catch (failure: Throwable) {
setState {
copy(startChattingState = Fail(Exception(stringProvider.getString(R.string.invite_users_to_room_failure))))
}
return@launch
}
_viewEvents.post(MatrixToViewEvents.NavigateToRoom(roomId))
}
}
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.app.features.matrixto
import im.vector.app.core.platform.VectorViewEvents
sealed class MatrixToViewEvents : VectorViewEvents {
data class NavigateToRoom(val roomId: String) : MatrixToViewEvents()
object Dismiss : MatrixToViewEvents()
}

View File

@ -23,11 +23,9 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.toast
import im.vector.app.features.home.HomeActivity
import im.vector.app.features.home.LoadingFragment
import im.vector.app.features.login.LoginActivity
import io.reactivex.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class PermalinkHandlerActivity : VectorBaseActivity() {
@ -45,23 +43,28 @@ class PermalinkHandlerActivity : VectorBaseActivity() {
if (isFirstCreation()) {
replaceFragment(R.id.simpleFragmentContainer, LoadingFragment::class.java)
}
handleIntent()
}
private fun handleIntent() {
// If we are not logged in, open login screen.
// In the future, we might want to relaunch the process after login.
if (!sessionHolder.hasActiveSession()) {
startLoginActivity()
return
}
val uri = intent.dataString
permalinkHandler.launch(this, uri, buildTask = true)
.delay(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { isHandled ->
if (!isHandled) {
toast(R.string.permalink_malformed)
}
finish()
}
.disposeOnDestroy()
// We forward intent to HomeActivity (singleTask) to avoid the dueling app problem
// https://stackoverflow.com/questions/25884954/deep-linking-and-multiple-app-instances
intent.setClass(this, HomeActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
startActivity(intent)
finish()
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
handleIntent()
}
private fun startLoginActivity() {

View File

@ -36,7 +36,6 @@ import im.vector.app.core.utils.onPermissionDeniedSnackbar
import im.vector.app.features.matrixto.MatrixToBottomSheet
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_simple.*
import org.matrix.android.sdk.api.util.MatrixItem
import javax.inject.Inject
import kotlin.reflect.KClass
@ -72,7 +71,7 @@ class UserCodeActivity
UserCodeState.Mode.SCAN -> showFragment(ScanUserCodeFragment::class, Bundle.EMPTY)
is UserCodeState.Mode.RESULT -> {
showFragment(ShowUserCodeFragment::class, Bundle.EMPTY)
MatrixToBottomSheet.create(mode.matrixItem, this).show(supportFragmentManager, "MatrixToBottomSheet")
MatrixToBottomSheet.withUserId(mode.matrixItem.id, this).show(supportFragmentManager, "MatrixToBottomSheet")
}
}
}
@ -104,8 +103,8 @@ class UserCodeActivity
}
}
override fun didTapStartMessage(matrixItem: MatrixItem) {
sharedViewModel.handle(UserCodeActions.StartChattingWithUser(matrixItem))
override fun navigateToRoom(roomId: String) {
navigator.openRoom(this, roomId)
}
override fun onBackPressed() = withState(sharedViewModel) {

View File

@ -30,12 +30,15 @@ import im.vector.app.features.raw.wellknown.getElementWellknown
import im.vector.app.features.raw.wellknown.isE2EByDefault
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.permalinks.PermalinkData
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
import org.matrix.android.sdk.api.session.profile.ProfileService
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.user.model.User
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.internal.util.awaitCallback
@ -72,12 +75,12 @@ class UserCodeSharedViewModel @AssistedInject constructor(
override fun handle(action: UserCodeActions) {
when (action) {
UserCodeActions.DismissAction -> _viewEvents.post(UserCodeShareViewEvents.Dismiss)
is UserCodeActions.SwitchMode -> setState { copy(mode = action.mode) }
is UserCodeActions.DecodedQRCode -> handleQrCodeDecoded(action)
is UserCodeActions.StartChattingWithUser -> handleStartChatting(action)
UserCodeActions.DismissAction -> _viewEvents.post(UserCodeShareViewEvents.Dismiss)
is UserCodeActions.SwitchMode -> setState { copy(mode = action.mode) }
is UserCodeActions.DecodedQRCode -> handleQrCodeDecoded(action)
is UserCodeActions.StartChattingWithUser -> handleStartChatting(action)
UserCodeActions.CameraPermissionNotGranted -> _viewEvents.post(UserCodeShareViewEvents.CameraPermissionNotGranted)
UserCodeActions.ShareByText -> handleShareByText()
UserCodeActions.ShareByText -> handleShareByText()
}
}
@ -139,13 +142,21 @@ class UserCodeSharedViewModel @AssistedInject constructor(
_viewEvents.post(UserCodeShareViewEvents.ShowWaitingScreen)
viewModelScope.launch(Dispatchers.IO) {
when (linkedId) {
is PermalinkData.RoomLink -> TODO()
is PermalinkData.UserLink -> {
val user = session.getUser(linkedId.userId) ?: awaitCallback<List<User>> {
session.searchUsersDirectory(linkedId.userId, 10, emptySet(), it)
}.firstOrNull { it.userId == linkedId.userId }
// Create raw Uxid in case the user is not searchable
?: User(linkedId.userId, null, null)
is PermalinkData.RoomLink -> {
// not yet supported
_viewEvents.post(UserCodeShareViewEvents.ToastMessage(stringProvider.getString(R.string.not_implemented)))
}
is PermalinkData.UserLink -> {
val user = session.getUser(linkedId.userId)
?: tryOrNull {
awaitCallback<JsonDict> {
session.getProfile(linkedId.userId, it)
}
}?.let {
User(linkedId.userId, it[ProfileService.DISPLAY_NAME_KEY] as? String, it[ProfileService.AVATAR_URL_KEY] as? String)
}
// Create raw Uxid in case the user is not searchable
?: User(linkedId.userId, null, null)
setState {
copy(
@ -153,8 +164,14 @@ class UserCodeSharedViewModel @AssistedInject constructor(
)
}
}
is PermalinkData.GroupLink -> TODO()
is PermalinkData.FallbackLink -> TODO()
is PermalinkData.GroupLink -> {
// not yet supported
_viewEvents.post(UserCodeShareViewEvents.ToastMessage(stringProvider.getString(R.string.not_implemented)))
}
is PermalinkData.FallbackLink -> {
// not yet supported
_viewEvents.post(UserCodeShareViewEvents.ToastMessage(stringProvider.getString(R.string.not_implemented)))
}
}
_viewEvents.post(UserCodeShareViewEvents.HideWaitingScreen)
}

View File

@ -3,13 +3,24 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:minHeight="200dp">
<ProgressBar
android:id="@+id/matrixToCardContentLoading"
android:layout_width="40dp"
android:layout_height="40dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<ImageView
android:id="@+id/matrixToCardAvatar"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginTop="@dimen/layout_vertical_margin_big"
android:elevation="4dp"
android:transitionName="profile"
@ -63,4 +74,23 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/matrixToCardUserIdText" />
<ProgressBar
android:id="@+id/matrixToCardButtonLoading"
style="?android:attr/progressBarStyleSmall"
android:layout_width="20dp"
android:layout_height="20dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/matrixToCardSendMessageButton"
app:layout_constraintEnd_toEndOf="@id/matrixToCardSendMessageButton"
app:layout_constraintStart_toStartOf="@id/matrixToCardSendMessageButton"
app:layout_constraintTop_toTopOf="@id/matrixToCardSendMessageButton"
tools:visibility="visible" />
<androidx.constraintlayout.widget.Group
android:id="@+id/matrixToCardUserContentVisibility"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="matrixToCardAvatar,matrixToCardNameText,matrixToCardUserIdText"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>