com.android.deskclock2.DeskClock.java Source code

Java tutorial

Introduction

Here is the source code for com.android.deskclock2.DeskClock.java

Source

/*
 *  Copyright (C) 2015 The Android Open Source Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.android.deskclock2;

import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
import android.support.design.widget.TabLayout;
import android.support.design.widget.TabLayout.Tab;
import android.support.design.widget.TabLayout.ViewPagerOnTabSelectedListener;
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.ArraySet;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;

import com.android.deskclock2.actionbarmenu.ActionBarMenuManager;
import com.android.deskclock2.actionbarmenu.MenuItemControllerFactory;
import com.android.deskclock2.actionbarmenu.NightModeMenuItemController;
import com.android.deskclock2.actionbarmenu.SettingMenuItemController;
import com.android.deskclock2.alarms.AlarmStateManager;
import com.android.deskclock2.data.DataModel;
import com.android.deskclock2.events.Events;
import com.android.deskclock2.provider.Alarm;
import com.android.deskclock2.stopwatch.StopwatchFragment;
import com.android.deskclock2.timer.TimerFragment;
import com.android.deskclock2.widget.RtlViewPager;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * DeskClock clock view for desk docks.
 */
public class DeskClock extends BaseActivity implements LabelDialogFragment.AlarmLabelDialogHandler {

    private static final String TAG = "DeskClock";

    // Alarm action for midnight (so we can update the date display).
    private static final String KEY_SELECTED_TAB = "selected_tab";
    public static final String SELECT_TAB_INTENT_EXTRA = "deskclock.select.tab";

    public static final int ALARM_TAB_INDEX = 0;
    public static final int CLOCK_TAB_INDEX = 1;
    public static final int TIMER_TAB_INDEX = 2;
    public static final int STOPWATCH_TAB_INDEX = 3;

    private final ActionBarMenuManager mActionBarMenuManager = new ActionBarMenuManager(this);

    private TabLayout mTabLayout;
    private RtlViewPager mViewPager;
    private ImageView mFab;
    private ImageButton mLeftButton;
    private ImageButton mRightButton;

    private TabsAdapter mTabsAdapter;
    private int mSelectedTab;

    /** {@code true} when a settings change necessitates recreating this activity. */
    private boolean mRecreateActivity;

