mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-25 02:45:53 +03:00
Fix issues with matrix.to deep linking
This commit is contained in:
parent
bcd86977d2
commit
804afc9a1d
11 changed files with 511 additions and 58 deletions
|
@ -81,7 +81,8 @@
|
||||||
android:resource="@xml/shortcuts" />
|
android:resource="@xml/shortcuts" />
|
||||||
</activity-alias>
|
</activity-alias>
|
||||||
|
|
||||||
<activity android:name=".features.home.HomeActivity" />
|
<activity android:name=".features.home.HomeActivity"
|
||||||
|
android:launchMode="singleTask"/>
|
||||||
<activity
|
<activity
|
||||||
android:name=".features.login.LoginActivity"
|
android:name=".features.login.LoginActivity"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
|
@ -189,10 +190,9 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".features.signout.soft.SoftLogoutActivity"
|
android:name=".features.signout.soft.SoftLogoutActivity"
|
||||||
android:windowSoftInputMode="adjustResize" />
|
android:windowSoftInputMode="adjustResize" />
|
||||||
<activity android:name=".features.permalink.PermalinkHandlerActivity">
|
<activity android:name=".features.permalink.PermalinkHandlerActivity" android:launchMode="singleTask">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,12 @@ import im.vector.app.core.extensions.replaceFragment
|
||||||
import im.vector.app.core.platform.ToolbarConfigurable
|
import im.vector.app.core.platform.ToolbarConfigurable
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
import im.vector.app.core.pushers.PushersManager
|
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.disclaimer.showDisclaimerDialog
|
||||||
|
import im.vector.app.features.matrixto.MatrixToBottomSheet
|
||||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
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.DefaultVectorAlert
|
||||||
import im.vector.app.features.popup.PopupAlertManager
|
import im.vector.app.features.popup.PopupAlertManager
|
||||||
import im.vector.app.features.popup.VerificationVectorAlert
|
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.ServerBackupStatusViewModel
|
||||||
import im.vector.app.features.workers.signout.ServerBackupStatusViewState
|
import im.vector.app.features.workers.signout.ServerBackupStatusViewState
|
||||||
import im.vector.app.push.fcm.FcmHelper
|
import im.vector.app.push.fcm.FcmHelper
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.activity_home.*
|
import kotlinx.android.synthetic.main.activity_home.*
|
||||||
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.InitialSyncProgressService
|
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 org.matrix.android.sdk.api.util.MatrixItem
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -64,7 +70,8 @@ data class HomeActivityArgs(
|
||||||
val accountCreation: Boolean
|
val accountCreation: Boolean
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDetectorSharedViewModel.Factory, ServerBackupStatusViewModel.Factory {
|
class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDetectorSharedViewModel.Factory, ServerBackupStatusViewModel.Factory,
|
||||||
|
NavigationInterceptor {
|
||||||
|
|
||||||
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
|
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
|
||||||
|
|
||||||
|
@ -82,6 +89,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
||||||
@Inject lateinit var popupAlertManager: PopupAlertManager
|
@Inject lateinit var popupAlertManager: PopupAlertManager
|
||||||
@Inject lateinit var shortcutsHandler: ShortcutsHandler
|
@Inject lateinit var shortcutsHandler: ShortcutsHandler
|
||||||
@Inject lateinit var unknownDeviceViewModelFactory: UnknownDeviceDetectorSharedViewModel.Factory
|
@Inject lateinit var unknownDeviceViewModelFactory: UnknownDeviceDetectorSharedViewModel.Factory
|
||||||
|
@Inject lateinit var permalinkHandler: PermalinkHandler
|
||||||
|
|
||||||
private val drawerListener = object : DrawerLayout.SimpleDrawerListener() {
|
private val drawerListener = object : DrawerLayout.SimpleDrawerListener() {
|
||||||
override fun onDrawerStateChanged(newState: Int) {
|
override fun onDrawerStateChanged(newState: Int) {
|
||||||
|
@ -117,9 +125,9 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
||||||
.observe()
|
.observe()
|
||||||
.subscribe { sharedAction ->
|
.subscribe { sharedAction ->
|
||||||
when (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.CloseDrawer -> drawerLayout.closeDrawer(GravityCompat.START)
|
||||||
is HomeActivitySharedAction.OpenGroup -> {
|
is HomeActivitySharedAction.OpenGroup -> {
|
||||||
drawerLayout.closeDrawer(GravityCompat.START)
|
drawerLayout.closeDrawer(GravityCompat.START)
|
||||||
replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true)
|
replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true)
|
||||||
}
|
}
|
||||||
|
@ -136,20 +144,42 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
||||||
homeActivityViewModel.observeViewEvents {
|
homeActivityViewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it)
|
is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it)
|
||||||
is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
|
is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
|
||||||
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
|
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
|
||||||
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
|
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
homeActivityViewModel.subscribe(this) { renderState(it) }
|
homeActivityViewModel.subscribe(this) { renderState(it) }
|
||||||
|
|
||||||
shortcutsHandler.observeRoomsAndBuildShortcuts()
|
shortcutsHandler.observeRoomsAndBuildShortcuts()
|
||||||
.disposeOnDestroy()
|
.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) {
|
private fun renderState(state: HomeActivityViewState) {
|
||||||
when (val status = state.initialSyncProgressServiceStatus) {
|
when (val status = state.initialSyncProgressServiceStatus) {
|
||||||
is InitialSyncProgressService.Status.Idle -> {
|
is InitialSyncProgressService.Status.Idle -> {
|
||||||
waiting_view.isVisible = false
|
waiting_view.isVisible = false
|
||||||
}
|
}
|
||||||
is InitialSyncProgressService.Status.Progressing -> {
|
is InitialSyncProgressService.Status.Progressing -> {
|
||||||
|
@ -270,6 +300,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
||||||
if (intent?.getParcelableExtra<HomeActivityArgs>(MvRx.KEY_ARG)?.clearNotification == true) {
|
if (intent?.getParcelableExtra<HomeActivityArgs>(MvRx.KEY_ARG)?.clearNotification == true) {
|
||||||
notificationDrawerManager.clearAllEvents()
|
notificationDrawerManager.clearAllEvents()
|
||||||
}
|
}
|
||||||
|
handleIntent(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
@ -313,11 +344,11 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
|
||||||
bugReporter.openBugReportScreen(this, false)
|
bugReporter.openBugReportScreen(this, false)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.menu_home_filter -> {
|
R.id.menu_home_filter -> {
|
||||||
navigator.openRoomsFiltering(this)
|
navigator.openRoomsFiltering(this)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.menu_home_setting -> {
|
R.id.menu_home_setting -> {
|
||||||
navigator.openSettings(this)
|
navigator.openSettings(this)
|
||||||
return true
|
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 {
|
companion object {
|
||||||
fun newIntent(context: Context, clearNotification: Boolean = false, accountCreation: Boolean = false): Intent {
|
fun newIntent(context: Context, clearNotification: Boolean = false, accountCreation: Boolean = false): Intent {
|
||||||
val args = HomeActivityArgs(
|
val args = HomeActivityArgs(
|
||||||
|
|
|
@ -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,38 @@
|
||||||
package im.vector.app.features.matrixto
|
package im.vector.app.features.matrixto
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Parcelable
|
||||||
import android.view.View
|
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.R
|
||||||
import im.vector.app.core.di.ScreenComponent
|
import im.vector.app.core.di.ScreenComponent
|
||||||
import im.vector.app.core.extensions.setTextOrHide
|
import im.vector.app.core.extensions.setTextOrHide
|
||||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.bottom_sheet_matrix_to_card.*
|
import kotlinx.android.synthetic.main.bottom_sheet_matrix_to_card.*
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
|
||||||
import javax.inject.Inject
|
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
|
@Inject lateinit var avatarRenderer: AvatarRenderer
|
||||||
|
|
||||||
interface InteractionListener {
|
@Inject
|
||||||
fun didTapStartMessage(matrixItem: MatrixItem)
|
lateinit var matrixToBottomSheetViewModelFactory: MatrixToBottomSheetViewModel.Factory
|
||||||
}
|
|
||||||
|
|
||||||
override fun injectWith(injector: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
|
@ -43,21 +58,100 @@ class MatrixToBottomSheet(private val matrixItem: MatrixItem) : VectorBaseBottom
|
||||||
|
|
||||||
override fun getLayoutResId() = R.layout.bottom_sheet_matrix_to_card
|
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?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
matrixToCardSendMessageButton.debouncedClicks {
|
matrixToCardSendMessageButton.debouncedClicks {
|
||||||
interactionListener?.didTapStartMessage(matrixItem)
|
withState(viewModel) {
|
||||||
dismiss()
|
it.matrixItem.invoke()?.let { item ->
|
||||||
|
viewModel.handle(MatrixToAction.StartChattingWithUser(item))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
matrixToCardNameText.setTextOrHide(matrixItem.displayName)
|
viewModel.observeViewEvents {
|
||||||
matrixToCardUserIdText.setTextOrHide(matrixItem.id)
|
when (it) {
|
||||||
avatarRenderer.render(matrixItem, matrixToCardAvatar)
|
is MatrixToViewEvents.NavigateToRoom -> {
|
||||||
|
interactionListener?.navigateToRoom(it.roomId)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
MatrixToViewEvents.Dismiss -> dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun create(matrixItem: MatrixItem, listener: InteractionListener?): MatrixToBottomSheet {
|
fun withLink(matrixToLink: String, listener: InteractionListener?): MatrixToBottomSheet {
|
||||||
return MatrixToBottomSheet(matrixItem).apply {
|
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
|
interactionListener = listener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
|
@ -23,11 +23,9 @@ 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.replaceFragment
|
import im.vector.app.core.extensions.replaceFragment
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
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.home.LoadingFragment
|
||||||
import im.vector.app.features.login.LoginActivity
|
import im.vector.app.features.login.LoginActivity
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class PermalinkHandlerActivity : VectorBaseActivity() {
|
class PermalinkHandlerActivity : VectorBaseActivity() {
|
||||||
|
@ -45,23 +43,28 @@ class PermalinkHandlerActivity : VectorBaseActivity() {
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
replaceFragment(R.id.simpleFragmentContainer, LoadingFragment::class.java)
|
replaceFragment(R.id.simpleFragmentContainer, LoadingFragment::class.java)
|
||||||
}
|
}
|
||||||
|
handleIntent()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleIntent() {
|
||||||
// If we are not logged in, open login screen.
|
// If we are not logged in, open login screen.
|
||||||
// In the future, we might want to relaunch the process after login.
|
// In the future, we might want to relaunch the process after login.
|
||||||
if (!sessionHolder.hasActiveSession()) {
|
if (!sessionHolder.hasActiveSession()) {
|
||||||
startLoginActivity()
|
startLoginActivity()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val uri = intent.dataString
|
// We forward intent to HomeActivity (singleTask) to avoid the dueling app problem
|
||||||
permalinkHandler.launch(this, uri, buildTask = true)
|
// https://stackoverflow.com/questions/25884954/deep-linking-and-multiple-app-instances
|
||||||
.delay(500, TimeUnit.MILLISECONDS)
|
intent.setClass(this, HomeActivity::class.java)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||||
.subscribe { isHandled ->
|
startActivity(intent)
|
||||||
if (!isHandled) {
|
|
||||||
toast(R.string.permalink_malformed)
|
finish()
|
||||||
}
|
}
|
||||||
finish()
|
|
||||||
}
|
override fun onNewIntent(intent: Intent?) {
|
||||||
.disposeOnDestroy()
|
super.onNewIntent(intent)
|
||||||
|
handleIntent()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startLoginActivity() {
|
private fun startLoginActivity() {
|
||||||
|
|
|
@ -36,7 +36,6 @@ import im.vector.app.core.utils.onPermissionDeniedSnackbar
|
||||||
import im.vector.app.features.matrixto.MatrixToBottomSheet
|
import im.vector.app.features.matrixto.MatrixToBottomSheet
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.activity_simple.*
|
import kotlinx.android.synthetic.main.activity_simple.*
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
@ -72,7 +71,7 @@ class UserCodeActivity
|
||||||
UserCodeState.Mode.SCAN -> showFragment(ScanUserCodeFragment::class, Bundle.EMPTY)
|
UserCodeState.Mode.SCAN -> showFragment(ScanUserCodeFragment::class, Bundle.EMPTY)
|
||||||
is UserCodeState.Mode.RESULT -> {
|
is UserCodeState.Mode.RESULT -> {
|
||||||
showFragment(ShowUserCodeFragment::class, Bundle.EMPTY)
|
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) {
|
override fun navigateToRoom(roomId: String) {
|
||||||
sharedViewModel.handle(UserCodeActions.StartChattingWithUser(matrixItem))
|
navigator.openRoom(this, roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() = withState(sharedViewModel) {
|
override fun onBackPressed() = withState(sharedViewModel) {
|
||||||
|
|
|
@ -30,12 +30,15 @@ import im.vector.app.features.raw.wellknown.getElementWellknown
|
||||||
import im.vector.app.features.raw.wellknown.isE2EByDefault
|
import im.vector.app.features.raw.wellknown.isE2EByDefault
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
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.raw.RawService
|
||||||
import org.matrix.android.sdk.api.session.Session
|
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.PermalinkData
|
||||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
|
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.room.model.create.CreateRoomParams
|
||||||
import org.matrix.android.sdk.api.session.user.model.User
|
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.api.util.toMatrixItem
|
||||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||||
|
|
||||||
|
@ -72,12 +75,12 @@ class UserCodeSharedViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
override fun handle(action: UserCodeActions) {
|
override fun handle(action: UserCodeActions) {
|
||||||
when (action) {
|
when (action) {
|
||||||
UserCodeActions.DismissAction -> _viewEvents.post(UserCodeShareViewEvents.Dismiss)
|
UserCodeActions.DismissAction -> _viewEvents.post(UserCodeShareViewEvents.Dismiss)
|
||||||
is UserCodeActions.SwitchMode -> setState { copy(mode = action.mode) }
|
is UserCodeActions.SwitchMode -> setState { copy(mode = action.mode) }
|
||||||
is UserCodeActions.DecodedQRCode -> handleQrCodeDecoded(action)
|
is UserCodeActions.DecodedQRCode -> handleQrCodeDecoded(action)
|
||||||
is UserCodeActions.StartChattingWithUser -> handleStartChatting(action)
|
is UserCodeActions.StartChattingWithUser -> handleStartChatting(action)
|
||||||
UserCodeActions.CameraPermissionNotGranted -> _viewEvents.post(UserCodeShareViewEvents.CameraPermissionNotGranted)
|
UserCodeActions.CameraPermissionNotGranted -> _viewEvents.post(UserCodeShareViewEvents.CameraPermissionNotGranted)
|
||||||
UserCodeActions.ShareByText -> handleShareByText()
|
UserCodeActions.ShareByText -> handleShareByText()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,13 +142,21 @@ class UserCodeSharedViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(UserCodeShareViewEvents.ShowWaitingScreen)
|
_viewEvents.post(UserCodeShareViewEvents.ShowWaitingScreen)
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
when (linkedId) {
|
when (linkedId) {
|
||||||
is PermalinkData.RoomLink -> TODO()
|
is PermalinkData.RoomLink -> {
|
||||||
is PermalinkData.UserLink -> {
|
// not yet supported
|
||||||
val user = session.getUser(linkedId.userId) ?: awaitCallback<List<User>> {
|
_viewEvents.post(UserCodeShareViewEvents.ToastMessage(stringProvider.getString(R.string.not_implemented)))
|
||||||
session.searchUsersDirectory(linkedId.userId, 10, emptySet(), it)
|
}
|
||||||
}.firstOrNull { it.userId == linkedId.userId }
|
is PermalinkData.UserLink -> {
|
||||||
// Create raw Uxid in case the user is not searchable
|
val user = session.getUser(linkedId.userId)
|
||||||
?: User(linkedId.userId, null, null)
|
?: 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 {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
|
@ -153,8 +164,14 @@ class UserCodeSharedViewModel @AssistedInject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PermalinkData.GroupLink -> TODO()
|
is PermalinkData.GroupLink -> {
|
||||||
is PermalinkData.FallbackLink -> TODO()
|
// 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)
|
_viewEvents.post(UserCodeShareViewEvents.HideWaitingScreen)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,24 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
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
|
<ImageView
|
||||||
android:id="@+id/matrixToCardAvatar"
|
android:id="@+id/matrixToCardAvatar"
|
||||||
android:layout_width="60dp"
|
android:layout_width="60dp"
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
|
||||||
android:layout_marginTop="@dimen/layout_vertical_margin_big"
|
android:layout_marginTop="@dimen/layout_vertical_margin_big"
|
||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
android:transitionName="profile"
|
android:transitionName="profile"
|
||||||
|
@ -63,4 +74,23 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/matrixToCardUserIdText" />
|
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>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
Loading…
Reference in a new issue