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
17package com.android.deskclock.timer;
18
19import android.app.Fragment;
20import android.app.FragmentManager;
21import android.app.FragmentTransaction;
22import android.support.v13.app.FragmentCompat;
23import android.support.v4.view.PagerAdapter;
24import android.util.ArrayMap;
25import android.view.View;
26import android.view.ViewGroup;
27
28import com.android.deskclock.data.DataModel;
29import com.android.deskclock.data.Timer;
30import com.android.deskclock.data.TimerListener;
31
32import java.util.List;
33import java.util.Map;
34
35/**
36 * This adapter produces a {@link TimerItemFragment} for each timer.
37 */
38class 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
51    public TimerPagerAdapter(FragmentManager fragmentManager) {
52        mFragmentManager = fragmentManager;
53    }
54
55    @Override
56    public int getCount() {
57        return getTimers().size();
58    }
59
60    @Override
61    public boolean isViewFromObject(View view, Object object) {
62        return ((Fragment) object).getView() == view;
63    }
64
65    @Override
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
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
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
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
133    public void finishUpdate(ViewGroup container) {
134        if (mCurrentTransaction != null) {
135            mCurrentTransaction.commitAllowingStateLoss();
136            mCurrentTransaction = null;
137            mFragmentManager.executePendingTransactions();
138        }
139    }
140
141    @Override
142    public void timerAdded(Timer timer) {
143        notifyDataSetChanged();
144    }
145
146    @Override
147    public void timerRemoved(Timer timer) {
148        notifyDataSetChanged();
149    }
150
151    @Override
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     */
162    boolean updateTime() {
163        boolean continuousUpdates = false;
164        for (TimerItemFragment fragment : mFragments.values()) {
165            continuousUpdates |= fragment.updateTime();
166        }
167        return continuousUpdates;
168    }
169
170    Timer getTimer(int index) {
171        return getTimers().get(index);
172    }
173
174    private List<Timer> getTimers() {
175        return DataModel.getDataModel().getTimers();
176    }
177
178    private static void setItemVisible(Fragment item, boolean visible) {
179        FragmentCompat.setMenuVisibility(item, visible);
180        FragmentCompat.setUserVisibleHint(item, visible);
181    }
182}