Home | History | Annotate | Download | only in deskclock
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.deskclock;
     18 
     19 import android.app.Fragment;
     20 import android.app.FragmentManager;
     21 import android.app.FragmentTransaction;
     22 import android.support.v13.app.FragmentCompat;
     23 import android.support.v4.view.PagerAdapter;
     24 import android.util.ArrayMap;
     25 import android.view.View;
     26 import android.view.ViewGroup;
     27 
     28 import com.android.deskclock.uidata.UiDataModel;
     29 
     30 import java.util.Map;
     31 
     32 /**
     33  * This adapter produces the DeskClockFragments that are the content of the DeskClock tabs. The
     34  * adapter presents the tabs in LTR and RTL order depending on the text layout direction for the
     35  * current locale. To prevent issues when switching between LTR and RTL, fragments are registered
     36  * with the manager using position-independent tags, which is an important departure from
     37  * FragmentPagerAdapter.
     38  */
     39 final class FragmentTabPagerAdapter extends PagerAdapter {
     40 
     41     private final DeskClock mDeskClock;
     42 
     43     /** The manager into which fragments are added. */
     44     private final FragmentManager mFragmentManager;
     45 
     46     /** A fragment cache that can be accessed before {@link #instantiateItem} is called. */
     47     private final Map<UiDataModel.Tab, DeskClockFragment> mFragmentCache;
     48 
     49     /** The active fragment transaction if one exists. */
     50     private FragmentTransaction mCurrentTransaction;
     51 
     52     /** The current fragment displayed to the user. */
     53     private Fragment mCurrentPrimaryItem;
     54 
     55     FragmentTabPagerAdapter(DeskClock deskClock) {
     56         mDeskClock = deskClock;
     57         mFragmentCache = new ArrayMap<>(getCount());
     58         mFragmentManager = deskClock.getFragmentManager();
     59     }
     60 
     61     @Override
     62     public int getCount() {
     63         return UiDataModel.getUiDataModel().getTabCount();
     64     }
     65 
     66     /**
     67      * @param position the left-to-right index of the fragment to be returned
     68      * @return the fragment displayed at the given {@code position}
     69      */
     70     DeskClockFragment getDeskClockFragment(int position) {
     71         // Fetch the tab the UiDataModel reports for the position.
     72         final UiDataModel.Tab tab = UiDataModel.getUiDataModel().getTabAt(position);
     73 
     74         // First check the local cache for the fragment.
     75         DeskClockFragment fragment = mFragmentCache.get(tab);
     76         if (fragment != null) {
     77             return fragment;
     78         }
     79 
     80         // Next check the fragment manager; relevant when app is rebuilt after locale changes
     81         // because this adapter will be new and mFragmentCache will be empty, but the fragment
     82         // manager will retain the Fragments built on original application launch.
     83         fragment = (DeskClockFragment) mFragmentManager.findFragmentByTag(tab.name());
     84         if (fragment != null) {
     85             fragment.setFabContainer(mDeskClock);
     86             mFragmentCache.put(tab, fragment);
     87             return fragment;
     88         }
     89 
     90         // Otherwise, build the fragment from scratch.
     91         final String fragmentClassName = tab.getFragmentClassName();
     92         fragment = (DeskClockFragment) Fragment.instantiate(mDeskClock, fragmentClassName);
     93         fragment.setFabContainer(mDeskClock);
     94         mFragmentCache.put(tab, fragment);
     95         return fragment;
     96     }
     97 
     98     @Override
     99     public void startUpdate(ViewGroup container) {
    100         if (container.getId() == View.NO_ID) {
    101             throw new IllegalStateException("ViewPager with adapter " + this + " has no id");
    102         }
    103     }
    104 
    105     @Override
    106     public Object instantiateItem(ViewGroup container, int position) {
    107         if (mCurrentTransaction == null) {
    108             mCurrentTransaction = mFragmentManager.beginTransaction();
    109         }
    110 
    111         // Use the fragment located in the fragment manager if one exists.
    112         final UiDataModel.Tab tab = UiDataModel.getUiDataModel().getTabAt(position);
    113         Fragment fragment = mFragmentManager.findFragmentByTag(tab.name());
    114         if (fragment != null) {
    115             mCurrentTransaction.attach(fragment);
    116         } else {
    117             fragment = getDeskClockFragment(position);
    118             mCurrentTransaction.add(container.getId(), fragment, tab.name());
    119         }
    120 
    121         if (fragment != mCurrentPrimaryItem) {
    122             FragmentCompat.setMenuVisibility(fragment, false);
    123             FragmentCompat.setUserVisibleHint(fragment, false);
    124         }
    125 
    126         return fragment;
    127     }
    128 
    129     @Override
    130     public void destroyItem(ViewGroup container, int position, Object object) {
    131         if (mCurrentTransaction == null) {
    132             mCurrentTransaction = mFragmentManager.beginTransaction();
    133         }
    134         final DeskClockFragment fragment = (DeskClockFragment) object;
    135         fragment.setFabContainer(null);
    136         mCurrentTransaction.detach(fragment);
    137     }
    138 
    139     @Override
    140     public void setPrimaryItem(ViewGroup container, int position, Object object) {
    141         final Fragment fragment = (Fragment) object;
    142         if (fragment != mCurrentPrimaryItem) {
    143             if (mCurrentPrimaryItem != null) {
    144                 FragmentCompat.setMenuVisibility(mCurrentPrimaryItem, false);
    145                 FragmentCompat.setUserVisibleHint(mCurrentPrimaryItem, false);
    146             }
    147             if (fragment != null) {
    148                 FragmentCompat.setMenuVisibility(fragment, true);
    149                 FragmentCompat.setUserVisibleHint(fragment, true);
    150             }
    151             mCurrentPrimaryItem = fragment;
    152         }
    153     }
    154 
    155     @Override
    156     public void finishUpdate(ViewGroup container) {
    157         if (mCurrentTransaction != null) {
    158             mCurrentTransaction.commitAllowingStateLoss();
    159             mCurrentTransaction = null;
    160             mFragmentManager.executePendingTransactions();
    161         }
    162     }
    163 
    164     @Override
    165     public boolean isViewFromObject(View view, Object object) {
    166         return ((Fragment) object).getView() == view;
    167     }
    168 }