1 /*
2  * Copyright (C) 2015 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.timer;
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.data.DataModel;
29 import com.android.deskclock.data.Timer;
30 import com.android.deskclock.data.TimerListener;
31 
32 import java.util.List;
33 import java.util.Map;
34 
35 /**
36  * This adapter produces a {@link TimerItemFragment} for each timer.
37  */
38 class TimerPagerAdapter extends PagerAdapter implements TimerListener {
39 
40     private final FragmentManager mFragmentManager;
41 
42     /** Maps each timer id to the corresponding {@link TimerItemFragment} that draws it. */
43     private final Map<Integer, TimerItemFragment> mFragments = new ArrayMap<>();
44 
45     /** The current fragment transaction in play or {@code null}. */
46     private FragmentTransaction mCurrentTransaction;
47 
48     /** The {@link TimerItemFragment} that is current visible on screen. */
49     private Fragment mCurrentPrimaryItem;
50 
TimerPagerAdapter(FragmentManager fragmentManager)51     public TimerPagerAdapter(FragmentManager fragmentManager) {
52         mFragmentManager = fragmentManager;
53     }
54 
55     @Override
getCount()56     public int getCount() {
57         return getTimers().size();
58     }
59 
60     @Override
isViewFromObject(View view, Object object)61     public boolean isViewFromObject(View view, Object object) {
62         return ((Fragment) object).getView() == view;
63     }
64 
65     @Override
getItemPosition(Object object)66     public int getItemPosition(Object object) {
67         final TimerItemFragment fragment = (TimerItemFragment) object;
68         final Timer timer = fragment.getTimer();
69 
70         final int position = getTimers().indexOf(timer);
71         return position == -1 ? POSITION_NONE : position;
72     }
73 
74     @Override
instantiateItem(ViewGroup container, int position)75     public Fragment instantiateItem(ViewGroup container, int position) {
76         if (mCurrentTransaction == null) {
77             mCurrentTransaction = mFragmentManager.beginTransaction();
78         }
79 
80         final Timer timer = getTimers().get(position);
81 
82         // Search for the existing fragment by tag.
83         final String tag = getClass().getSimpleName() + timer.getId();
84         TimerItemFragment fragment = (TimerItemFragment) mFragmentManager.findFragmentByTag(tag);
85 
86         if (fragment != null) {
87             // Reattach the existing fragment.
88             mCurrentTransaction.attach(fragment);
89         } else {
90             // Create and add a new fragment.
91             fragment = TimerItemFragment.newInstance(timer);
92             mCurrentTransaction.add(container.getId(), fragment, tag);
93         }
94 
95         if (fragment != mCurrentPrimaryItem) {
96             setItemVisible(fragment, false);
97         }
98 
99         mFragments.put(timer.getId(), fragment);
100 
101         return fragment;
102     }
103 
104     @Override
destroyItem(ViewGroup container, int position, Object object)105     public void destroyItem(ViewGroup container, int position, Object object) {
106         final TimerItemFragment fragment = (TimerItemFragment) object;
107 
108         if (mCurrentTransaction == null) {
109             mCurrentTransaction = mFragmentManager.beginTransaction();
110         }
111 
112         mFragments.remove(fragment.getTimerId());
113         mCurrentTransaction.remove(fragment);
114     }
115 
116     @Override
setPrimaryItem(ViewGroup container, int position, Object object)117     public void setPrimaryItem(ViewGroup container, int position, Object object) {
118         final Fragment fragment = (Fragment) object;
119         if (fragment != mCurrentPrimaryItem) {
120             if (mCurrentPrimaryItem != null) {
121                 setItemVisible(mCurrentPrimaryItem, false);
122             }
123 
124             mCurrentPrimaryItem = fragment;
125 
126             if (mCurrentPrimaryItem != null) {
127                 setItemVisible(mCurrentPrimaryItem, true);
128             }
129         }
130     }
131 
132     @Override
finishUpdate(ViewGroup container)133     public void finishUpdate(ViewGroup container) {
134         if (mCurrentTransaction != null) {
135             mCurrentTransaction.commitAllowingStateLoss();
136             mCurrentTransaction = null;
137             mFragmentManager.executePendingTransactions();
138         }
139     }
140 
141     @Override
timerAdded(Timer timer)142     public void timerAdded(Timer timer) {
143         notifyDataSetChanged();
144     }
145 
146     @Override
timerRemoved(Timer timer)147     public void timerRemoved(Timer timer) {
148         notifyDataSetChanged();
149     }
150 
151     @Override
timerUpdated(Timer before, Timer after)152     public void timerUpdated(Timer before, Timer after) {
153         final TimerItemFragment timerItemFragment = mFragments.get(after.getId());
154         if (timerItemFragment != null) {
155             timerItemFragment.updateTime();
156         }
157     }
158 
159     /**
160      * @return {@code true} if at least one timer is in a state requiring continuous updates
161      */
updateTime()162     boolean updateTime() {
163         boolean continuousUpdates = false;
164         for (TimerItemFragment fragment : mFragments.values()) {
165             continuousUpdates |= fragment.updateTime();
166         }
167         return continuousUpdates;
168     }
169 
getTimer(int index)170     Timer getTimer(int index) {
171         return getTimers().get(index);
172     }
173 
getTimers()174     private List<Timer> getTimers() {
175         return DataModel.getDataModel().getTimers();
176     }
177 
setItemVisible(Fragment item, boolean visible)178     private static void setItemVisible(Fragment item, boolean visible) {
179         FragmentCompat.setMenuVisibility(item, visible);
180         FragmentCompat.setUserVisibleHint(item, visible);
181     }
182 }