Merge pull request #6436 from vector-im/feature/bma/activity_fragment_1_5_0

Upgrade androidx activity and fragment to 1.5.0
This commit is contained in:
Benoit Marty 2022-07-06 11:44:22 +02:00 committed by GitHub
commit 7616912411
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 459 additions and 342 deletions

1
changelog.d/6436.misc Normal file
View file

@ -0,0 +1 @@
Let your Activity or Fragment implement `VectorMenuProvider` if they provide a menu.

View file

@ -29,7 +29,7 @@ def bigImageViewer = "1.8.1"
def jjwt = "0.11.5"
def vanniktechEmoji = "0.15.0"
def fragment = "1.4.1"
def fragment = "1.5.0"
// Testing
def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819
@ -50,7 +50,7 @@ ext.libs = [
],
androidx : [
'annotation' : "androidx.annotation:annotation:1.4.0",
'activity' : "androidx.activity:activity:1.4.0",
'activity' : "androidx.activity:activity:1.5.0",
'annotations' : "androidx.annotation:annotation:1.3.0",
'appCompat' : "androidx.appcompat:appcompat:1.4.2",
'biometric' : "androidx.biometric:biometric:1.1.0",

View file

@ -53,6 +53,7 @@ android {
dependencies {
implementation libs.androidx.appCompat
implementation libs.androidx.fragmentKtx
implementation libs.google.material
// Pref theme
implementation libs.androidx.preferenceKtx

View file

@ -18,8 +18,12 @@ package im.vector.lib.ui.styles.debug
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import im.vector.lib.ui.styles.R
@ -31,6 +35,7 @@ abstract class DebugMaterialThemeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupMenu()
val views = ActivityDebugMaterialThemeBinding.inflate(layoutInflater)
setContentView(views.root)
@ -72,6 +77,27 @@ abstract class DebugMaterialThemeActivity : AppCompatActivity() {
}
}
private fun setupMenu() {
addMenuProvider(
object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_debug, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
Toast.makeText(
this@DebugMaterialThemeActivity,
"Menu ${menuItem.title} clicked!",
Toast.LENGTH_SHORT
).show()
return true
}
},
this,
Lifecycle.State.RESUMED
)
}
private fun showTestDialog(theme: Int) {
MaterialAlertDialogBuilder(this, theme)
.setTitle("Dialog title")
@ -82,9 +108,4 @@ abstract class DebugMaterialThemeActivity : AppCompatActivity() {
.setNeutralButton("Neutral", null)
.show()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_debug, menu)
return true
}
}

View file

@ -180,3 +180,8 @@ System\.currentTimeMillis\(\)===2
### Remove extra space between the name and the description
\* @\w+ \w+ +
### Please use the MenuProvider interface now
onCreateOptionsMenu
onOptionsItemSelected
onPrepareOptionsMenu

View file

@ -19,11 +19,11 @@ package im.vector.app.core.platform
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.WindowInsetsController
@ -31,15 +31,18 @@ import android.view.WindowManager
import android.widget.TextView
import androidx.annotation.CallSuper
import androidx.annotation.MainThread
import androidx.annotation.MenuRes
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.MultiWindowModeChangedInfo
import androidx.core.content.ContextCompat
import androidx.core.util.Consumer
import androidx.core.view.MenuProvider
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding
@ -86,6 +89,7 @@ import im.vector.app.features.themes.ThemeUtils
import im.vector.app.receivers.DebugReceiver
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.failure.GlobalError
import org.matrix.android.sdk.api.failure.InitialSyncRequestReason
@ -199,6 +203,8 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
supportFragmentManager.fragmentFactory = fragmentFactory
viewModelFactory = activityEntryPoint.viewModelFactory()
super.onCreate(savedInstanceState)
addOnMultiWindowModeChangedListener(onMultiWindowModeChangedListener)
setupMenu()
configurationViewModel = viewModelProvider.get(ConfigurationViewModel::class.java)
bugReporter = singletonEntryPoint.bugReporter()
pinLocker = singletonEntryPoint.pinLocker()
@ -249,6 +255,32 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
}
}
private fun setupMenu() {
// Always add a MenuProvider to handle the back action from the Toolbar
val vectorMenuProvider = this as? VectorMenuProvider
addMenuProvider(
object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
vectorMenuProvider?.let {
menuInflater.inflate(it.getMenuRes(), menu)
it.handlePostCreateMenu(menu)
}
}
override fun onPrepareMenu(menu: Menu) {
vectorMenuProvider?.handlePrepareMenu(menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return vectorMenuProvider?.handleMenuItemSelected(menuItem).orFalse() ||
handleMenuItemHome(menuItem)
}
},
this,
Lifecycle.State.RESUMED
)
}
/**
* This method has to be called for the font size setting be supported correctly.
*/
@ -332,6 +364,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
}
override fun onDestroy() {
removeOnMultiWindowModeChangedListener(onMultiWindowModeChangedListener)
super.onDestroy()
Timber.i("onDestroy Activity ${javaClass.simpleName}")
}
@ -417,11 +450,9 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
}
}
override fun onMultiWindowModeChanged(isInMultiWindowMode: Boolean, newConfig: Configuration?) {
super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig)
Timber.w("onMultiWindowModeChanged. isInMultiWindowMode: $isInMultiWindowMode")
bugReporter.inMultiWindowMode = isInMultiWindowMode
private val onMultiWindowModeChangedListener = Consumer<MultiWindowModeChangedInfo> {
Timber.w("onMultiWindowModeChanged. isInMultiWindowMode: ${it.isInMultiWindowMode}")
bugReporter.inMultiWindowMode = it.isInMultiWindowMode
}
protected fun createFragment(fragmentClass: Class<out Fragment>, argsParcelable: Parcelable? = null): Fragment {
@ -463,28 +494,14 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
}
}
/* ==========================================================================================
* MENU MANAGEMENT
* ========================================================================================== */
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val menuRes = getMenuRes()
if (menuRes != -1) {
menuInflater.inflate(menuRes, menu)
return true
private fun handleMenuItemHome(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
onBackPressed(true)
true
}
else -> false
}
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressed(true)
return true
}
return super.onOptionsItemSelected(item)
}
override fun onBackPressed() {
@ -587,9 +604,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
@StringRes
open fun getTitleRes() = -1
@MenuRes
open fun getMenuRes() = -1
/**
* Return a object containing other themes for this activity.
*/

View file

@ -22,12 +22,16 @@ import android.os.Parcelable
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.annotation.CallSuper
import androidx.annotation.MainThread
import androidx.appcompat.app.AlertDialog
import androidx.core.view.MenuHost
import androidx.core.view.MenuProvider
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding
@ -126,9 +130,7 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
@CallSuper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (getMenuRes() != -1) {
setHasOptionsMenu(true)
}
Timber.i("onCreate Fragment ${javaClass.simpleName}")
}
final override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@ -158,6 +160,31 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Timber.i("onViewCreated Fragment ${javaClass.simpleName}")
setupMenu()
}
private fun setupMenu() {
if (this !is VectorMenuProvider) return
if (getMenuRes() == -1) return
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(
object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(getMenuRes(), menu)
handlePostCreateMenu(menu)
}
override fun onPrepareMenu(menu: Menu) {
handlePrepareMenu(menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return handleMenuItemSelected(menuItem)
}
},
viewLifecycleOwner,
Lifecycle.State.RESUMED
)
}
open fun showLoading(message: CharSequence?) {
@ -270,16 +297,6 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
* MENU MANAGEMENT
* ========================================================================================== */
open fun getMenuRes() = -1
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
val menuRes = getMenuRes()
if (menuRes != -1) {
inflater.inflate(menuRes, menu)
}
}
// This should be provided by the framework
protected fun invalidateOptionsMenu() = requireActivity().invalidateOptionsMenu()

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2022 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.core.platform
import android.view.Menu
import android.view.MenuItem
import androidx.annotation.MenuRes
/**
* Let your Activity of Fragment implement this interface if they provide a Menu.
*/
interface VectorMenuProvider {
@MenuRes
fun getMenuRes(): Int
// No op by default
fun handlePostCreateMenu(menu: Menu) {}
// No op by default
fun handlePrepareMenu(menu: Menu) {}
fun handleMenuItemSelected(item: MenuItem): Boolean
}

