16d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux/*
26d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Copyright (C) 2015 The Android Open Source Project
36d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux *
46d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Licensed under the Apache License, Version 2.0 (the "License");
56d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * you may not use this file except in compliance with the License.
66d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * You may obtain a copy of the License at
76d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux *
86d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux *      http://www.apache.org/licenses/LICENSE-2.0
96d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux *
106d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * Unless required by applicable law or agreed to in writing, software
116d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * distributed under the License is distributed on an "AS IS" BASIS,
126d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * See the License for the specific language governing permissions and
146d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * limitations under the License.
156d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */
166d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
176d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuxpackage com.android.deskclock.timer;
186d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
196d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.app.Fragment;
206d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.app.FragmentManager;
216d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.app.FragmentTransaction;
226d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.support.v13.app.FragmentCompat;
236d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.support.v4.view.PagerAdapter;
246d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.util.ArrayMap;
256d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.view.View;
266d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport android.view.ViewGroup;
276d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
286d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport com.android.deskclock.data.DataModel;
296d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport com.android.deskclock.data.Timer;
306d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport com.android.deskclock.data.TimerListener;
316d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
326d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport java.util.List;
336d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuximport java.util.Map;
346d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
356d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux/**
366d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux * This adapter produces a {@link TimerItemFragment} for each timer.
376d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux */
386d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieuxclass TimerPagerAdapter extends PagerAdapter implements TimerListener {
396d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
406d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    private final FragmentManager mFragmentManager;
416d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
426d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /** Maps each timer id to the corresponding {@link TimerItemFragment} that draws it. */
436d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    private final Map<Integer, TimerItemFragment> mFragments = new ArrayMap<>();
446d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
456d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /** The current fragment transaction in play or {@code null}. */
466d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    private FragmentTransaction mCurrentTransaction;
476d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
486d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /** The {@link TimerItemFragment} that is current visible on screen. */
496d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    private Fragment mCurrentPrimaryItem;
506d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
516d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public TimerPagerAdapter(FragmentManager fragmentManager) {
526d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mFragmentManager = fragmentManager;
536d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
546d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
556d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    @Override
566d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public int getCount() {
576d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return getTimers().size();
586d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
596d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
606d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    @Override
616d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public boolean isViewFromObject(View view, Object object) {
626d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return ((Fragment) object).getView() == view;
636d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
646d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
656d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    @Override
666d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public int getItemPosition(Object object) {
676d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        final TimerItemFragment fragment = (TimerItemFragment) object;
686d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        final Timer timer = fragment.getTimer();
696d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
706d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        final int position = getTimers().indexOf(timer);
716d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return position == -1 ? POSITION_NONE : position;
726d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
736d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
746d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    @Override
756d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public Fragment instantiateItem(ViewGroup container, int position) {
766d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        if (mCurrentTransaction == null) {
776d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            mCurrentTransaction = mFragmentManager.beginTransaction();
786d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        }
796d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
806d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        final Timer timer = getTimers().get(position);
81f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux
82f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux        // Search for the existing fragment by tag.
83f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux        final String tag = getClass().getSimpleName() + timer.getId();
84f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux        TimerItemFragment fragment = (TimerItemFragment) mFragmentManager.findFragmentByTag(tag);
85f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux
86f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux        if (fragment != null) {
87f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux            // Reattach the existing fragment.
88f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux            mCurrentTransaction.attach(fragment);
89f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux        } else {
90f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux            // Create and add a new fragment.
91f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux            fragment = TimerItemFragment.newInstance(timer);
92f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux            mCurrentTransaction.add(container.getId(), fragment, tag);
93f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux        }
94f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux
95f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux        if (fragment != mCurrentPrimaryItem) {
96f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux            setItemVisible(fragment, false);
97f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux        }
986d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
996d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mFragments.put(timer.getId(), fragment);
1006d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1016d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return fragment;
1026d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
1036d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1046d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    @Override
1056d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void destroyItem(ViewGroup container, int position, Object object) {
1066d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        final TimerItemFragment fragment = (TimerItemFragment) object;
1076d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1086d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        if (mCurrentTransaction == null) {
1096d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            mCurrentTransaction = mFragmentManager.beginTransaction();
1106d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        }
1116d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1126d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mFragments.remove(fragment.getTimerId());
1136d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        mCurrentTransaction.remove(fragment);
1146d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
1156d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1166d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    @Override
1176d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void setPrimaryItem(ViewGroup container, int position, Object object) {
1186d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        final Fragment fragment = (Fragment) object;
1196d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        if (fragment != mCurrentPrimaryItem) {
1206d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            if (mCurrentPrimaryItem != null) {
1216d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux                setItemVisible(mCurrentPrimaryItem, false);
1226d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            }
1236d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1246d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            mCurrentPrimaryItem = fragment;
1256d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1266d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            if (mCurrentPrimaryItem != null) {
1276d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux                setItemVisible(mCurrentPrimaryItem, true);
1286d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            }
1296d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        }
1306d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
1316d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1326d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    @Override
1336d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void finishUpdate(ViewGroup container) {
1346d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        if (mCurrentTransaction != null) {
1356d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            mCurrentTransaction.commitAllowingStateLoss();
1366d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            mCurrentTransaction = null;
1376d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            mFragmentManager.executePendingTransactions();
1386d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        }
1396d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
1406d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1416d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    @Override
1426d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void timerAdded(Timer timer) {
1436d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        notifyDataSetChanged();
1446d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
1456d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1466d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    @Override
147f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux    public void timerRemoved(Timer timer) {
148f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux        notifyDataSetChanged();
149f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux    }
150f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux
151f4d06f8040dadcf9c8c8232c33f50756bde0a2e5James Lemieux    @Override
1526d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    public void timerUpdated(Timer before, Timer after) {
1536d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        final TimerItemFragment timerItemFragment = mFragments.get(after.getId());
1546d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        if (timerItemFragment != null) {
1556d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            timerItemFragment.updateTime();
1566d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        }
1576d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
1586d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1596d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    /**
1606d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     * @return {@code true} if at least one timer is in a state requiring continuous updates
1616d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux     */
1626d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    boolean updateTime() {
1636d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        boolean continuousUpdates = false;
1646d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        for (TimerItemFragment fragment : mFragments.values()) {
1656d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux            continuousUpdates |= fragment.updateTime();
1666d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        }
1676d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return continuousUpdates;
1686d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
1696d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1706d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    Timer getTimer(int index) {
1716d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return getTimers().get(index);
1726d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
1736d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1746d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    private List<Timer> getTimers() {
1756d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        return DataModel.getDataModel().getTimers();
1766d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
1776d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux
1786d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    private static void setItemVisible(Fragment item, boolean visible) {
1796d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        FragmentCompat.setMenuVisibility(item, visible);
1806d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux        FragmentCompat.setUserVisibleHint(item, visible);
1816d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux    }
1826d603b7c62bb38d763a681a8bf20fadb1442e833James Lemieux}