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