View file

@ -44,6 +44,7 @@ import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.insertBeforeLast
import im.vector.app.core.extensions.registerStartForActivityResult
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.time.Clock
import im.vector.app.core.utils.OnSnapPositionChangeListener
@ -67,7 +68,9 @@ class AttachmentsPreviewFragment @Inject constructor(
private val attachmentBigPreviewController: AttachmentBigPreviewController,
private val colorProvider: ColorProvider,
private val clock: Clock,
) : VectorBaseFragment<FragmentAttachmentsPreviewBinding>(), AttachmentMiniaturePreviewController.Callback {
) : VectorBaseFragment<FragmentAttachmentsPreviewBinding>(),
AttachmentMiniaturePreviewController.Callback,
VectorMenuProvider {
private val fragmentArgs: AttachmentsPreviewArgs by args()
private val viewModel: AttachmentsPreviewViewModel by fragmentViewModel()
@ -97,7 +100,7 @@ class AttachmentsPreviewFragment @Inject constructor(
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.attachmentsPreviewRemoveAction -> {
handleRemoveAction()
@ -107,20 +110,16 @@ class AttachmentsPreviewFragment @Inject constructor(
handleEditAction()
true
}
else -> {
super.onOptionsItemSelected(item)
}
else -> false
}
}
override fun onPrepareOptionsMenu(menu: Menu) {
override fun handlePrepareMenu(menu: Menu) {
withState(viewModel) { state ->
val editMenuItem = menu.findItem(R.id.attachmentsPreviewEditAction)
val showEditMenuItem = state.attachments.getOrNull(state.currentAttachmentIndex)?.isEditable().orFalse()
editMenuItem.setVisible(showEditMenuItem)
}
super.onPrepareOptionsMenu(menu)
}
override fun getMenuRes() = R.menu.vector_attachments_preview

View file

@ -22,7 +22,6 @@ import android.app.PictureInPictureParams
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
import android.content.res.Configuration
import android.graphics.Color
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
@ -35,8 +34,10 @@ import android.view.View
import android.view.WindowManager
import androidx.activity.result.ActivityResult
import androidx.annotation.StringRes
import androidx.core.app.PictureInPictureModeChangedInfo
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.core.util.Consumer
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import com.airbnb.mvrx.Fail
@ -50,6 +51,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.utils.PERMISSIONS_FOR_AUDIO_IP_CALL
import im.vector.app.core.utils.PERMISSIONS_FOR_VIDEO_IP_CALL
import im.vector.app.core.utils.checkPermissions
@ -94,7 +96,10 @@ data class CallArgs(
private val loggerTag = LoggerTag("VectorCallActivity", LoggerTag.VOIP)
@AndroidEntryPoint
class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallControlsView.InteractionListener {
class VectorCallActivity :
VectorBaseActivity<ActivityCallBinding>(),
CallControlsView.InteractionListener,
VectorMenuProvider {
override fun getBinding() = ActivityCallBinding.inflate(layoutInflater)
@ -128,6 +133,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
window.statusBarColor = Color.TRANSPARENT
window.navigationBarColor = Color.BLACK
super.onCreate(savedInstanceState)
addOnPictureInPictureModeChangedListener(pictureInPictureModeChangedInfoConsumer)
Timber.tag(loggerTag.value).v("EXTRA_MODE is ${intent.getStringExtra(EXTRA_MODE)}")
if (intent.getStringExtra(EXTRA_MODE) == INCOMING_RINGING) {
@ -210,25 +216,31 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && isInPictureInPictureMode
}
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) = withState(callViewModel) {
renderState(it)
private val pictureInPictureModeChangedInfoConsumer = Consumer<PictureInPictureModeChangedInfo> {
withState(callViewModel) {
renderState(it)
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.menu_call_open_chat) {
returnToChat()
return true
} else if (item.itemId == android.R.id.home) {
// We check here as we want PiP in some cases
onBackPressed()
return true
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_call_open_chat -> {
returnToChat()
true
}
android.R.id.home -> {
// We check here as we want PiP in some cases
onBackPressed()
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
override fun onDestroy() {
detachRenderersIfNeeded()
turnScreenOffAndKeyguardOn()
removeOnPictureInPictureModeChangedListener(pictureInPictureModeChangedInfoConsumer)
super.onDestroy()
}

View file

@ -19,12 +19,13 @@ package im.vector.app.features.call.conference
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.widget.FrameLayout
import android.widget.Toast
import androidx.core.app.PictureInPictureModeChangedInfo
import androidx.core.util.Consumer
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import com.airbnb.mvrx.Fail
@ -66,6 +67,7 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addOnPictureInPictureModeChangedListener(pictureInPictureModeChangedInfoConsumer)
jitsiViewModel.onEach {
renderState(it)
@ -109,6 +111,7 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
ConferenceEventEmitter(this).emitConferenceEnded()
}
JitsiMeetActivityDelegate.onHostDestroy(this)
removeOnPictureInPictureModeChangedListener(pictureInPictureModeChangedInfoConsumer)
super.onDestroy()
}
@ -138,13 +141,9 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
.show()
}
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean,
newConfig: Configuration
) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
private val pictureInPictureModeChangedInfoConsumer = Consumer<PictureInPictureModeChangedInfo> {
checkIfActivityShouldBeFinished()
Timber.w("onPictureInPictureModeChanged($isInPictureInPictureMode)")
Timber.w("onPictureInPictureModeChanged(${it.isInPictureInPictureMode})")
}
private fun checkIfActivityShouldBeFinished() {

View file

@ -49,6 +49,7 @@ class CallTransferPagerAdapter(
fragment.arguments = UserListFragmentArgs(
title = "",
menuResId = -1,
submitMenuItemId = -1,
singleSelection = true,
showInviteActions = false,
showToolbar = false,

View file

@ -81,7 +81,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
when (action) {
UserListSharedAction.Close -> finish()
UserListSharedAction.GoBack -> onBackPressed()
is UserListSharedAction.OnMenuItemSelected -> onMenuItemSelected(action)
is UserListSharedAction.OnMenuItemSubmitClick -> handleOnMenuItemSubmitClick(action)
UserListSharedAction.OpenPhoneBook -> openPhoneBook()
UserListSharedAction.AddByQrCode -> openAddByQrCode()
}
@ -93,7 +93,8 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
UserListFragment::class.java,
UserListFragmentArgs(
title = getString(R.string.fab_menu_create_chat),
menuResId = R.menu.vector_create_direct_room
menuResId = R.menu.vector_create_direct_room,
submitMenuItemId = R.id.action_create_direct_room,
)
)
}
@ -159,10 +160,8 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
}
}
private fun onMenuItemSelected(action: UserListSharedAction.OnMenuItemSelected) {
if (action.itemId == R.id.action_create_direct_room) {
viewModel.handle(CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers(action.selections))
}
private fun handleOnMenuItemSubmitClick(action: UserListSharedAction.OnMenuItemSubmitClick) {
viewModel.handle(CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers(action.selections))
}
private fun renderCreateAndInviteState(state: Async<String>) {

View file

@ -187,16 +187,6 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
}
}
// I think this code is useful, but it violates the code quality rules
// override fun onOptionsItemSelected(item: MenuItem): Boolean {
// if (item.itemId == android .R. id. home) {
// onBackPressed()
// return true
// }
//
// return super.onOptionsItemSelected(item)
// }
companion object {
const val KEYS_VERSION = "KEYS_VERSION"
const val MANUAL_EXPORT = "MANUAL_EXPORT"

View file

@ -21,7 +21,6 @@ import android.content.Intent
import android.os.Parcelable
import android.view.Menu
import android.view.MenuItem
import androidx.core.view.forEach
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.airbnb.mvrx.Fail
@ -36,6 +35,7 @@ import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.platform.SimpleFragmentActivity
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.utils.createJSonViewerStyleProvider
import kotlinx.parcelize.Parcelize
@ -43,7 +43,10 @@ import org.billcarsonfr.jsonviewer.JSonViewerFragment
import javax.inject.Inject
@AndroidEntryPoint
class RoomDevToolActivity : SimpleFragmentActivity(), FragmentManager.OnBackStackChangedListener {
class RoomDevToolActivity :
SimpleFragmentActivity(),
FragmentManager.OnBackStackChangedListener,
VectorMenuProvider {
@Inject lateinit var colorProvider: ColorProvider
@ -133,16 +136,18 @@ class RoomDevToolActivity : SimpleFragmentActivity(), FragmentManager.OnBackStac
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.menuItemEdit) {
viewModel.handle(RoomDevToolAction.MenuEdit)
return true
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menuItemEdit -> {
viewModel.handle(RoomDevToolAction.MenuEdit)
true
}
R.id.menuItemSend -> {
viewModel.handle(RoomDevToolAction.MenuItemSend)
true
}
else -> false
}
if (item.itemId == R.id.menuItemSend) {
viewModel.handle(RoomDevToolAction.MenuItemSend)
return true
}
return super.onOptionsItemSelected(item)
}
override fun onBackPressed() {
@ -174,21 +179,12 @@ class RoomDevToolActivity : SimpleFragmentActivity(), FragmentManager.OnBackStac
super.onDestroy()
}
override fun onPrepareOptionsMenu(menu: Menu?): Boolean = withState(viewModel) { state ->
menu?.forEach {
val isVisible = when (it.itemId) {
R.id.menuItemEdit -> {
state.displayMode is RoomDevToolViewState.Mode.StateEventDetail
}
R.id.menuItemSend -> {
state.displayMode is RoomDevToolViewState.Mode.EditEventContent ||
state.displayMode is RoomDevToolViewState.Mode.SendEventForm
}
else -> true
}
it.isVisible = isVisible
override fun handlePrepareMenu(menu: Menu) {
withState(viewModel) { state ->
menu.findItem(R.id.menuItemEdit).isVisible = state.displayMode == RoomDevToolViewState.Mode.StateEventDetail
menu.findItem(R.id.menuItemSend).isVisible = state.displayMode == RoomDevToolViewState.Mode.EditEventContent ||
state.displayMode is RoomDevToolViewState.Mode.SendEventForm
}
return@withState true
}
companion object {

View file

@ -43,6 +43,7 @@ import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.extensions.validateBackPressed
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.databinding.ActivityHomeBinding
@ -103,7 +104,8 @@ class HomeActivity :
VectorBaseActivity<ActivityHomeBinding>(),
NavigationInterceptor,
SpaceInviteBottomSheet.InteractionListener,
MatrixToBottomSheet.InteractionListener {
MatrixToBottomSheet.InteractionListener,
VectorMenuProvider {
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
@ -547,47 +549,45 @@ class HomeActivity :
override fun getMenuRes() = R.menu.home
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
override fun handlePrepareMenu(menu: Menu) {
menu.findItem(R.id.menu_home_init_sync_legacy).isVisible = vectorPreferences.developerMode()
menu.findItem(R.id.menu_home_init_sync_optimized).isVisible = vectorPreferences.developerMode()
return super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_home_suggestion -> {
bugReporter.openBugReportScreen(this, ReportType.SUGGESTION)
return true
true
}
R.id.menu_home_report_bug -> {
bugReporter.openBugReportScreen(this, ReportType.BUG_REPORT)
return true
true
}
R.id.menu_home_init_sync_legacy -> {
// Configure the SDK
initialSyncStrategy = InitialSyncStrategy.Legacy
// And clear cache
MainActivity.restartApp(this, MainActivityArgs(clearCache = true))
return true
true
}
R.id.menu_home_init_sync_optimized -> {
// Configure the SDK
initialSyncStrategy = InitialSyncStrategy.Optimized()
// And clear cache
MainActivity.restartApp(this, MainActivityArgs(clearCache = true))
return true
true
}
R.id.menu_home_filter -> {
navigator.openRoomsFiltering(this)
return true
true
}
R.id.menu_home_setting -> {
navigator.openSettings(this)
return true
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
override fun onBackPressed() {

View file

@ -36,6 +36,7 @@ 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
@ -71,7 +72,8 @@ class HomeDetailFragment @Inject constructor(
) : VectorBaseFragment<FragmentHomeDetailBinding>(),
KeysBackupBanner.Delegate,
CurrentCallsView.Callback,
OnBackPressed {
OnBackPressed,
VectorMenuProvider {
private val viewModel: HomeDetailViewModel by fragmentViewModel()
private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
@ -91,23 +93,21 @@ class HomeDetailFragment @Inject constructor(
override fun getMenuRes() = R.menu.room_list
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_home_mark_all_as_read -> {
viewModel.handle(HomeDetailAction.MarkAllRoomsRead)
return true
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
override fun onPrepareOptionsMenu(menu: Menu) {
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
}
super.onPrepareOptionsMenu(menu)
}
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentHomeDetailBinding {

View file

@ -30,7 +30,6 @@ import android.view.HapticFeedbackConstants
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
@ -89,6 +88,7 @@ import im.vector.app.core.hardware.vibrate
import im.vector.app.core.intent.getFilenameFromUri
import im.vector.app.core.intent.getMimeTypeFromUri
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.platform.lifecycleAwareLazy
import im.vector.app.core.platform.showOptimizedSnackbar
import im.vector.app.core.resources.ColorProvider
@ -280,7 +280,8 @@ class TimelineFragment @Inject constructor(
AttachmentTypeSelectorView.Callback,
AttachmentsHelper.Callback,
GalleryOrCameraDialogHelper.Listener,
CurrentCallsView.Callback {
CurrentCallsView.Callback,
VectorMenuProvider {
companion object {
@ -1055,15 +1056,14 @@ class TimelineFragment @Inject constructor(
}
@SuppressLint("RestrictedApi")
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
override fun handlePostCreateMenu(menu: Menu) {
if (isThreadTimeLine()) {
if (menu is MenuBuilder) menu.setOptionalIconsVisible(true)
}
super.onCreateOptionsMenu(menu, inflater)
// We use a custom layout for this menu item, so we need to set a ClickListener
menu.findItem(R.id.open_matrix_apps)?.let { menuItem ->
menuItem.actionView.debouncedClicks {
onOptionsItemSelected(menuItem)
handleMenuItemSelected(menuItem)
}
}
val joinConfItem = menu.findItem(R.id.join_conference)
@ -1073,13 +1073,13 @@ class TimelineFragment @Inject constructor(
// Custom thread notification menu item
menu.findItem(R.id.menu_timeline_thread_list)?.let { menuItem ->
menuItem.actionView.setOnClickListener {
onOptionsItemSelected(menuItem)
menuItem.actionView.debouncedClicks {
handleMenuItemSelected(menuItem)
}
}
}
override fun onPrepareOptionsMenu(menu: Menu) {
override fun handlePrepareMenu(menu: Menu) {
menu.forEach {
it.isVisible = timelineViewModel.isMenuItemVisible(it.itemId)
}
@ -1121,7 +1121,7 @@ class TimelineFragment @Inject constructor(
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.invite -> {
navigator.openInviteUsersToRoom(requireActivity(), timelineArgs.roomId)
@ -1174,7 +1174,7 @@ class TimelineFragment @Inject constructor(
}
true
}
else -> super.onOptionsItemSelected(item)
else -> false
}
}

View file

@ -19,7 +19,6 @@ package im.vector.app.features.home.room.threads.list.views
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
@ -31,6 +30,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.databinding.FragmentThreadListBinding
import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.home.AvatarRenderer
@ -54,7 +54,8 @@ class ThreadListFragment @Inject constructor(
private val threadListController: ThreadListController,
val threadListViewModelFactory: ThreadListViewModel.Factory
) : VectorBaseFragment<FragmentThreadListBinding>(),
ThreadListController.Listener {
ThreadListController.Listener,
VectorMenuProvider {
private val threadListViewModel: ThreadListViewModel by fragmentViewModel()
@ -71,27 +72,26 @@ class ThreadListFragment @Inject constructor(
analyticsScreenName = MobileScreen.ScreenName.ThreadList
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
override fun handlePostCreateMenu(menu: Menu) {
// We use a custom layout for this menu item, so we need to set a ClickListener
menu.findItem(R.id.menu_thread_list_filter)?.let { menuItem ->
menuItem.actionView.setOnClickListener {
onOptionsItemSelected(menuItem)
menuItem.actionView.debouncedClicks {
handleMenuItemSelected(menuItem)
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_thread_list_filter -> {
ThreadListBottomSheet().show(childFragmentManager, "Filtering")
true
}
else -> super.onOptionsItemSelected(item)
else -> false
}
}
override fun onPrepareOptionsMenu(menu: Menu) {
override fun handlePrepareMenu(menu: Menu) {
withState(threadListViewModel) { state ->
val filterIcon = menu.findItem(R.id.menu_thread_list_filter).actionView
val filterBadge = filterIcon.findViewById<View>(R.id.threadListFilterBadge)

View file

@ -71,7 +71,7 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity() {
when (sharedAction) {
UserListSharedAction.Close -> finish()
UserListSharedAction.GoBack -> onBackPressed()
is UserListSharedAction.OnMenuItemSelected -> onMenuItemSelected(sharedAction)
is UserListSharedAction.OnMenuItemSubmitClick -> handleOnMenuItemSubmitClick(sharedAction)
UserListSharedAction.OpenPhoneBook -> openPhoneBook()
// not exhaustive because it's a sharedAction
else -> Unit
@ -85,6 +85,7 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity() {
UserListFragmentArgs(
title = getString(R.string.invite_users_to_room_title),
menuResId = R.menu.vector_invite_users_to_room,
submitMenuItemId = R.id.action_invite_users_to_room_invite,
excludedUserIds = viewModel.getUserIdsOfRoomMembers(),
showInviteActions = false
)
@ -94,10 +95,8 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity() {
viewModel.observeViewEvents { renderInviteEvents(it) }
}
private fun onMenuItemSelected(action: UserListSharedAction.OnMenuItemSelected) {
if (action.itemId == R.id.action_invite_users_to_room_invite) {
viewModel.handle(InviteUsersToRoomAction.InviteSelectedUsers(action.selections))
}
private fun handleOnMenuItemSubmitClick(action: UserListSharedAction.OnMenuItemSubmitClick) {
viewModel.handle(InviteUsersToRoomAction.InviteSelectedUsers(action.selections))
}
private fun openPhoneBook() {

View file

@ -26,6 +26,7 @@ import com.airbnb.mvrx.args
import com.mapbox.mapboxsdk.maps.MapView
import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.utils.openLocation
import im.vector.app.databinding.FragmentLocationPreviewBinding
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
@ -38,7 +39,8 @@ import javax.inject.Inject
class LocationPreviewFragment @Inject constructor(
private val urlMapProvider: UrlMapProvider,
private val locationPinProvider: LocationPinProvider
) : VectorBaseFragment<FragmentLocationPreviewBinding>() {
) : VectorBaseFragment<FragmentLocationPreviewBinding>(),
VectorMenuProvider {
private val args: LocationSharingArgs by args()
@ -99,14 +101,14 @@ class LocationPreviewFragment @Inject constructor(
override fun getMenuRes() = R.menu.menu_location_preview
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.share_external -> {
onShareLocationExternal()
return true
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
private fun onShareLocationExternal() {

View file

@ -28,6 +28,7 @@ import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.databinding.ActivityBugReportBinding
import org.matrix.android.sdk.api.extensions.tryOrNull
import timber.log.Timber
@ -36,7 +37,9 @@ import timber.log.Timber
* Form to send a bug report.
*/
@AndroidEntryPoint
class BugReportActivity : VectorBaseActivity<ActivityBugReportBinding>() {
class BugReportActivity :
VectorBaseActivity<ActivityBugReportBinding>(),
VectorMenuProvider {
override fun getBinding() = ActivityBugReportBinding.inflate(layoutInflater)
@ -120,29 +123,27 @@ class BugReportActivity : VectorBaseActivity<ActivityBugReportBinding>() {
override fun getMenuRes() = R.menu.bug_report
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
override fun handlePrepareMenu(menu: Menu) {
menu.findItem(R.id.ic_action_send_bug_report)?.let {
val isValid = !views.bugReportMaskView.isVisible
it.isEnabled = isValid
it.icon.alpha = if (isValid) 255 else 100
}
return super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.ic_action_send_bug_report -> {
if (views.bugReportEditText.text.toString().trim().length >= 10) {
sendBugReport()
} else {
views.bugReportTextInputLayout.error = getString(R.string.bug_report_error_too_short)
}
return true
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
/**

View file

@ -21,7 +21,6 @@ import android.content.Intent
import android.graphics.Typeface
import android.util.TypedValue
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.widget.SearchView
import androidx.core.view.isVisible
@ -33,6 +32,7 @@ import im.vector.app.EmojiCompatFontProvider
import im.vector.app.R
import im.vector.app.core.extensions.observeEvent
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.databinding.ActivityEmojiReactionPickerBinding
import im.vector.app.features.reactions.data.EmojiDataSource
import im.vector.lib.core.utils.flow.throttleFirst
@ -48,13 +48,17 @@ import javax.inject.Inject
* TODO Finish Refactor to vector base activity
*/
@AndroidEntryPoint
class EmojiReactionPickerActivity : VectorBaseActivity<ActivityEmojiReactionPickerBinding>(),
EmojiCompatFontProvider.FontProviderListener {
class EmojiReactionPickerActivity :
VectorBaseActivity<ActivityEmojiReactionPickerBinding>(),
EmojiCompatFontProvider.FontProviderListener,
VectorMenuProvider {
lateinit var viewModel: EmojiChooserViewModel
override fun getMenuRes() = R.menu.menu_emoji_reaction_picker
override fun handleMenuItemSelected(item: MenuItem) = false
override fun getBinding() = ActivityEmojiReactionPickerBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
@ -138,10 +142,7 @@ class EmojiReactionPickerActivity : VectorBaseActivity<ActivityEmojiReactionPick
super.onDestroy()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater: MenuInflater = menuInflater
inflater.inflate(getMenuRes(), menu)
override fun handlePostCreateMenu(menu: Menu) {
val searchItem = menu.findItem(R.id.search)
(searchItem.actionView as? SearchView)?.let { searchView ->
searchItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
@ -175,7 +176,6 @@ class EmojiReactionPickerActivity : VectorBaseActivity<ActivityEmojiReactionPick
.launchIn(lifecycleScope)
}
searchItem.expandActionView()
return true
}
// TODO move to ThemeUtils when core module is created

View file

@ -30,6 +30,7 @@ import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.trackItemsVisibilityChange
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.platform.showOptimizedSnackbar
import im.vector.app.core.utils.toast
import im.vector.app.databinding.FragmentPublicRoomsBinding
@ -55,7 +56,8 @@ class PublicRoomsFragment @Inject constructor(
private val permalinkHandler: PermalinkHandler,
private val session: Session
) : VectorBaseFragment<FragmentPublicRoomsBinding>(),
PublicRoomsController.Callback {
PublicRoomsController.Callback,
VectorMenuProvider {
private val viewModel: RoomDirectoryViewModel by activityViewModel()
private lateinit var sharedActionViewModel: RoomDirectorySharedActionViewModel
@ -105,14 +107,13 @@ class PublicRoomsFragment @Inject constructor(
super.onDestroyView()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_room_directory_change_protocol -> {
sharedActionViewModel.post(RoomDirectorySharedAction.ChangeProtocol)
true
}
else ->
super.onOptionsItemSelected(item)
else -> false
}
}

View file

@ -42,6 +42,7 @@ import im.vector.app.core.extensions.copyOnLongClick
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.StateView
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.DialogBaseEditTextBinding
import im.vector.app.databinding.DialogShareQrCodeBinding
@ -74,7 +75,8 @@ class RoomMemberProfileFragment @Inject constructor(
private val roomDetailPendingActionStore: RoomDetailPendingActionStore,
private val matrixItemColorProvider: MatrixItemColorProvider
) : VectorBaseFragment<FragmentMatrixProfileBinding>(),
RoomMemberProfileController.Callback {
RoomMemberProfileController.Callback,
VectorMenuProvider {
private lateinit var headerViews: ViewStubRoomMemberProfileHeaderBinding
@ -160,14 +162,14 @@ class RoomMemberProfileFragment @Inject constructor(
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.roomMemberProfileShareAction -> {
viewModel.handle(RoomMemberProfileAction.ShareRoomMemberProfile)
return true
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
private fun handleStartVerification(startVerification: RoomMemberProfileViewEvents.StartVerification) {

View file

@ -39,6 +39,7 @@ import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.copyOnLongClick
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.FragmentMatrixProfileBinding
@ -70,7 +71,8 @@ class RoomProfileFragment @Inject constructor(
private val roomDetailPendingActionStore: RoomDetailPendingActionStore,
) :
VectorBaseFragment<FragmentMatrixProfileBinding>(),
RoomProfileController.Callback {
RoomProfileController.Callback,
VectorMenuProvider {
private lateinit var headerViews: ViewStubRoomProfileHeaderBinding
@ -170,14 +172,14 @@ class RoomProfileFragment @Inject constructor(
headerViews.roomProfileAliasView.copyOnLongClick()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.roomProfileShareAction -> {
roomProfileViewModel.handle(RoomProfileAction.ShareRoomProfile)
return true
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
private fun handleQuickActions(action: RoomListQuickActionsSharedAction) = when (action) {

View file

@ -36,6 +36,7 @@ import im.vector.app.core.extensions.configureWith
import im.vector.app.core.intent.getFilenameFromUri
import im.vector.app.core.platform.OnBackPressed
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.time.Clock
import im.vector.app.core.utils.toast
@ -64,7 +65,8 @@ class RoomSettingsFragment @Inject constructor(
VectorBaseFragment<FragmentRoomSettingGenericBinding>(),
RoomSettingsController.Callback,
OnBackPressed,
GalleryOrCameraDialogHelper.Listener {
GalleryOrCameraDialogHelper.Listener,
VectorMenuProvider {
private val viewModel: RoomSettingsViewModel by fragmentViewModel()
private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel
@ -139,18 +141,20 @@ class RoomSettingsFragment @Inject constructor(
super.onDestroyView()
}
override fun onPrepareOptionsMenu(menu: Menu) {
override fun handlePrepareMenu(menu: Menu) {
withState(viewModel) { state ->
menu.findItem(R.id.roomSettingsSaveAction).isVisible = state.showSaveAction
}
super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.roomSettingsSaveAction) {
viewModel.handle(RoomSettingsAction.Save)
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.roomSettingsSaveAction -> {
viewModel.handle(RoomSettingsAction.Save)
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
override fun invalidate() = withState(viewModel) { viewState ->

View file

@ -26,8 +26,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
@ -36,6 +34,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.safeOpenOutputStream
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.time.Clock
import im.vector.app.core.utils.selectTxtFileToWrite
import im.vector.app.databinding.FragmentDevtoolKeyrequestsBinding
@ -44,7 +43,8 @@ import javax.inject.Inject
class KeyRequestsFragment @Inject constructor(
private val clock: Clock,
) : VectorBaseFragment<FragmentDevtoolKeyrequestsBinding>() {
) : VectorBaseFragment<FragmentDevtoolKeyrequestsBinding>(),
VectorMenuProvider {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentDevtoolKeyrequestsBinding {
return FragmentDevtoolKeyrequestsBinding.inflate(inflater, container, false)
@ -61,19 +61,6 @@ class KeyRequestsFragment @Inject constructor(
override fun getMenuRes(): Int = R.menu.menu_audit
private val pageAdapterListener = object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
invalidateOptionsMenu()
}
override fun onPageScrollStateChanged(state: Int) {
childFragmentManager.fragments.forEach {
it.setHasOptionsMenu(state == SCROLL_STATE_IDLE)
}
invalidateOptionsMenu()
}
}
override fun invalidate() = withState(viewModel) {
when (it.exporting) {
is Loading -> views.exportWaitingView.isVisible = true
@ -81,16 +68,10 @@ class KeyRequestsFragment @Inject constructor(
}
}
override fun onDestroy() {
invalidateOptionsMenu()
super.onDestroy()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mPagerAdapter = KeyReqPagerAdapter(this)
views.devToolKeyRequestPager.adapter = mPagerAdapter
views.devToolKeyRequestPager.registerOnPageChangeCallback(pageAdapterListener)
TabLayoutMediator(views.devToolKeyRequestTabs, views.devToolKeyRequestPager) { tab, position ->
when (position) {
@ -119,25 +100,26 @@ class KeyRequestsFragment @Inject constructor(
}
override fun onDestroyView() {
views.devToolKeyRequestPager.unregisterOnPageChangeCallback(pageAdapterListener)
mPagerAdapter = null
super.onDestroyView()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.audit_export) {
selectTxtFileToWrite(
activity = requireActivity(),
activityResultLauncher = epxortAuditForActivityResult,
defaultFileName = "audit-export_${clock.epochMillis()}.txt",
chooserHint = "Export Audit"
)
return true
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.audit_export -> {
selectTxtFileToWrite(
activity = requireActivity(),
activityResultLauncher = exportAuditForActivityResult,
defaultFileName = "audit-export_${clock.epochMillis()}.txt",
chooserHint = "Export Audit"
)
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
private val epxortAuditForActivityResult = registerStartForActivityResult { activityResult ->
private val exportAuditForActivityResult = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) {
val uri = activityResult.data?.data
if (uri != null) {

View file

@ -29,6 +29,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import org.matrix.android.sdk.api.session.pushers.Pusher
import javax.inject.Inject
@ -36,7 +37,8 @@ import javax.inject.Inject
// Referenced in vector_settings_notifications.xml
class PushGatewaysFragment @Inject constructor(
private val epoxyController: PushGateWayController
) : VectorBaseFragment<FragmentGenericRecyclerBinding>() {
) : VectorBaseFragment<FragmentGenericRecyclerBinding>(),
VectorMenuProvider {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGenericRecyclerBinding {
return FragmentGenericRecyclerBinding.inflate(inflater, container, false)
@ -46,14 +48,13 @@ class PushGatewaysFragment @Inject constructor(
override fun getMenuRes() = R.menu.menu_push_gateways
override fun onOptionsItemSelected(item: MenuItem): Boolean {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.refresh -> {
viewModel.handle(PushGatewayAction.Refresh)
true
}
else ->
super.onOptionsItemSelected(item)
else -> false
}
}

View file

@ -38,6 +38,7 @@ import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.OnBackPressed
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.utils.colorizeMatchingText
import im.vector.app.core.utils.isValidUrl
@ -67,7 +68,8 @@ class SpaceDirectoryFragment @Inject constructor(
) : VectorBaseFragment<FragmentSpaceDirectoryBinding>(),
SpaceDirectoryController.InteractionListener,
TimelineEventController.UrlClickCallback,
OnBackPressed {
OnBackPressed,
VectorMenuProvider {
override fun getMenuRes() = R.menu.menu_space_directory
@ -177,40 +179,41 @@ class SpaceDirectoryFragment @Inject constructor(
views.addOrCreateChatRoomButton.isVisible = state.canAddRooms
}
override fun onPrepareOptionsMenu(menu: Menu) = withState(viewModel) { state ->
menu.findItem(R.id.spaceAddRoom)?.isVisible = state.canAddRooms
menu.findItem(R.id.spaceCreateRoom)?.isVisible = false // Not yet implemented
override fun handlePrepareMenu(menu: Menu) {
withState(viewModel) { state ->
menu.findItem(R.id.spaceAddRoom)?.isVisible = state.canAddRooms
menu.findItem(R.id.spaceCreateRoom)?.isVisible = false // Not yet implemented
menu.findItem(R.id.spaceSearch)?.let { searchItem ->
val searchView = searchItem.actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return true
}
menu.findItem(R.id.spaceSearch)?.let { searchItem ->
val searchView = searchItem.actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
onFilterQueryChanged(newText)
return true
}
})
override fun onQueryTextChange(newText: String?): Boolean {
onFilterQueryChanged(newText)
return true
}
})
}
}
super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.spaceAddRoom -> {
withState(viewModel) { state ->
addExistingRooms(state.spaceId)
}
return true
true
}
R.id.spaceCreateRoom -> {
// not implemented yet
return true
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
override fun onFilterQueryChanged(query: String?) {

View file

@ -32,6 +32,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.utils.ToggleableAppBarLayoutBehavior
import im.vector.app.databinding.FragmentSpaceLeaveAdvancedBinding
import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -40,7 +41,8 @@ import javax.inject.Inject
class SpaceLeaveAdvancedFragment @Inject constructor(
val controller: SelectChildrenController
) : VectorBaseFragment<FragmentSpaceLeaveAdvancedBinding>(),
SelectChildrenController.Listener {
SelectChildrenController.Listener,
VectorMenuProvider {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
FragmentSpaceLeaveAdvancedBinding.inflate(layoutInflater, container, false)
@ -49,6 +51,8 @@ class SpaceLeaveAdvancedFragment @Inject constructor(
override fun getMenuRes() = R.menu.menu_space_leave
override fun handleMenuItemSelected(item: MenuItem) = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -89,7 +93,7 @@ class SpaceLeaveAdvancedFragment @Inject constructor(
}
}
override fun onPrepareOptionsMenu(menu: Menu) {
override fun handlePrepareMenu(menu: Menu) {
menu.findItem(R.id.menu_space_leave_search)?.let { searchItem ->
searchItem.bind(
onExpanded = { viewModel.handle(SpaceLeaveAdvanceViewAction.SetFilteringEnabled(isEnabled = true)) },
@ -97,7 +101,6 @@ class SpaceLeaveAdvancedFragment @Inject constructor(
onTextChanged = { viewModel.handle(SpaceLeaveAdvanceViewAction.UpdateFilter(it)) }
)
}
super.onPrepareOptionsMenu(menu)
}
override fun onDestroyView() {

View file

@ -36,6 +36,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.databinding.FragmentSpaceAddRoomsBinding
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.debounce
@ -51,7 +52,9 @@ class SpaceAddRoomFragment @Inject constructor(
private val roomEpoxyController: AddRoomListController,
private val dmEpoxyController: AddRoomListController,
) : VectorBaseFragment<FragmentSpaceAddRoomsBinding>(),
OnBackPressed, AddRoomListController.Listener {
OnBackPressed,
AddRoomListController.Listener,
VectorMenuProvider {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
FragmentSpaceAddRoomsBinding.inflate(layoutInflater, container, false)
@ -151,17 +154,18 @@ class SpaceAddRoomFragment @Inject constructor(
}
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
override fun handlePrepareMenu(menu: Menu) {
menu.findItem(R.id.spaceAddRoomSaveItem).isVisible = saveNeeded
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.spaceAddRoomSaveItem) {
viewModel.handle(SpaceAddRoomActions.Save)
return true
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.spaceAddRoomSaveItem -> {
viewModel.handle(SpaceAddRoomActions.Save)
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
override fun onDestroyView() {

View file

@ -37,6 +37,7 @@ import im.vector.app.core.extensions.configureWith
import im.vector.app.core.intent.getFilenameFromUri
import im.vector.app.core.platform.OnBackPressed
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.time.Clock
import im.vector.app.core.utils.toast
@ -66,7 +67,8 @@ class SpaceSettingsFragment @Inject constructor(
) : VectorBaseFragment<FragmentRoomSettingGenericBinding>(),
SpaceSettingsController.Callback,
GalleryOrCameraDialogHelper.Listener,
OnBackPressed {
OnBackPressed,
VectorMenuProvider {
private val viewModel: RoomSettingsViewModel by fragmentViewModel()
private val sharedViewModel: SpaceManageSharedViewModel by activityViewModel()
@ -111,18 +113,20 @@ class SpaceSettingsFragment @Inject constructor(
super.onDestroyView()
}
override fun onPrepareOptionsMenu(menu: Menu) {
override fun handlePrepareMenu(menu: Menu) {
withState(viewModel) { state ->
menu.findItem(R.id.roomSettingsSaveAction).isVisible = state.showSaveAction
}
super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.roomSettingsSaveAction) {
viewModel.handle(RoomSettingsAction.Save)
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.roomSettingsSaveAction -> {
viewModel.handle(RoomSettingsAction.Save)
true
}
else -> false
}
return super.onOptionsItemSelected(item)
}
private fun renderRoomSummary(state: RoomSettingsViewState) {

View file

@ -23,7 +23,6 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.ScrollView
import androidx.core.view.forEach
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.activityViewModel
@ -37,6 +36,7 @@ import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.setupAsSearch
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.core.utils.showIdentityServerConsentDialog
import im.vector.app.core.utils.startSharePlainTextIntent
@ -55,7 +55,8 @@ class UserListFragment @Inject constructor(
private val userListController: UserListController,
private val dimensionConverter: DimensionConverter,
) : VectorBaseFragment<FragmentUserListBinding>(),
UserListController.Callback {
UserListController.Callback,
VectorMenuProvider {
private val args: UserListFragmentArgs by args()
private val viewModel: UserListViewModel by activityViewModel()
@ -113,19 +114,24 @@ class UserListFragment @Inject constructor(
super.onDestroyView()
}
override fun onPrepareOptionsMenu(menu: Menu) {
override fun handlePrepareMenu(menu: Menu) {
if (args.submitMenuItemId == -1) return
withState(viewModel) {
val showMenuItem = it.pendingSelections.isNotEmpty()
menu.forEach { menuItem ->
menuItem.isVisible = showMenuItem
}
menu.findItem(args.submitMenuItemId).isVisible = showMenuItem
}
super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean = withState(viewModel) {
sharedActionViewModel.post(UserListSharedAction.OnMenuItemSelected(item.itemId, it.pendingSelections))
return@withState true
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
args.submitMenuItemId -> {
withState(viewModel) {
sharedActionViewModel.post(UserListSharedAction.OnMenuItemSubmitClick(it.pendingSelections))
}
true
}
else -> false
}
}
private fun setupRecyclerView() {

View file

@ -23,6 +23,7 @@ import kotlinx.parcelize.Parcelize
data class UserListFragmentArgs(
val title: String,
val menuResId: Int,
val submitMenuItemId: Int,
val excludedUserIds: Set<String>? = null,
val singleSelection: Boolean = false,
val showInviteActions: Boolean = true,

View file

@ -21,7 +21,7 @@ import im.vector.app.core.platform.VectorSharedAction
sealed class UserListSharedAction : VectorSharedAction {
object Close : UserListSharedAction()
object GoBack : UserListSharedAction()
data class OnMenuItemSelected(val itemId: Int, val selections: Set<PendingSelection>) : UserListSharedAction()
data class OnMenuItemSubmitClick(val selections: Set<PendingSelection>) : UserListSharedAction()
object OpenPhoneBook : UserListSharedAction()
object AddByQrCode : UserListSharedAction()
}

View file

@ -64,8 +64,6 @@ class WidgetActivity : VectorBaseActivity<ActivityWidgetBinding>() {
override fun getBinding() = ActivityWidgetBinding.inflate(layoutInflater)
override fun getMenuRes() = R.menu.menu_widget
override fun getTitleRes() = R.string.room_widget_activity_title
override fun initUiAndData() {

View file

@ -42,6 +42,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.VectorMenuProvider
import im.vector.app.core.utils.openUrlInExternalBrowser
import im.vector.app.databinding.FragmentRoomWidgetBinding
import im.vector.app.features.webview.WebEventListener
@ -68,7 +69,8 @@ class WidgetFragment @Inject constructor(
) :
VectorBaseFragment<FragmentRoomWidgetBinding>(),
WebEventListener,
OnBackPressed {
OnBackPressed,
VectorMenuProvider {
private val fragmentArgs: WidgetArgs by args()
private val viewModel: WidgetViewModel by activityViewModel()
@ -79,7 +81,6 @@ class WidgetFragment @Inject constructor(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
views.widgetWebView.setupForWidget(this)
if (fragmentArgs.kind.isAdmin()) {
viewModel.getPostAPIMediator().setWebView(views.widgetWebView)
@ -136,53 +137,64 @@ class WidgetFragment @Inject constructor(
}
}
override fun onPrepareOptionsMenu(menu: Menu) = withState(viewModel) { state ->
val widget = state.asyncWidget()
menu.findItem(R.id.action_edit)?.isVisible = state.widgetKind != WidgetKind.INTEGRATION_MANAGER
if (widget == null) {
menu.findItem(R.id.action_refresh)?.isVisible = false
menu.findItem(R.id.action_widget_open_ext)?.isVisible = false
menu.findItem(R.id.action_delete)?.isVisible = false
menu.findItem(R.id.action_revoke)?.isVisible = false
} else {
menu.findItem(R.id.action_refresh)?.isVisible = true
menu.findItem(R.id.action_widget_open_ext)?.isVisible = true
menu.findItem(R.id.action_delete)?.isVisible = state.canManageWidgets && widget.isAddedByMe
menu.findItem(R.id.action_revoke)?.isVisible = state.status == WidgetStatus.WIDGET_ALLOWED && !widget.isAddedByMe
override fun getMenuRes() = R.menu.menu_widget
override fun handlePrepareMenu(menu: Menu) {
withState(viewModel) { state ->
val widget = state.asyncWidget()
menu.findItem(R.id.action_edit)?.isVisible = state.widgetKind != WidgetKind.INTEGRATION_MANAGER
if (widget == null) {
menu.findItem(R.id.action_refresh)?.isVisible = false
menu.findItem(R.id.action_widget_open_ext)?.isVisible = false
menu.findItem(R.id.action_delete)?.isVisible = false
menu.findItem(R.id.action_revoke)?.isVisible = false
} else {
menu.findItem(R.id.action_refresh)?.isVisible = true
menu.findItem(R.id.action_widget_open_ext)?.isVisible = true
menu.findItem(R.id.action_delete)?.isVisible = state.canManageWidgets && widget.isAddedByMe
menu.findItem(R.id.action_revoke)?.isVisible = state.status == WidgetStatus.WIDGET_ALLOWED && !widget.isAddedByMe
}
}
super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean = withState(viewModel) { state ->
when (item.itemId) {
R.id.action_edit -> {
navigator.openIntegrationManager(
requireContext(),
integrationManagerActivityResultLauncher,
state.roomId,
state.widgetId,
state.widgetKind.screenId
)
return@withState true
}
R.id.action_delete -> {
deleteWidget()
return@withState true
}
R.id.action_refresh -> if (state.formattedURL.complete) {
views.widgetWebView.reload()
return@withState true
}
R.id.action_widget_open_ext -> if (state.formattedURL.complete) {
openUrlInExternalBrowser(requireContext(), state.formattedURL.invoke())
return@withState true
}
R.id.action_revoke -> if (state.status == WidgetStatus.WIDGET_ALLOWED) {
revokeWidget()
return@withState true
override fun handleMenuItemSelected(item: MenuItem): Boolean {
return withState(viewModel) { state ->
return@withState when (item.itemId) {
R.id.action_edit -> {
navigator.openIntegrationManager(
requireContext(),
integrationManagerActivityResultLauncher,
state.roomId,
state.widgetId,
state.widgetKind.screenId
)
true
}
R.id.action_delete -> {
deleteWidget()
true
}
R.id.action_refresh -> {
if (state.formattedURL.complete) {
views.widgetWebView.reload()
}
true
}
R.id.action_widget_open_ext -> {
if (state.formattedURL.complete) {
openUrlInExternalBrowser(requireContext(), state.formattedURL.invoke())
}
true
}
R.id.action_revoke -> {
if (state.status == WidgetStatus.WIDGET_ALLOWED) {
revokeWidget()
}
true
}
else -> false
}
}
return@withState super.onOptionsItemSelected(item)
}
override fun onBackPressed(toolbarButton: Boolean): Boolean = withState(viewModel) { state ->