Merge pull request #2444 from vector-im/feature/bca/deeplink_mxto
Fix issues with matrix.to deep linking
This commit is contained in:
commit
bc889cbcf4
|
@ -35,6 +35,11 @@ interface UserService {
|
|||
*/
|
||||
fun getUser(userId: String): User?
|
||||
|
||||
/**
|
||||
* Try to resolve user from known users, or using profile api
|
||||
*/
|
||||
fun resolveUser(userId: String, callback: MatrixCallback<User>)
|
||||
|
||||
/**
|
||||
* Search list of users on server directory.
|
||||
* @param search the searched term
|
||||
|
|
|
@ -19,10 +19,13 @@ package org.matrix.android.sdk.internal.session.user
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.PagedList
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.session.profile.ProfileService
|
||||
import org.matrix.android.sdk.api.session.user.UserService
|
||||
import org.matrix.android.sdk.api.session.user.model.User
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.internal.session.profile.GetProfileInfoTask
|
||||
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateIgnoredUserIdsTask
|
||||
import org.matrix.android.sdk.internal.session.user.model.SearchUserTask
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
|
@ -32,12 +35,40 @@ import javax.inject.Inject
|
|||
internal class DefaultUserService @Inject constructor(private val userDataSource: UserDataSource,
|
||||
private val searchUserTask: SearchUserTask,
|
||||
private val updateIgnoredUserIdsTask: UpdateIgnoredUserIdsTask,
|
||||
private val getProfileInfoTask: GetProfileInfoTask,
|
||||
private val taskExecutor: TaskExecutor) : UserService {
|
||||
|
||||
override fun getUser(userId: String): User? {
|
||||
return userDataSource.getUser(userId)
|
||||
}
|
||||
|
||||
override fun resolveUser(userId: String, callback: MatrixCallback<User>) {
|
||||
val known = getUser(userId)
|
||||
if (known != null) {
|
||||
callback.onSuccess(known)
|
||||
} else {
|
||||
val params = GetProfileInfoTask.Params(userId)
|
||||
getProfileInfoTask
|
||||
.configureWith(params) {
|
||||
this.callback = object : MatrixCallback<JsonDict> {
|
||||
override fun onSuccess(data: JsonDict) {
|
||||
callback.onSuccess(
|
||||
User(
|
||||
userId,
|
||||
data[ProfileService.DISPLAY_NAME_KEY] as? String,
|
||||
data[ProfileService.AVATAR_URL_KEY] as? String)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback.onFailure(failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getUserLive(userId: String): LiveData<Optional<User>> {
|
||||
return userDataSource.getUserLive(userId)
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.app.features.home
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.MenuItem
|
||||
|
@ -38,8 +39,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 +55,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 +71,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 +90,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) {
|
||||
|
@ -145,6 +154,28 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
|||
|
||||
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) {
|
||||
|
@ -270,6 +301,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
|||
if (intent?.getParcelableExtra<HomeActivityArgs>(MvRx.KEY_ARG)?.clearNotification == true) {
|
||||
notificationDrawerManager.clearAllEvents()
|
||||
}
|
||||
handleIntent(intent)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -313,11 +345,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 +366,18 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
|||
}
|
||||
}
|
||||
|
||||
override fun navToMemberProfile(userId: String, deepLink: Uri): Boolean {
|
||||
val listener = object : MatrixToBottomSheet.InteractionListener {
|
||||
override fun navigateToRoom(roomId: String) {
|
||||
navigator.openRoom(this@HomeActivity, roomId)
|
||||
}
|
||||
}
|
||||
// TODO check if there is already one??
|
||||
MatrixToBottomSheet.withLink(deepLink.toString(), listener)
|
||||
.show(supportFragmentManager, "HA#MatrixToBottomSheet")
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newIntent(context: Context, clearNotification: Boolean = false, accountCreation: Boolean = false): Intent {
|
||||
val args = HomeActivityArgs(
|
||||
|
|
|
@ -1460,7 +1460,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
return false
|
||||
}
|
||||
|
||||
override fun navToMemberProfile(userId: String): Boolean {
|
||||
override fun navToMemberProfile(userId: String, deepLink: Uri): Boolean {
|
||||
openRoomMemberProfile(userId)
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -17,23 +17,37 @@
|
|||
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
|
||||
) : 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 +57,87 @@ 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
|
||||
))
|
||||
}
|
||||
interactionListener = listener
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 deepLink: String,
|
||||
val matrixItem: Async<MatrixItem> = Uninitialized,
|
||||
val startChattingState: Async<Unit> = Uninitialized
|
||||
) : MvRxState {
|
||||
|
||||
constructor(args: MatrixToBottomSheet.MatrixToArgs) : this(
|
||||
deepLink = args.matrixToLink
|
||||
)
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* 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.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.user.model.User
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||
|
||||
class MatrixToBottomSheetViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: MatrixToBottomSheetState,
|
||||
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): MatrixToBottomSheetViewModel
|
||||
}
|
||||
|
||||
init {
|
||||
setState {
|
||||
copy(matrixItem = Loading())
|
||||
}
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
resolveLink(initialState)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun resolveLink(initialState: MatrixToBottomSheetState) {
|
||||
val permalinkData = PermalinkParser.parse(initialState.deepLink)
|
||||
if (permalinkData is PermalinkData.FallbackLink) {
|
||||
setState {
|
||||
copy(
|
||||
matrixItem = Fail(IllegalArgumentException(stringProvider.getString(R.string.permalink_malformed))),
|
||||
startChattingState = Uninitialized
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
when (permalinkData) {
|
||||
is PermalinkData.UserLink -> {
|
||||
val user = resolveUser(permalinkData.userId)
|
||||
setState {
|
||||
copy(
|
||||
matrixItem = Success(user.toMatrixItem()),
|
||||
startChattingState = Success(Unit)
|
||||
)
|
||||
}
|
||||
}
|
||||
is PermalinkData.RoomLink -> {
|
||||
// not yet supported
|
||||
_viewEvents.post(MatrixToViewEvents.Dismiss)
|
||||
}
|
||||
is PermalinkData.GroupLink -> {
|
||||
// not yet supported
|
||||
_viewEvents.post(MatrixToViewEvents.Dismiss)
|
||||
}
|
||||
is PermalinkData.FallbackLink -> {
|
||||
_viewEvents.post(MatrixToViewEvents.Dismiss)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun resolveUser(userId: String): User {
|
||||
return tryOrNull {
|
||||
awaitCallback<User> {
|
||||
session.resolveUser(userId, it)
|
||||
}
|
||||
}
|
||||
// Create raw user 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()
|
||||
|
||||
return fragment.matrixToBottomSheetViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
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) }
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(startChattingState = Fail(Exception(stringProvider.getString(R.string.invite_users_to_room_failure))))
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
setState {
|
||||
// we can hide this button has we will navigate out
|
||||
copy(startChattingState = Uninitialized)
|
||||
}
|
||||
_viewEvents.post(MatrixToViewEvents.NavigateToRoom(roomId))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -63,13 +63,14 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
|
|||
.subscribeOn(Schedulers.computation())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.flatMap { permalinkData ->
|
||||
handlePermalink(permalinkData, context, navigationInterceptor, buildTask)
|
||||
handlePermalink(permalinkData, deepLink, context, navigationInterceptor, buildTask)
|
||||
}
|
||||
.onErrorReturnItem(false)
|
||||
}
|
||||
|
||||
private fun handlePermalink(
|
||||
permalinkData: PermalinkData,
|
||||
rawLink: Uri,
|
||||
context: Context,
|
||||
navigationInterceptor: NavigationInterceptor?,
|
||||
buildTask: Boolean
|
||||
|
@ -96,7 +97,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti
|
|||
Single.just(true)
|
||||
}
|
||||
is PermalinkData.UserLink -> {
|
||||
if (navigationInterceptor?.navToMemberProfile(permalinkData.userId) != true) {
|
||||
if (navigationInterceptor?.navToMemberProfile(permalinkData.userId, rawLink) != true) {
|
||||
navigator.openRoomMemberProfile(userId = permalinkData.userId, roomId = null, context = context, buildTask = buildTask)
|
||||
}
|
||||
Single.just(true)
|
||||
|
@ -175,7 +176,7 @@ interface NavigationInterceptor {
|
|||
/**
|
||||
* Return true if the navigation has been intercepted
|
||||
*/
|
||||
fun navToMemberProfile(userId: String): Boolean {
|
||||
fun navToMemberProfile(userId: String, deepLink: Uri): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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.withLink(mode.rawLink, 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) {
|
||||
|
|
|
@ -30,6 +30,7 @@ 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
|
||||
|
@ -72,12 +73,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,22 +140,33 @@ 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 }
|
||||
is PermalinkData.RoomLink -> {
|
||||
// not yet supported
|
||||
_viewEvents.post(UserCodeShareViewEvents.ToastMessage(stringProvider.getString(R.string.not_implemented)))
|
||||
}
|
||||
is PermalinkData.UserLink -> {
|
||||
val user = tryOrNull {
|
||||
awaitCallback<User> {
|
||||
session.resolveUser(linkedId.userId, it)
|
||||
}
|
||||
}
|
||||
// Create raw Uxid in case the user is not searchable
|
||||
?: User(linkedId.userId, null, null)
|
||||
?: User(linkedId.userId, null, null)
|
||||
|
||||
setState {
|
||||
copy(
|
||||
mode = UserCodeState.Mode.RESULT(user.toMatrixItem())
|
||||
mode = UserCodeState.Mode.RESULT(user.toMatrixItem(), action.code)
|
||||
)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ data class UserCodeState(
|
|||
sealed class Mode {
|
||||
object SHOW : Mode()
|
||||
object SCAN : Mode()
|
||||
data class RESULT(val matrixItem: MatrixItem) : Mode()
|
||||
data class RESULT(val matrixItem: MatrixItem, val rawLink: String) : Mode()
|
||||
}
|
||||
|
||||
constructor(args: UserCodeActivity.Args) : this(
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue