blob: e0189a0cd6849097c9d266c3fed4e526af3ca8cf [file] [log] [blame]
/*
* Copyright (C) 2009 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.deskclock;
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.deskclock.actionbarmenu.ActionBarMenuManager;
import com.android.deskclock.actionbarmenu.MenuItemControllerFactory;
import com.android.deskclock.actionbarmenu.NightModeMenuItemController;
import com.android.deskclock.actionbarmenu.SettingMenuItemController;
import com.android.deskclock.alarms.AlarmStateManager;
import com.android.deskclock.data.DataModel;
import com.android.deskclock.events.Events;
import com.android.deskclock.provider.Alarm;
import com.android.deskclock.stopwatch.StopwatchFragment;
import com.android.deskclock.timer.TimerFragment;
import com.android.deskclock.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 TabLayout.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(TabLayout.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;
}
}