/* * 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 = 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 }