diff --git a/changelog.d/6655.feature b/changelog.d/6655.feature
new file mode 100644
index 0000000000..13a4c6d572
--- /dev/null
+++ b/changelog.d/6655.feature
@@ -0,0 +1 @@
+Adds new app layout toolbar (feature flagged)
diff --git a/library/ui-styles/src/main/res/drawable/ic_home_search.xml b/library/ui-styles/src/main/res/drawable/ic_home_search.xml
new file mode 100644
index 0000000000..5cb88ba1e4
--- /dev/null
+++ b/library/ui-styles/src/main/res/drawable/ic_home_search.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml
index 70d051b457..53f1044a12 100644
--- a/library/ui-styles/src/main/res/values/dimens.xml
+++ b/library/ui-styles/src/main/res/values/dimens.xml
@@ -71,4 +71,7 @@
8dp
12dp
22dp
+
+
+ 112dp
diff --git a/library/ui-styles/src/main/res/values/styles_toolbar.xml b/library/ui-styles/src/main/res/values/styles_toolbar.xml
index 505419c6fe..893de92aae 100644
--- a/library/ui-styles/src/main/res/values/styles_toolbar.xml
+++ b/library/ui-styles/src/main/res/values/styles_toolbar.xml
@@ -39,4 +39,14 @@
- 12sp
-
\ No newline at end of file
+
+
+
+
+
+
+
diff --git a/library/ui-styles/src/main/res/values/text_appearances.xml b/library/ui-styles/src/main/res/values/text_appearances.xml
index 1e60e05acf..570d26fdfd 100644
--- a/library/ui-styles/src/main/res/values/text_appearances.xml
+++ b/library/ui-styles/src/main/res/values/text_appearances.xml
@@ -32,6 +32,15 @@
- ?vctr_content_primary
+
+
-
\ No newline at end of file
+
diff --git a/library/ui-styles/src/main/res/values/theme_dark.xml b/library/ui-styles/src/main/res/values/theme_dark.xml
index f86a05ed66..9f4e5c1e28 100644
--- a/library/ui-styles/src/main/res/values/theme_dark.xml
+++ b/library/ui-styles/src/main/res/values/theme_dark.xml
@@ -149,6 +149,9 @@
- @color/vctr_live_location_dark
+
+
+ - @dimen/collapsing_toolbar_layout_medium_size
diff --git a/library/ui-styles/src/main/res/values/theme_light.xml b/library/ui-styles/src/main/res/values/theme_light.xml
index 173b502dcd..c8182abecc 100644
--- a/library/ui-styles/src/main/res/values/theme_light.xml
+++ b/library/ui-styles/src/main/res/values/theme_light.xml
@@ -150,8 +150,12 @@
- @color/vctr_live_location_light
+
+
+ - @dimen/collapsing_toolbar_layout_medium_size
+
diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt
index 08ba53a024..3cdc8a1afe 100644
--- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt
+++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt
@@ -57,6 +57,7 @@ import im.vector.app.features.discovery.change.SetIdentityServerFragment
import im.vector.app.features.home.HomeDetailFragment
import im.vector.app.features.home.HomeDrawerFragment
import im.vector.app.features.home.LoadingFragment
+import im.vector.app.features.home.NewHomeDetailFragment
import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment
import im.vector.app.features.home.room.detail.TimelineFragment
import im.vector.app.features.home.room.detail.search.SearchFragment
@@ -258,6 +259,11 @@ interface FragmentModule {
@FragmentKey(HomeDetailFragment::class)
fun bindHomeDetailFragment(fragment: HomeDetailFragment): Fragment
+ @Binds
+ @IntoMap
+ @FragmentKey(NewHomeDetailFragment::class)
+ fun bindNewHomeDetailFragment(fragment: NewHomeDetailFragment): Fragment
+
@Binds
@IntoMap
@FragmentKey(EmojiSearchResultFragment::class)
diff --git a/vector/src/main/java/im/vector/app/core/extensions/EditText.kt b/vector/src/main/java/im/vector/app/core/extensions/EditText.kt
index 24c8e6bd36..2e2c8429d0 100644
--- a/vector/src/main/java/im/vector/app/core/extensions/EditText.kt
+++ b/vector/src/main/java/im/vector/app/core/extensions/EditText.kt
@@ -28,7 +28,7 @@ import im.vector.app.R
import im.vector.app.core.platform.SimpleTextWatcher
fun EditText.setupAsSearch(
- @DrawableRes searchIconRes: Int = R.drawable.ic_search,
+ @DrawableRes searchIconRes: Int = R.drawable.ic_home_search,
@DrawableRes clearIconRes: Int = R.drawable.ic_x_gray
) {
addTextChangedListener(object : SimpleTextWatcher() {
diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
index 5ca5b62d1e..e7d130a6d5 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
@@ -50,6 +50,7 @@ import androidx.viewbinding.ViewBinding
import com.airbnb.mvrx.MavericksView
import com.bumptech.glide.util.Util
import com.google.android.material.appbar.MaterialToolbar
+import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.EntryPointAccessors
@@ -72,6 +73,7 @@ import im.vector.app.core.utils.ToolbarConfig
import im.vector.app.core.utils.toast
import im.vector.app.features.MainActivity
import im.vector.app.features.MainActivityArgs
+import im.vector.app.features.VectorFeatures
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.configuration.VectorConfiguration
@@ -161,6 +163,9 @@ abstract class VectorBaseActivity : AppCompatActivity(), Maver
@Inject
lateinit var fontScalePreferences: FontScalePreferences
+ @Inject
+ lateinit var vectorFeatures: VectorFeatures
+
lateinit var navigator: Navigator
private set
private lateinit var fragmentFactory: FragmentFactory
@@ -253,6 +258,14 @@ abstract class VectorBaseActivity : AppCompatActivity(), Maver
initUiAndData()
+ if (vectorFeatures.isNewAppLayoutEnabled()) {
+ tryOrNull { // Add to XML theme when feature flag is removed
+ val toolbarBackground = MaterialColors.getColor(views.root, R.attr.vctr_toolbar_background)
+ window.statusBarColor = toolbarBackground
+ window.navigationBarColor = toolbarBackground
+ }
+ }
+
val titleRes = getTitleRes()
if (titleRes != -1) {
supportActionBar?.let {
diff --git a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
index bb38411980..6cfe8acc16 100644
--- a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
@@ -159,7 +159,7 @@ fun startInstallFromSourceIntent(context: Context, activityResultLauncher: Activ
}
fun startSharePlainTextIntent(
- fragment: Fragment,
+ context: Context,
activityResultLauncher: ActivityResultLauncher?,
chooserTitle: String?,
text: String,
@@ -182,10 +182,10 @@ fun startSharePlainTextIntent(
if (activityResultLauncher != null) {
activityResultLauncher.launch(intent)
} else {
- fragment.startActivity(intent)
+ context.startActivity(intent)
}
} catch (activityNotFoundException: ActivityNotFoundException) {
- fragment.activity?.toast(R.string.error_no_external_application_found)
+ context.toast(R.string.error_no_external_application_found)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt
index 61148f3aab..b8b84ea322 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt
@@ -142,7 +142,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment(R.id.keys_backup_setup_share)?.debouncedClicks {
startSharePlainTextIntent(
- fragment = this,
+ context = requireContext(),
activityResultLauncher = null,
chooserTitle = context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title),
text = recoveryKey,
diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt
index efabae2f3a..db24807c1b 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt
@@ -104,7 +104,7 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
?: return@withState
startSharePlainTextIntent(
- this,
+ requireContext(),
copyStartForActivityResult,
context?.getString(R.string.keys_backup_setup_step3_share_intent_chooser_title),
recoveryKey,
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 389b4b7b27..12cdaecdf9 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -46,6 +46,7 @@ import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.pushers.UnifiedPushHelper
+import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.ActivityHomeBinding
import im.vector.app.features.MainActivity
import im.vector.app.features.MainActivityArgs
@@ -203,11 +204,16 @@ class HomeActivity :
)
}
}
- sharedActionViewModel = viewModelProvider.get(HomeSharedActionViewModel::class.java)
+ sharedActionViewModel = viewModelProvider[HomeSharedActionViewModel::class.java]
views.drawerLayout.addDrawerListener(drawerListener)
if (isFirstCreation()) {
- replaceFragment(views.homeDetailFragmentContainer, HomeDetailFragment::class.java)
- replaceFragment(views.homeDrawerFragmentContainer, HomeDrawerFragment::class.java)
+ if (vectorFeatures.isNewAppLayoutEnabled()) {
+ views.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
+ replaceFragment(views.homeDetailFragmentContainer, NewHomeDetailFragment::class.java)
+ } else {
+ replaceFragment(views.homeDetailFragmentContainer, HomeDetailFragment::class.java)
+ replaceFragment(views.homeDrawerFragmentContainer, HomeDrawerFragment::class.java)
+ }
}
sharedActionViewModel
@@ -552,7 +558,7 @@ class HomeActivity :
nightlyProxy.onHomeResumed()
}
- override fun getMenuRes() = R.menu.home
+ override fun getMenuRes() = if (vectorFeatures.isNewAppLayoutEnabled()) R.menu.menu_new_home else R.menu.menu_home
override fun handlePrepareMenu(menu: Menu) {
menu.findItem(R.id.menu_home_init_sync_legacy).isVisible = vectorPreferences.developerMode()
@@ -591,10 +597,29 @@ class HomeActivity :
navigator.openSettings(this)
true
}
+ R.id.menu_home_invite_friends -> {
+ launchInviteFriends()
+ true
+ }
else -> false
}
}
+ private fun launchInviteFriends() {
+ activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(sharedActionViewModel.session.myUserId)?.let { permalink ->
+ analyticsTracker.screen(MobileScreen(screenName = MobileScreen.ScreenName.InviteFriends))
+ val text = getString(R.string.invite_friends_text, permalink)
+
+ startSharePlainTextIntent(
+ context = this,
+ activityResultLauncher = null,
+ chooserTitle = getString(R.string.invite_friends),
+ text = text,
+ extraTitle = getString(R.string.invite_friends_rich_title)
+ )
+ }
+ }
+
override fun onBackPressed() {
if (views.drawerLayout.isDrawerOpen(GravityCompat.START)) {
views.drawerLayout.closeDrawer(GravityCompat.START)
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt
index 9ce950ba31..e79387c66d 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt
@@ -102,7 +102,7 @@ class HomeDrawerFragment @Inject constructor(
val text = getString(R.string.invite_friends_text, permalink)
startSharePlainTextIntent(
- fragment = this,
+ context = requireContext(),
activityResultLauncher = null,
chooserTitle = getString(R.string.invite_friends),
text = text,
diff --git a/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt
new file mode 100644
index 0000000000..36372fc73a
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt
@@ -0,0 +1,462 @@
+/*
+ * Copyright 2019 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.home
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import com.airbnb.mvrx.activityViewModel
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
+import com.google.android.material.badge.BadgeDrawable
+import im.vector.app.AppStateHandler
+import im.vector.app.R
+import im.vector.app.core.extensions.commitTransaction
+import im.vector.app.core.extensions.toMvRxBundle
+import im.vector.app.core.platform.OnBackPressed
+import im.vector.app.core.platform.VectorBaseActivity
+import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.core.platform.VectorMenuProvider
+import im.vector.app.core.resources.ColorProvider
+import im.vector.app.core.ui.views.CurrentCallsView
+import im.vector.app.core.ui.views.CurrentCallsViewPresenter
+import im.vector.app.core.ui.views.KeysBackupBanner
+import im.vector.app.databinding.FragmentNewHomeDetailBinding
+import im.vector.app.features.call.SharedKnownCallsViewModel
+import im.vector.app.features.call.VectorCallActivity
+import im.vector.app.features.call.dialpad.DialPadFragment
+import im.vector.app.features.call.webrtc.WebRtcCallManager
+import im.vector.app.features.home.room.list.RoomListFragment
+import im.vector.app.features.home.room.list.RoomListParams
+import im.vector.app.features.popup.PopupAlertManager
+import im.vector.app.features.popup.VerificationVectorAlert
+import im.vector.app.features.settings.VectorLocale
+import im.vector.app.features.settings.VectorPreferences
+import im.vector.app.features.settings.VectorSettingsActivity.Companion.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS
+import im.vector.app.features.themes.ThemeUtils
+import im.vector.app.features.workers.signout.BannerState
+import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import org.matrix.android.sdk.api.util.toMatrixItem
+import javax.inject.Inject
+
+class NewHomeDetailFragment @Inject constructor(
+ private val avatarRenderer: AvatarRenderer,
+ private val colorProvider: ColorProvider,
+ private val alertManager: PopupAlertManager,
+ private val callManager: WebRtcCallManager,
+ private val vectorPreferences: VectorPreferences,
+ private val appStateHandler: AppStateHandler,
+ private val session: Session,
+) : VectorBaseFragment(),
+ KeysBackupBanner.Delegate,
+ CurrentCallsView.Callback,
+ OnBackPressed,
+ VectorMenuProvider {
+
+ private val viewModel: HomeDetailViewModel by fragmentViewModel()
+ private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
+ private val unreadMessagesSharedViewModel: UnreadMessagesSharedViewModel by activityViewModel()
+ private val serverBackupStatusViewModel: ServerBackupStatusViewModel by activityViewModel()
+
+ private lateinit var sharedActionViewModel: HomeSharedActionViewModel
+ private lateinit var sharedCallActionViewModel: SharedKnownCallsViewModel
+
+ private var hasUnreadRooms = false
+ set(value) {
+ if (value != field) {
+ field = value
+ invalidateOptionsMenu()
+ }
+ }
+
+ override fun getMenuRes() = R.menu.room_list
+
+ override fun handleMenuItemSelected(item: MenuItem): Boolean {
+ return when (item.itemId) {
+ R.id.menu_home_mark_all_as_read -> {
+ viewModel.handle(HomeDetailAction.MarkAllRoomsRead)
+ true
+ }
+ else -> false
+ }
+ }
+
+ override fun handlePrepareMenu(menu: Menu) {
+ withState(viewModel) { state ->
+ val isRoomList = state.currentTab is HomeTab.RoomList
+ menu.findItem(R.id.menu_home_mark_all_as_read).isVisible = isRoomList && hasUnreadRooms
+ }
+ }
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentNewHomeDetailBinding {
+ return FragmentNewHomeDetailBinding.inflate(inflater, container, false)
+ }
+
+ private val currentCallsViewPresenter = CurrentCallsViewPresenter()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java)
+ sharedCallActionViewModel = activityViewModelProvider.get(SharedKnownCallsViewModel::class.java)
+ setupBottomNavigationView()
+ setupToolbar()
+ setupKeysBackupBanner()
+ setupActiveCallView()
+
+ withState(viewModel) {
+ // Update the navigation view if needed (for when we restore the tabs)
+ views.bottomNavigationView.selectedItemId = it.currentTab.toMenuId()
+ }
+
+ viewModel.onEach(HomeDetailViewState::selectedSpace) { selectedSpace ->
+ onSpaceChange(selectedSpace)
+ }
+
+ viewModel.onEach(HomeDetailViewState::currentTab) { currentTab ->
+ updateUIForTab(currentTab)
+ }
+
+ viewModel.onEach(HomeDetailViewState::showDialPadTab) { showDialPadTab ->
+ updateTabVisibilitySafely(R.id.bottom_action_dial_pad, showDialPadTab)
+ }
+
+ viewModel.observeViewEvents { viewEvent ->
+ when (viewEvent) {
+ HomeDetailViewEvents.CallStarted -> handleCallStarted()
+ is HomeDetailViewEvents.FailToCall -> showFailure(viewEvent.failure)
+ HomeDetailViewEvents.Loading -> showLoadingDialog()
+ }
+ }
+
+ unknownDeviceDetectorSharedViewModel.onEach { state ->
+ state.unknownSessions.invoke()?.let { unknownDevices ->
+ if (unknownDevices.firstOrNull()?.currentSessionTrust == true) {
+ val uid = "review_login"
+ alertManager.cancelAlert(uid)
+ val olderUnverified = unknownDevices.filter { !it.isNew }
+ val newest = unknownDevices.firstOrNull { it.isNew }?.deviceInfo
+ if (newest != null) {
+ promptForNewUnknownDevices(uid, state, newest)
+ } else if (olderUnverified.isNotEmpty()) {
+ // In this case we prompt to go to settings to review logins
+ promptToReviewChanges(uid, state, olderUnverified.map { it.deviceInfo })
+ }
+ }
+ }
+ }
+
+ sharedCallActionViewModel
+ .liveKnownCalls
+ .observe(viewLifecycleOwner) {
+ currentCallsViewPresenter.updateCall(callManager.getCurrentCall(), callManager.getCalls())
+ invalidateOptionsMenu()
+ }
+ }
+
+ private fun navigateBack() {
+ val previousSpaceId = appStateHandler.getSpaceBackstack().removeLastOrNull()
+ val parentSpaceId = appStateHandler.getCurrentSpace()?.flattenParentIds?.lastOrNull()
+ setCurrentSpace(previousSpaceId ?: parentSpaceId)
+ }
+
+ private fun setCurrentSpace(spaceId: String?) {
+ appStateHandler.setCurrentSpace(spaceId, isForwardNavigation = false)
+ sharedActionViewModel.post(HomeActivitySharedAction.OnCloseSpace)
+ }
+
+ private fun handleCallStarted() {
+ dismissLoadingDialog()
+ val fragmentTag = HomeTab.DialPad.toFragmentTag()
+ (childFragmentManager.findFragmentByTag(fragmentTag) as? DialPadFragment)?.clear()
+ }
+
+ override fun onDestroyView() {
+ currentCallsViewPresenter.unBind()
+ super.onDestroyView()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ updateTabVisibilitySafely(R.id.bottom_action_notification, vectorPreferences.labAddNotificationTab())
+ callManager.checkForProtocolsSupportIfNeeded()
+ refreshSpaceState()
+ }
+
+ private fun refreshSpaceState() {
+ appStateHandler.getCurrentSpace()?.let {
+ onSpaceChange(it)
+ }
+ }
+
+ private fun promptForNewUnknownDevices(uid: String, state: UnknownDevicesState, newest: DeviceInfo) {
+ val user = state.myMatrixItem
+ alertManager.postVectorAlert(
+ VerificationVectorAlert(
+ uid = uid,
+ title = getString(R.string.new_session),
+ description = getString(R.string.verify_this_session, newest.displayName ?: newest.deviceId ?: ""),
+ iconId = R.drawable.ic_shield_warning
+ ).apply {
+ viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer)
+ colorInt = colorProvider.getColorFromAttribute(R.attr.colorPrimary)
+ contentAction = Runnable {
+ (weakCurrentActivity?.get() as? VectorBaseActivity<*>)
+ ?.navigator
+ ?.requestSessionVerification(requireContext(), newest.deviceId ?: "")
+ unknownDeviceDetectorSharedViewModel.handle(
+ UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(newest.deviceId?.let { listOf(it) }.orEmpty())
+ )
+ }
+ dismissedAction = Runnable {
+ unknownDeviceDetectorSharedViewModel.handle(
+ UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(newest.deviceId?.let { listOf(it) }.orEmpty())
+ )
+ }
+ }
+ )
+ }
+
+ private fun promptToReviewChanges(uid: String, state: UnknownDevicesState, oldUnverified: List) {
+ val user = state.myMatrixItem
+ alertManager.postVectorAlert(
+ VerificationVectorAlert(
+ uid = uid,
+ title = getString(R.string.review_logins),
+ description = getString(R.string.verify_other_sessions),
+ iconId = R.drawable.ic_shield_warning
+ ).apply {
+ viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer)
+ colorInt = colorProvider.getColorFromAttribute(R.attr.colorPrimary)
+ contentAction = Runnable {
+ (weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let { activity ->
+ // mark as ignored to avoid showing it again
+ unknownDeviceDetectorSharedViewModel.handle(
+ UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(oldUnverified.mapNotNull { it.deviceId })
+ )
+ activity.navigator.openSettings(activity, EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS)
+ }
+ }
+ dismissedAction = Runnable {
+ unknownDeviceDetectorSharedViewModel.handle(
+ UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(oldUnverified.mapNotNull { it.deviceId })
+ )
+ }
+ }
+ )
+ }
+
+ private fun onSpaceChange(spaceSummary: RoomSummary?) {
+ // Reimplement in next PR
+ println(spaceSummary)
+ }
+
+ private fun setupKeysBackupBanner() {
+ serverBackupStatusViewModel
+ .onEach {
+ when (val banState = it.bannerState.invoke()) {
+ is BannerState.Setup -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Setup(banState.numberOfKeys), false)
+ BannerState.BackingUp -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.BackingUp, false)
+ null,
+ BannerState.Hidden -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Hidden, false)
+ }
+ }
+ views.homeKeysBackupBanner.delegate = this
+ }
+
+ private fun setupActiveCallView() {
+ currentCallsViewPresenter.bind(views.currentCallsView, this)
+ }
+
+ private fun setupToolbar() {
+ setupToolbar(views.toolbar)
+
+ lifecycleScope.launch(Dispatchers.IO) {
+ session.userService().getUser(session.myUserId)?.let { user ->
+ avatarRenderer.render(user.toMatrixItem(), views.avatar)
+ }
+ }
+
+ views.avatar.debouncedClicks {
+ navigator.openSettings(requireContext())
+ }
+ }
+
+ private fun setupBottomNavigationView() {
+ views.bottomNavigationView.menu.findItem(R.id.bottom_action_notification).isVisible = vectorPreferences.labAddNotificationTab()
+ views.bottomNavigationView.setOnItemSelectedListener {
+ val tab = when (it.itemId) {
+ R.id.bottom_action_people -> HomeTab.RoomList(RoomListDisplayMode.PEOPLE)
+ R.id.bottom_action_rooms -> HomeTab.RoomList(RoomListDisplayMode.ROOMS)
+ R.id.bottom_action_notification -> HomeTab.RoomList(RoomListDisplayMode.NOTIFICATIONS)
+ else -> HomeTab.DialPad
+ }
+ viewModel.handle(HomeDetailAction.SwitchTab(tab))
+ true
+ }
+ }
+
+ private fun updateUIForTab(tab: HomeTab) {
+ views.bottomNavigationView.menu.findItem(tab.toMenuId()).isChecked = true
+ updateSelectedFragment(tab)
+ invalidateOptionsMenu()
+ }
+
+ private fun HomeTab.toFragmentTag() = "FRAGMENT_TAG_$this"
+
+ private fun updateSelectedFragment(tab: HomeTab) {
+ val fragmentTag = tab.toFragmentTag()
+ val fragmentToShow = childFragmentManager.findFragmentByTag(fragmentTag)
+ childFragmentManager.commitTransaction {
+ childFragmentManager.fragments
+ .filter { it != fragmentToShow }
+ .forEach {
+ detach(it)
+ }
+ if (fragmentToShow == null) {
+ when (tab) {
+ is HomeTab.RoomList -> {
+ val params = RoomListParams(tab.displayMode)
+ add(R.id.roomListContainer, RoomListFragment::class.java, params.toMvRxBundle(), fragmentTag)
+ }
+ is HomeTab.DialPad -> {
+ add(R.id.roomListContainer, createDialPadFragment(), fragmentTag)
+ }
+ }
+ } else {
+ if (tab is HomeTab.DialPad) {
+ (fragmentToShow as? DialPadFragment)?.applyCallback()
+ }
+ attach(fragmentToShow)
+ }
+ }
+ }
+
+ private fun createDialPadFragment(): Fragment {
+ val fragment = childFragmentManager.fragmentFactory.instantiate(vectorBaseActivity.classLoader, DialPadFragment::class.java.name)
+ return (fragment as DialPadFragment).apply {
+ arguments = Bundle().apply {
+ putBoolean(DialPadFragment.EXTRA_ENABLE_DELETE, true)
+ putBoolean(DialPadFragment.EXTRA_ENABLE_OK, true)
+ putString(DialPadFragment.EXTRA_REGION_CODE, VectorLocale.applicationLocale.country)
+ }
+ applyCallback()
+ }
+ }
+
+ private fun updateTabVisibilitySafely(tabId: Int, isVisible: Boolean) {
+ val wasVisible = views.bottomNavigationView.menu.findItem(tabId).isVisible
+ views.bottomNavigationView.menu.findItem(tabId).isVisible = isVisible
+ if (wasVisible && !isVisible) {
+ // As we hide it check if it's not the current item!
+ withState(viewModel) {
+ if (it.currentTab.toMenuId() == tabId) {
+ viewModel.handle(HomeDetailAction.SwitchTab(HomeTab.RoomList(RoomListDisplayMode.PEOPLE)))
+ }
+ }
+ }
+ }
+
+ /* ==========================================================================================
+ * KeysBackupBanner Listener
+ * ========================================================================================== */
+
+ override fun setupKeysBackup() {
+ navigator.openKeysBackupSetup(requireActivity(), false)
+ }
+
+ override fun recoverKeysBackup() {
+ navigator.openKeysBackupManager(requireActivity())
+ }
+
+ override fun invalidate() = withState(viewModel) {
+ views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_people).render(it.notificationCountPeople, it.notificationHighlightPeople)
+ views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_rooms).render(it.notificationCountRooms, it.notificationHighlightRooms)
+ views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_notification).render(it.notificationCountCatchup, it.notificationHighlightCatchup)
+ views.syncStateView.render(
+ it.syncState,
+ it.incrementalSyncRequestState,
+ it.pushCounter,
+ vectorPreferences.developerShowDebugInfo()
+ )
+
+ hasUnreadRooms = it.hasUnreadMessages
+ }
+
+ private fun BadgeDrawable.render(count: Int, highlight: Boolean) {
+ isVisible = count > 0
+ number = count
+ maxCharacterCount = 3
+ badgeTextColor = ThemeUtils.getColor(requireContext(), R.attr.colorOnPrimary)
+ backgroundColor = if (highlight) {
+ ThemeUtils.getColor(requireContext(), R.attr.colorError)
+ } else {
+ ThemeUtils.getColor(requireContext(), R.attr.vctr_unread_background)
+ }
+ }
+
+ private fun HomeTab.toMenuId() = when (this) {
+ is HomeTab.DialPad -> R.id.bottom_action_dial_pad
+ is HomeTab.RoomList -> when (displayMode) {
+ RoomListDisplayMode.PEOPLE -> R.id.bottom_action_people
+ RoomListDisplayMode.ROOMS -> R.id.bottom_action_rooms
+ else -> R.id.bottom_action_notification
+ }
+ }
+
+ override fun onTapToReturnToCall() {
+ callManager.getCurrentCall()?.let { call ->
+ VectorCallActivity.newIntent(
+ context = requireContext(),
+ callId = call.callId,
+ signalingRoomId = call.signalingRoomId,
+ otherUserId = call.mxCall.opponentUserId,
+ isIncomingCall = !call.mxCall.isOutgoing,
+ isVideoCall = call.mxCall.isVideoCall,
+ mode = null
+ ).let {
+ startActivity(it)
+ }
+ }
+ }
+
+ private fun DialPadFragment.applyCallback(): DialPadFragment {
+ callback = object : DialPadFragment.Callback {
+ override fun onOkClicked(formatted: String?, raw: String?) {
+ if (raw.isNullOrEmpty()) return
+ viewModel.handle(HomeDetailAction.StartCallWithPhoneNumber(raw))
+ }
+ }
+ return this
+ }
+
+ override fun onBackPressed(toolbarButton: Boolean) = if (appStateHandler.getCurrentSpace() != null) {
+ navigateBack()
+ true
+ } else {
+ false
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt
index d050b0d561..686c87a18c 100644
--- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt
@@ -334,7 +334,7 @@ class RoomMemberProfileFragment @Inject constructor(
.setNeutralButton(R.string.ok, null)
.setPositiveButton(R.string.share_by_text) { _, _ ->
startSharePlainTextIntent(
- fragment = this,
+ context = requireContext(),
activityResultLauncher = null,
chooserTitle = null,
text = permalink
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
index 2f8128b7af..1830cc04e8 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
@@ -337,7 +337,7 @@ class RoomProfileFragment @Inject constructor(
private fun onShareRoomProfile(permalink: String) {
startSharePlainTextIntent(
- fragment = this,
+ context = requireContext(),
activityResultLauncher = null,
chooserTitle = null,
text = permalink
diff --git a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt
index 72a2b6c156..3b0bc7f0fe 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt
@@ -89,7 +89,7 @@ class ShareSpaceBottomSheet : VectorBaseBottomSheetDialogFragment {
startSharePlainTextIntent(
- fragment = this,
+ context = requireContext(),
activityResultLauncher = null,
chooserTitle = getString(R.string.share_by_text),
text = getString(R.string.share_space_link_message, event.spaceName, event.permalink),
diff --git a/vector/src/main/java/im/vector/app/features/usercode/ShowUserCodeFragment.kt b/vector/src/main/java/im/vector/app/features/usercode/ShowUserCodeFragment.kt
index a31b0d3a25..259a220874 100644
--- a/vector/src/main/java/im/vector/app/features/usercode/ShowUserCodeFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/usercode/ShowUserCodeFragment.kt
@@ -67,7 +67,7 @@ class ShowUserCodeFragment @Inject constructor(
sharedViewModel.observeViewEvents {
if (it is UserCodeShareViewEvents.SharePlainText) {
startSharePlainTextIntent(
- fragment = this,
+ context = requireContext(),
activityResultLauncher = null,
chooserTitle = it.title,
text = it.text,
diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt
index b31833e37c..3fe95cfb7c 100644
--- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt
@@ -96,7 +96,7 @@ class UserListFragment @Inject constructor(
is UserListViewEvents.OpenShareMatrixToLink -> {
val text = getString(R.string.invite_friends_text, it.link)
startSharePlainTextIntent(
- fragment = this,
+ context = requireContext(),
activityResultLauncher = null,
chooserTitle = getString(R.string.invite_friends),
text = text,
diff --git a/vector/src/main/res/drawable/ic_search_no_results.xml b/vector/src/main/res/drawable/ic_search_no_results.xml
index 24c0f00131..b87f25f0eb 100644
--- a/vector/src/main/res/drawable/ic_search_no_results.xml
+++ b/vector/src/main/res/drawable/ic_search_no_results.xml
@@ -17,4 +17,4 @@
android:left="14dp"
android:right="14dp"
android:top="14dp" />
-
\ No newline at end of file
+
diff --git a/vector/src/main/res/layout/fragment_new_home_detail.xml b/vector/src/main/res/layout/fragment_new_home_detail.xml
new file mode 100644
index 0000000000..b0e7bf7634
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_new_home_detail.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/menu/menu_emoji_reaction_picker.xml b/vector/src/main/res/menu/menu_emoji_reaction_picker.xml
index 637d709707..8ccff89c9d 100644
--- a/vector/src/main/res/menu/menu_emoji_reaction_picker.xml
+++ b/vector/src/main/res/menu/menu_emoji_reaction_picker.xml
@@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android">
-
+
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 956956b63a..6b4eec1c9c 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -136,6 +136,7 @@
Matrix error
+ All Chats
@@ -2808,6 +2809,7 @@
Screenshot
Open widgets
+ Open settings
Import key from file
Image
Change avatar