Java tutorial
/* * 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; } }