    @Override
    public void onNewIntent(Intent newIntent) {
        super.onNewIntent(newIntent);
        LogUtils.d(TAG, "onNewIntent with intent: %s", newIntent);

        // update our intent so that we can consult it to determine whether or
        // not the most recent launch was via a dock event
        setIntent(newIntent);

        // Honor the tab requested by the intent, if any.
        int tab = newIntent.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1);
        if (tab != -1 && mTabLayout != null) {
            mTabLayout.getTabAt(tab).select();
            mViewPager.setCurrentItem(tab);
        }
    }

    @VisibleForTesting
    DeskClockFragment getSelectedFragment() {
        return (DeskClockFragment) mTabsAdapter.getItem(mSelectedTab);
    }

    private void createTabs() {
        final Tab alarmTab = mTabLayout.newTab();
        alarmTab.setIcon(R.drawable.ic_tab_alarm).setContentDescription(R.string.menu_alarm);
        mTabsAdapter.addTab(alarmTab, AlarmClockFragment.class, ALARM_TAB_INDEX);

        final Tab clockTab = mTabLayout.newTab();
        clockTab.setIcon(R.drawable.ic_tab_clock).setContentDescription(R.string.menu_clock);
        mTabsAdapter.addTab(clockTab, ClockFragment.class, CLOCK_TAB_INDEX);

        final Tab timerTab = mTabLayout.newTab();
        timerTab.setIcon(R.drawable.ic_tab_timer).setContentDescription(R.string.menu_timer);
        mTabsAdapter.addTab(timerTab, TimerFragment.class, TIMER_TAB_INDEX);

        final Tab stopwatchTab = mTabLayout.newTab();
        stopwatchTab.setIcon(R.drawable.ic_tab_stopwatch).setContentDescription(R.string.menu_stopwatch);
        mTabsAdapter.addTab(stopwatchTab, StopwatchFragment.class, STOPWATCH_TAB_INDEX);

        mTabLayout.getTabAt(mSelectedTab).select();
        mViewPager.setCurrentItem(mSelectedTab);
        mTabsAdapter.notifySelectedPage(mSelectedTab);
    }

    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setVolumeControlStream(AudioManager.STREAM_ALARM);

        if (icicle != null) {
            mSelectedTab = icicle.getInt(KEY_SELECTED_TAB, CLOCK_TAB_INDEX);
        } else {
            mSelectedTab = CLOCK_TAB_INDEX;

            // Set the background color to initially match the theme value so that we can
            // smoothly transition to the dynamic color.
            setBackgroundColor(getResources().getColor(R.color.default_background), false /* animate */);
        }

        // Honor the tab requested by the intent, if any.
        final Intent intent = getIntent();
        if (intent != null) {
            int tab = intent.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1);
            if (tab != -1) {
                mSelectedTab = tab;
            }
        }

        setContentView(R.layout.desk_clock);
        final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        mTabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
        mFab = (ImageView) findViewById(R.id.fab);
        mLeftButton = (ImageButton) findViewById(R.id.left_button);
        mRightButton = (ImageButton) findViewById(R.id.right_button);
        if (mTabsAdapter == null) {
            mViewPager = (RtlViewPager) findViewById(R.id.desk_clock_pager);
            // Keep all four tabs to minimize jank.
            mViewPager.setOffscreenPageLimit(3);
            // Set Accessibility Delegate to null so ViewPager doesn't intercept movements and
            // prevent the fab from being selected.
            mViewPager.setAccessibilityDelegate(null);
            mTabsAdapter = new TabsAdapter(this, mViewPager);
            createTabs();
            mTabLayout.setOnTabSelectedListener(new ViewPagerOnTabSelectedListener(mViewPager));
        }

        mFab.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                getSelectedFragment().onFabClick(view);
            }
        });
        mLeftButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                getSelectedFragment().onLeftButtonClick(view);
            }
        });
        mRightButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                getSelectedFragment().onRightButtonClick(view);
            }
        });

        // Configure the menu item controllers.
        mActionBarMenuManager.addMenuItemController(new SettingMenuItemController(this))
                .addMenuItemController(new NightModeMenuItemController(this))
                .addMenuItemController(MenuItemControllerFactory.getInstance().buildMenuItemControllers(this));

        // Inflate the menu during creation to avoid a double layout pass. Otherwise, the menu
        // inflation occurs *after* the initial draw and a second layout pass adds in the menu.
        onCreateOptionsMenu(toolbar.getMenu());

        // We need to update the system next alarm time on app startup because the
        // user might have clear our data.
        AlarmStateManager.updateNextAlarm(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        DataModel.getDataModel().setApplicationInForeground(true);
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();

        if (mRecreateActivity) {
            mRecreateActivity = false;

            // A runnable must be posted here or the new DeskClock activity will be recreated in a
            // paused state, even though it is the foreground activity.
            mViewPager.post(new Runnable() {
                @Override
                public void run() {
                    recreate();
                }
            });
        }
    }

    @Override
    public void onPause() {
        DataModel.getDataModel().setApplicationInForeground(false);
        super.onPause();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(KEY_SELECTED_TAB, mTabLayout.getSelectedTabPosition());
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        mActionBarMenuManager.createOptionsMenu(menu, getMenuInflater());
        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);
        mActionBarMenuManager.prepareShowMenu(menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mActionBarMenuManager.handleMenuItemClick(item)) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // Recreate the activity if any settings have been changed
        if (requestCode == SettingMenuItemController.REQUEST_CHANGE_SETTINGS && resultCode == RESULT_OK) {
            mRecreateActivity = true;
        }
    }

    public void registerPageChangedListener(DeskClockFragment frag) {
        if (mTabsAdapter != null) {
            mTabsAdapter.registerPageChangedListener(frag);
        }
    }

    public void unregisterPageChangedListener(DeskClockFragment frag) {
        if (mTabsAdapter != null) {
            mTabsAdapter.unregisterPageChangedListener(frag);
        }
    }

    /**
     * Adapter for wrapping together the ActionBar's tab with the ViewPager
     */
    private class TabsAdapter extends FragmentPagerAdapter implements OnPageChangeListener {

        private static final String KEY_TAB_POSITION = "tab_position";

        final class TabInfo {
            private final Class<?> clss;
            private final Bundle args;

            TabInfo(Class<?> _class, int position) {
                clss = _class;
                args = new Bundle();
                args.putInt(KEY_TAB_POSITION, position);
            }

            public int getPosition() {
                return args.getInt(KEY_TAB_POSITION, 0);
            }
        }

        private final List<TabInfo> mTabs = new ArrayList<>(4 /* number of fragments */);
        private final Context mContext;
        private final RtlViewPager mPager;
        // Used for doing callbacks to fragments.
        private final Set<String> mFragmentTags = new ArraySet<>(4 /* number of fragments */);

        public TabsAdapter(AppCompatActivity activity, RtlViewPager pager) {
            super(activity.getFragmentManager());
            mContext = activity;
            mPager = pager;
            mPager.setAdapter(this);
            mPager.setOnRTLPageChangeListener(this);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            return super.instantiateItem(container, mViewPager.getRtlAwareIndex(position));
        }

        @Override
        public Fragment getItem(int position) {
            // Because this public method is called outside many times,
            // check if it exits first before creating a new one.
            final String name = makeFragmentName(R.id.desk_clock_pager, position);
            Fragment fragment = getFragmentManager().findFragmentByTag(name);
            if (fragment == null) {
                TabInfo info = mTabs.get(position);
                fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
                if (fragment instanceof TimerFragment) {
                    ((TimerFragment) fragment).setFabAppearance();
                    ((TimerFragment) fragment).setLeftRightButtonAppearance();
                }
            }
            return fragment;
        }

        /**
         * Copied from:
         * android/frameworks/support/v13/java/android/support/v13/app/FragmentPagerAdapter.java#94
         * Create unique name for the fragment so fragment manager knows it exist.
         */
        private String makeFragmentName(int viewId, int index) {
            return "android:switcher:" + viewId + ":" + index;
        }

        @Override
        public int getCount() {
            return mTabs.size();
        }

        public void addTab(Tab tab, Class<?> clss, int position) {
            TabInfo info = new TabInfo(clss, position);
            mTabs.add(info);
            mTabLayout.addTab(tab);
            notifyDataSetChanged();
        }

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            // Do nothing
        }

        @Override
        public void onPageSelected(int position) {
            // Set the page before doing the menu so that onCreateOptionsMenu knows what page it is.
            mTabLayout.getTabAt(position).select();
            notifyPageChanged(position);

            mSelectedTab = position;

            // Avoid sending events for the initial tab selection on launch and the reselecting a
            // tab after a configuration change.
            if (DataModel.getDataModel().isApplicationInForeground()) {
                switch (mSelectedTab) {
                case ALARM_TAB_INDEX:
                    Events.sendAlarmEvent(R.string.action_show, R.string.label_deskclock);
                    break;
                case CLOCK_TAB_INDEX:
                    Events.sendClockEvent(R.string.action_show, R.string.label_deskclock);
                    break;
                case TIMER_TAB_INDEX:
                    Events.sendTimerEvent(R.string.action_show, R.string.label_deskclock);
                    break;
                case STOPWATCH_TAB_INDEX:
                    Events.sendStopwatchEvent(R.string.action_show, R.string.label_deskclock);
                    break;
                }
            }

            final DeskClockFragment f = (DeskClockFragment) getItem(position);
            if (f != null) {
                f.setFabAppearance();
                f.setLeftRightButtonAppearance();
            }
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            // Do nothing
        }

        public void notifySelectedPage(int page) {
            notifyPageChanged(page);
        }

        private void notifyPageChanged(int newPage) {
            for (String tag : mFragmentTags) {
                final FragmentManager fm = getFragmentManager();
                DeskClockFragment f = (DeskClockFragment) fm.findFragmentByTag(tag);
                if (f != null) {
                    f.onPageChanged(newPage);
                }
            }
        }

        public void registerPageChangedListener(DeskClockFragment frag) {
            String tag = frag.getTag();
            if (mFragmentTags.contains(tag)) {
                LogUtils.wtf(TAG, "Trying to add an existing fragment " + tag);
            } else {
                mFragmentTags.add(frag.getTag());
            }
            // Since registering a listener by the fragment is done sometimes after the page
            // was already changed, make sure the fragment gets the current page
            frag.onPageChanged(mTabLayout.getSelectedTabPosition());
        }

        public void unregisterPageChangedListener(DeskClockFragment frag) {
            mFragmentTags.remove(frag.getTag());
        }
    }

    /**
     * Called by the LabelDialogFormat class after the dialog is finished.
     */
    @Override
    public void onDialogLabelSet(Alarm alarm, String label, String tag) {
        Fragment frag = getFragmentManager().findFragmentByTag(tag);
        if (frag instanceof AlarmClockFragment) {
            ((AlarmClockFragment) frag).setLabel(alarm, label);
        }
    }

    public int getSelectedTab() {
        return mSelectedTab;
    }

    public ImageView getFab() {
        return mFab;
    }

    public ImageButton getLeftButton() {
        return mLeftButton;
    }

    public ImageButton getRightButton() {
        return mRightButton;
    }
}