From 3013d67c16d8feed2f938e901b1eea50e0ba882a Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 4 Nov 2019 19:33:56 +0100 Subject: [PATCH] Fragment factory: start including the new version with FragmentFactory [WIP] --- vector/build.gradle | 3 + .../im/vector/riotx/core/di/FragmentKey.kt | 28 ++++++++++ .../im/vector/riotx/core/di/FragmentModule.kt | 55 +++++++++++++++++++ .../vector/riotx/core/di/ScreenComponent.kt | 3 + .../riotx/core/di/VectorFragmentFactory.kt | 45 +++++++++++++++ .../vector/riotx/core/extensions/Fragment.kt | 6 +- .../riotx/core/platform/VectorBaseActivity.kt | 3 +- .../riotx/core/platform/VectorBaseFragment.kt | 6 +- .../riotx/features/home/HomeDetailFragment.kt | 16 ++++-- .../room/filtered/FilteredRoomsActivity.kt | 1 - .../home/room/list/RoomListFragment.kt | 21 +++---- vector/src/main/res/layout/activity_home.xml | 4 +- 12 files changed, 164 insertions(+), 27 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/di/FragmentKey.kt create mode 100644 vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt create mode 100644 vector/src/main/java/im/vector/riotx/core/di/VectorFragmentFactory.kt diff --git a/vector/build.gradle b/vector/build.gradle index d639b4c3e8..d0a757d4e0 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -217,6 +217,7 @@ android { dependencies { def epoxy_version = '3.8.0' + def fragment_version = '1.2.0-rc01' def arrow_version = "0.8.2" def coroutines_version = "1.3.2" def markwon_version = '4.1.2' @@ -234,6 +235,8 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" implementation 'androidx.appcompat:appcompat:1.1.0' + implementation "androidx.fragment:fragment:$fragment_version" + implementation "androidx.fragment:fragment-ktx:$fragment_version" //Do not use beta2 at the moment, as it breaks things implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1' implementation 'androidx.core:core-ktx:1.1.0' diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentKey.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentKey.kt new file mode 100644 index 0000000000..c20768ba10 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentKey.kt @@ -0,0 +1,28 @@ +/* + * 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.riotx.core.di + +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModel +import dagger.MapKey +import kotlin.reflect.KClass + +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +@Retention(AnnotationRetention.RUNTIME) +@MapKey +annotation class FragmentKey(val value: KClass) diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt new file mode 100644 index 0000000000..b3bb344d5c --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -0,0 +1,55 @@ +/* + * 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.riotx.core.di + +import androidx.fragment.app.FragmentFactory +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoMap +import im.vector.riotx.core.platform.ConfigurationViewModel +import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreFromKeyViewModel +import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreFromPassphraseViewModel +import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel +import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupSharedViewModel +import im.vector.riotx.features.crypto.verification.SasVerificationViewModel +import im.vector.riotx.features.home.HomeNavigationViewModel +import im.vector.riotx.features.home.createdirect.CreateDirectRoomNavigationViewModel +import im.vector.riotx.features.home.room.list.RoomListFragment +import im.vector.riotx.features.reactions.EmojiChooserViewModel +import im.vector.riotx.features.roomdirectory.RoomDirectoryNavigationViewModel +import im.vector.riotx.features.workers.signout.SignOutViewModel + +@Module +interface FragmentModule { + + /** + * Fragments with @IntoMap will be injected by this factory + */ + @Binds + fun bindFragmentFactory(factory: VectorFragmentFactory): FragmentFactory + + + @Binds + @IntoMap + @FragmentKey(RoomListFragment::class) + fun bindRoomListFragment(viewModel: RoomListFragment): ViewModel + + +} diff --git a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt index 79e496c141..a7c236e033 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt @@ -17,6 +17,7 @@ package im.vector.riotx.core.di import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentFactory import androidx.lifecycle.ViewModelProvider import dagger.BindsInstance import dagger.Component @@ -88,6 +89,8 @@ interface ScreenComponent { fun activeSessionHolder(): ActiveSessionHolder + fun fragmentFactory(): FragmentFactory + fun viewModelFactory(): ViewModelProvider.Factory fun bugReporter(): BugReporter diff --git a/vector/src/main/java/im/vector/riotx/core/di/VectorFragmentFactory.kt b/vector/src/main/java/im/vector/riotx/core/di/VectorFragmentFactory.kt new file mode 100644 index 0000000000..c27509458e --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/di/VectorFragmentFactory.kt @@ -0,0 +1,45 @@ +/* + * 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.riotx.core.di + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentFactory +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Provider + +/** + * FragmentFactory which uses Dagger to create the instances. + */ +class VectorFragmentFactory @Inject constructor( + private val creators: @JvmSuppressWildcards Map, Provider> +) : FragmentFactory() { + + override fun instantiate(classLoader: ClassLoader, className: String): Fragment { + val fragmentClass = loadFragmentClass(classLoader, className) + val creator: Provider? = creators[fragmentClass] + return if (creator == null) { + Timber.v("Unknown model class: $className, fallback to default instance") + super.instantiate(classLoader, className) + } else { + creator.get() + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/Fragment.kt b/vector/src/main/java/im/vector/riotx/core/extensions/Fragment.kt index 65f8fb2aaa..11ebf56ec9 100644 --- a/vector/src/main/java/im/vector/riotx/core/extensions/Fragment.kt +++ b/vector/src/main/java/im/vector/riotx/core/extensions/Fragment.kt @@ -19,15 +19,15 @@ package im.vector.riotx.core.extensions import androidx.fragment.app.Fragment fun Fragment.addFragment(fragment: Fragment, frameId: Int) { - fragmentManager?.inTransaction { add(frameId, fragment) } + parentFragmentManager.inTransaction { add(frameId, fragment) } } fun Fragment.replaceFragment(fragment: Fragment, frameId: Int) { - fragmentManager?.inTransaction { replace(frameId, fragment) } + parentFragmentManager.inTransaction { replace(frameId, fragment) } } fun Fragment.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) { - fragmentManager?.inTransaction { replace(frameId, fragment).addToBackStack(tag) } + parentFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) } } fun Fragment.addChildFragment(fragment: Fragment, frameId: Int) { diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt index 9a4f89d13a..3b80d6d794 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt @@ -26,6 +26,7 @@ import androidx.annotation.* import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.isVisible +import androidx.fragment.app.FragmentFactory import androidx.fragment.app.FragmentManager import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider @@ -125,7 +126,7 @@ abstract class VectorBaseActivity : BaseMvRxActivity(), HasScreenInjector { } Timber.v("Injecting dependencies into ${javaClass.simpleName} took $timeForInjection ms") ThemeUtils.setActivityTheme(this, getOtherThemes()) - + supportFragmentManager.fragmentFactory = screenComponent.fragmentFactory() super.onCreate(savedInstanceState) viewModelFactory = screenComponent.viewModelFactory() configurationViewModel = ViewModelProviders.of(this, viewModelFactory).get(ConfigurationViewModel::class.java) diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt index cc0d8940bc..34953bec33 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseFragment.kt @@ -134,7 +134,11 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { } protected fun setArguments(args: Parcelable? = null) { - arguments = args?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } } + arguments = args.toMvRxBundle() + } + + protected fun Parcelable?.toMvRxBundle(): Bundle? { + return this?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } } } @MainThread diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt index 844fd4f5b2..38fe491bd3 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt @@ -30,6 +30,7 @@ import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent +import im.vector.riotx.core.extensions.inTransaction import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.ui.views.KeysBackupBanner @@ -172,14 +173,17 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate { private fun updateSelectedFragment(displayMode: RoomListFragment.DisplayMode) { val fragmentTag = "FRAGMENT_TAG_${displayMode.name}" - var fragment = childFragmentManager.findFragmentByTag(fragmentTag) + val fragment = childFragmentManager.findFragmentByTag(fragmentTag) if (fragment == null) { - fragment = RoomListFragment.newInstance(RoomListParams(displayMode)) + childFragmentManager.inTransaction { + replace(R.id.roomListContainer, RoomListFragment::class.java, RoomListParams(displayMode).toMvRxBundle(), fragmentTag).addToBackStack(fragmentTag) + } + } else { + childFragmentManager.inTransaction { + replace(R.id.roomListContainer, fragment, fragmentTag).addToBackStack(fragmentTag) + } } - childFragmentManager.beginTransaction() - .replace(R.id.roomListContainer, fragment, fragmentTag) - .addToBackStack(fragmentTag) - .commit() + } /* ========================================================================================== diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/filtered/FilteredRoomsActivity.kt b/vector/src/main/java/im/vector/riotx/features/home/room/filtered/FilteredRoomsActivity.kt index 82fd203b87..b9f8579dd5 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/filtered/FilteredRoomsActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/filtered/FilteredRoomsActivity.kt @@ -44,7 +44,6 @@ class FilteredRoomsActivity : VectorBaseActivity() { super.onCreate(savedInstanceState) configureToolbar(filteredRoomsToolbar) if (isFirstCreation()) { - roomListFragment = RoomListFragment.newInstance(RoomListParams(RoomListFragment.DisplayMode.FILTERED)) replaceFragment(roomListFragment, R.id.filteredRoomsFragmentContainer, FRAGMENT_TAG) } else { roomListFragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as RoomListFragment diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index a705c91a9e..671559b73b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -52,7 +52,13 @@ data class RoomListParams( val sharedData: SharedData? = null ) : Parcelable -class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, FabMenuView.Listener { +class RoomListFragment @Inject constructor( + private val roomController: RoomSummaryController, + val roomListViewModelFactory: RoomListViewModel.Factory, + private val errorFormatter: ErrorFormatter, + private val notificationDrawerManager: NotificationDrawerManager + +) : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, FabMenuView.Listener { enum class DisplayMode(@StringRes val titleRes: Int) { HOME(R.string.bottom_action_home), @@ -62,25 +68,14 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O SHARE(/* Not used */ 0) } - companion object { - fun newInstance(roomListParams: RoomListParams): RoomListFragment { - return RoomListFragment().apply { - setArguments(roomListParams) - } - } - } - private val roomListParams: RoomListParams by args() - @Inject lateinit var roomController: RoomSummaryController - @Inject lateinit var roomListViewModelFactory: RoomListViewModel.Factory - @Inject lateinit var errorFormatter: ErrorFormatter - @Inject lateinit var notificationDrawerManager: NotificationDrawerManager private val roomListViewModel: RoomListViewModel by fragmentViewModel() override fun getLayoutResId() = R.layout.fragment_room_list override fun injectWith(injector: ScreenComponent) { injector.inject(this) + setArguments() } private var hasUnreadRooms = false diff --git a/vector/src/main/res/layout/activity_home.xml b/vector/src/main/res/layout/activity_home.xml index c6f74147d5..0ce124b787 100644 --- a/vector/src/main/res/layout/activity_home.xml +++ b/vector/src/main/res/layout/activity_home.xml @@ -11,7 +11,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - @@ -20,7 +20,7 @@ -