blob: 1c920f51ca67caa75386666cc26fbe26a2bf076b [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* 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 com.android.deskclock
import android.util.ArrayMap
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.viewpager.widget.PagerAdapter
import com.android.deskclock.uidata.UiDataModel
/**
* This adapter produces the DeskClockFragments that are the content of the DeskClock tabs. The
* adapter presents the tabs in LTR and RTL order depending on the text layout direction for the
* current locale. To prevent issues when switching between LTR and RTL, fragments are registered
* with the manager using position-independent tags, which is an important departure from
* FragmentPagerAdapter.
*/
internal class FragmentTabPagerAdapter(private val mDeskClock: DeskClock) : PagerAdapter() {
/** The manager into which fragments are added. */
private val mFragmentManager: FragmentManager = mDeskClock.supportFragmentManager
/** A fragment cache that can be accessed before [.instantiateItem] is called. */
private val mFragmentCache: MutableMap<UiDataModel.Tab, DeskClockFragment?> =
ArrayMap(getCount())
/** The active fragment transaction if one exists. */
private var mCurrentTransaction: FragmentTransaction? = null
/** The current fragment displayed to the user. */
private var mCurrentPrimaryItem: Fragment? = null
override fun getCount(): Int = UiDataModel.uiDataModel.tabCount
/**
* @param position the left-to-right index of the fragment to be returned
* @return the fragment displayed at the given `position`
*/
fun getDeskClockFragment(position: Int): DeskClockFragment {
// Fetch the tab the UiDataModel reports for the position.
val tab: UiDataModel.Tab = UiDataModel.uiDataModel.getTabAt(position)
// First check the local cache for the fragment.
var fragment = mFragmentCache[tab]
if (fragment != null) {
return fragment
}
// Next check the fragment manager; relevant when app is rebuilt after locale changes
// because this adapter will be new and mFragmentCache will be empty, but the fragment
// manager will retain the Fragments built on original application launch.
fragment = mFragmentManager.findFragmentByTag(tab.name) as DeskClockFragment?
if (fragment != null) {
fragment.setFabContainer(mDeskClock)
mFragmentCache[tab] = fragment
return fragment
}
// Otherwise, build the fragment from scratch.
val fragmentClassName: String = tab.fragmentClassName
fragment = Fragment.instantiate(mDeskClock, fragmentClassName) as DeskClockFragment
fragment.setFabContainer(mDeskClock)
mFragmentCache[tab] = fragment
return fragment
}
override fun startUpdate(container: ViewGroup) {
check(container.id != View.NO_ID) { "ViewPager with adapter $this has no id" }
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
if (mCurrentTransaction == null) {
mCurrentTransaction = mFragmentManager.beginTransaction()
}
// Use the fragment located in the fragment manager if one exists.
val tab: UiDataModel.Tab = UiDataModel.uiDataModel.getTabAt(position)
var fragment = mFragmentManager.findFragmentByTag(tab.name)
if (fragment != null) {
mCurrentTransaction!!.attach(fragment)
} else {
fragment = getDeskClockFragment(position)
mCurrentTransaction!!.add(container.id, fragment, tab.name)
}
if (fragment !== mCurrentPrimaryItem) {
fragment.setMenuVisibility(false)
fragment.setUserVisibleHint(false)
}
return fragment
}
override fun destroyItem(container: ViewGroup, position: Int, any: Any) {
if (mCurrentTransaction == null) {
mCurrentTransaction = mFragmentManager.beginTransaction()
}
val fragment = any as DeskClockFragment
fragment.setFabContainer(null)
mCurrentTransaction!!.detach(fragment)
}
override fun setPrimaryItem(container: ViewGroup, position: Int, any: Any) {
val fragment = any as Fragment
if (fragment !== mCurrentPrimaryItem) {
mCurrentPrimaryItem?.let {
it.setMenuVisibility(false)
it.setUserVisibleHint(false)
}
fragment.setMenuVisibility(true)
fragment.setUserVisibleHint(true)
mCurrentPrimaryItem = fragment
}
}
override fun finishUpdate(container: ViewGroup) {
if (mCurrentTransaction != null) {
mCurrentTransaction!!.commitAllowingStateLoss()
mCurrentTransaction = null
mFragmentManager.executePendingTransactions()
}
}
override fun isViewFromObject(view: View, any: Any): Boolean = (any as Fragment).view === view
}