DeskClock.java revision 421751e187dfa61bc1e48b52cdaf366628b06dd4
1/*
2 * Copyright (C) 2009 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;
18
19import android.app.ActionBar;
20import android.app.ActionBar.Tab;
21import android.app.Activity;
22import android.app.Fragment;
23import android.app.FragmentTransaction;
24import android.content.ActivityNotFoundException;
25import android.content.Context;
26import android.content.Intent;
27import android.content.SharedPreferences;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.Message;
31import android.preference.PreferenceManager;
32import android.support.v13.app.FragmentPagerAdapter;
33import android.support.v4.view.ViewPager;
34import android.util.Log;
35import android.view.Menu;
36import android.view.MenuItem;
37import android.view.View;
38import android.view.animation.AnimationUtils;
39import android.widget.PopupMenu;
40import android.widget.Toast;
41
42import com.android.deskclock.stopwatch.StopwatchFragment;
43import com.android.deskclock.stopwatch.StopwatchService;
44import com.android.deskclock.stopwatch.Stopwatches;
45import com.android.deskclock.timer.TimerFragment;
46import com.android.deskclock.timer.Timers;
47import com.android.deskclock.worldclock.CitiesActivity;
48
49import java.util.ArrayList;
50import java.util.TimeZone;
51
52/**
53 * DeskClock clock view for desk docks.
54 */
55public class DeskClock extends Activity {
56    private static final boolean DEBUG = false;
57
58    private static final String LOG_TAG = "DeskClock";
59
60    // Alarm action for midnight (so we can update the date display).
61    private static final String KEY_SELECTED_TAB = "selected_tab";
62    private static final String KEY_CLOCK_STATE = "clock_state";
63
64    public static final String SELECT_TAB_INTENT_EXTRA = "deskclock.select.tab";
65
66    private ActionBar mActionBar;
67    private Tab mTimerTab;
68    private Tab mClockTab;
69    private Tab mStopwatchTab;
70
71    private ViewPager mViewPager;
72    private TabsAdapter mTabsAdapter;
73
74    public static final int TIMER_TAB_INDEX = 0;
75    public static final int CLOCK_TAB_INDEX = 1;
76    public static final int STOPWATCH_TAB_INDEX = 2;
77
78    private int mSelectedTab;
79    private final boolean mDimmed = false;
80
81    private int mClockState = CLOCK_NORMAL;
82    private static final int CLOCK_NORMAL = 0;
83    private static final int CLOCK_LIGHTS_OUT = 1;
84    private static final int CLOCK_DIMMED = 2;
85
86
87
88    // Delay before hiding the action bar and buttons
89    private static final long LIGHTSOUT_TIMEOUT = 10 * 1000; // 10 seconds
90    // Delay before dimming the screen
91    private static final long DIM_TIMEOUT = 10 * 1000; // 10 seconds
92
93    // Opacity of black layer between clock display and wallpaper.
94    private final float DIM_BEHIND_AMOUNT_NORMAL = 0.4f;
95    private final float DIM_BEHIND_AMOUNT_DIMMED = 0.8f; // higher contrast when display dimmed
96
97    private final int SCREEN_SAVER_TIMEOUT_MSG   = 0x2000;
98    private final int SCREEN_SAVER_MOVE_MSG      = 0x2001;
99    private final int DIM_TIMEOUT_MSG            = 0x2002;
100    private final int LIGHTSOUT_TIMEOUT_MSG      = 0x2003;
101    private final int BACK_TO_NORMAL_MSG         = 0x2004;
102
103
104
105    private final Handler mHandy = new Handler() {
106        @Override
107        public void handleMessage(Message m) {
108     /*       if (m.what == LIGHTSOUT_TIMEOUT_MSG) {
109                doLightsOut(true);
110                mClockState = CLOCK_LIGHTS_OUT;
111                // Only dim if clock fragment is visible
112                if (mViewPager.getCurrentItem() ==  CLOCK_TAB_INDEX) {
113                    scheduleDim();
114                }
115            }  else if (m.what == DIM_TIMEOUT_MSG) {
116                mClockState = CLOCK_DIMMED;
117                doDim(true);
118            } else if (m.what == BACK_TO_NORMAL_MSG){
119                // ignore user interaction and do not go back to normal if a button was clicked
120                DeskClockFragment f =
121                        (DeskClockFragment) mTabsAdapter.getFragment(mViewPager.getCurrentItem());
122                if (f != null && f.isButtonClicked()) {
123                    return;
124                }
125
126                int oldState = mClockState;
127                mClockState = CLOCK_NORMAL;
128
129                switch (oldState) {
130                    case CLOCK_LIGHTS_OUT:
131                        doLightsOut(false);
132                        break;
133                    case CLOCK_DIMMED:
134                        doLightsOut(false);
135                        doDim(true);
136                        break;
137                }
138            }*/
139        }
140    };
141
142    @Override
143    public void onNewIntent(Intent newIntent) {
144        super.onNewIntent(newIntent);
145        if (DEBUG) Log.d(LOG_TAG, "onNewIntent with intent: " + newIntent);
146
147        // update our intent so that we can consult it to determine whether or
148        // not the most recent launch was via a dock event
149        setIntent(newIntent);
150
151        // Timer receiver may ask to go to the timers fragment if a timer expired.
152        int tab = newIntent.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1);
153        if (tab != -1) {
154            if (mActionBar != null) {
155                mActionBar.setSelectedNavigationItem(tab);
156            }
157        }
158    }
159
160    private void initViews() {
161
162        if (mTabsAdapter == null) {
163            mViewPager = new ViewPager(this);
164            mViewPager.setId(R.id.desk_clock_pager);
165            mTabsAdapter = new TabsAdapter(this, mViewPager);
166            createTabs(mSelectedTab);
167        }
168        setContentView(mViewPager);
169        mActionBar.setSelectedNavigationItem(mSelectedTab);
170    }
171
172    private void createTabs(int selectedIndex) {
173        mActionBar = getActionBar();
174
175        mActionBar.setDisplayOptions(0);
176        if (mActionBar != null) {
177            mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
178            mTimerTab = mActionBar.newTab();
179            mTimerTab.setIcon(R.drawable.timer_tab);
180            mTimerTab.setContentDescription(R.string.menu_timer);
181            mTabsAdapter.addTab(mTimerTab, TimerFragment.class,TIMER_TAB_INDEX);
182
183            mClockTab = mActionBar.newTab();
184            mClockTab.setIcon(R.drawable.clock_tab);
185            mClockTab.setContentDescription(R.string.menu_clock);
186            mTabsAdapter.addTab(mClockTab, ClockFragment.class,CLOCK_TAB_INDEX);
187            mStopwatchTab = mActionBar.newTab();
188            mStopwatchTab.setIcon(R.drawable.stopwatch_tab);
189            mStopwatchTab.setContentDescription(R.string.menu_stopwatch);
190            mTabsAdapter.addTab(mStopwatchTab, StopwatchFragment.class,STOPWATCH_TAB_INDEX);
191            mActionBar.setSelectedNavigationItem(selectedIndex);
192        }
193    }
194
195    @Override
196    protected void onCreate(Bundle icicle) {
197        super.onCreate(icicle);
198
199        mSelectedTab = CLOCK_TAB_INDEX;
200        if (icicle != null) {
201            mSelectedTab = icicle.getInt(KEY_SELECTED_TAB, CLOCK_TAB_INDEX);
202            mClockState = icicle.getInt(KEY_CLOCK_STATE, CLOCK_NORMAL);
203        }
204
205        // Timer receiver may ask the app to go to the timer fragment if a timer expired
206        Intent i = getIntent();
207        if (i != null) {
208            int tab = i.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1);
209            if (tab != -1) {
210                mSelectedTab = tab;
211            }
212        }
213        initViews();
214        setHomeTimeZone();
215    }
216
217    @Override
218    protected void onResume() {
219        super.onResume();
220        setClockState(false);
221        Intent stopwatchIntent = new Intent(getApplicationContext(), StopwatchService.class);
222        stopwatchIntent.setAction(Stopwatches.KILL_NOTIF);
223        startService(stopwatchIntent);
224
225        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
226        SharedPreferences.Editor editor = prefs.edit();
227        editor.putBoolean(Timers.NOTIF_APP_OPEN, true);
228        editor.apply();
229        Intent timerIntent = new Intent();
230        timerIntent.setAction(Timers.NOTIF_IN_USE_CANCEL);
231        sendBroadcast(timerIntent);
232    }
233
234    @Override
235    public void onPause() {
236        Intent intent = new Intent(getApplicationContext(), StopwatchService.class);
237        intent.setAction(Stopwatches.SHOW_NOTIF);
238        startService(intent);
239
240        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
241        SharedPreferences.Editor editor = prefs.edit();
242        editor.putBoolean(Timers.NOTIF_APP_OPEN, false);
243        editor.apply();
244        Intent timerIntent = new Intent();
245        timerIntent.setAction(Timers.NOTIF_IN_USE_SHOW);
246        sendBroadcast(timerIntent);
247
248        super.onPause();
249    }
250
251    @Override
252    protected void onSaveInstanceState(Bundle outState) {
253        super.onSaveInstanceState(outState);
254        outState.putInt(KEY_SELECTED_TAB, mActionBar.getSelectedNavigationIndex());
255        outState.putInt(KEY_CLOCK_STATE, mClockState);
256    }
257
258    private void setClockState(boolean fade) {
259        doDim(fade);
260        switch(mClockState) {
261            case CLOCK_NORMAL:
262                doLightsOut(false);
263                break;
264            case CLOCK_LIGHTS_OUT:
265            case CLOCK_DIMMED:
266                doLightsOut(true);
267                break;
268            default:
269                break;
270        }
271    }
272
273    public void clockButtonsOnClick(View v) {
274        if (v == null)
275            return;
276        switch (v.getId()) {
277            case R.id.alarms_button:
278                startActivity(new Intent(this, AlarmClock.class));
279                break;
280            case R.id.cities_button:
281                startActivity(new Intent(this, CitiesActivity.class));
282                break;
283            case R.id.menu_button:
284                showMenu(v);
285                break;
286            default:
287                break;
288        }
289    }
290
291    private void showMenu(View v) {
292        PopupMenu popupMenu = new PopupMenu(this, v);
293        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener () {
294            @Override
295            public boolean onMenuItemClick(MenuItem item) {
296                switch (item.getItemId()) {
297                    case R.id.menu_item_settings:
298                        startActivity(new Intent(DeskClock.this, SettingsActivity.class));
299                        return true;
300                    case R.id.menu_item_help:
301                        Intent i = item.getIntent();
302                        if (i != null) {
303                            try {
304                                startActivity(i);
305                            } catch (ActivityNotFoundException e) {
306                                // No activity found to match the intent - ignore
307                            }
308                        }
309                        return true;
310                    default:
311                        break;
312                }
313                return true;
314            }
315        });
316        popupMenu.inflate(R.menu.desk_clock_menu);
317
318        Menu menu = popupMenu.getMenu();
319        MenuItem help = menu.findItem(R.id.menu_item_help);
320        if (help != null) {
321            Utils.prepareHelpMenuItem(this, help);
322        }
323        popupMenu.show();
324    }
325
326    /***
327     * Insert the local time zone as the Home Time Zone if one is not set
328     */
329    private void setHomeTimeZone() {
330        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
331        String homeTimeZone = prefs.getString(SettingsActivity.KEY_HOME_TZ, "");
332        if (!homeTimeZone.isEmpty()) {
333        return;
334        }
335        homeTimeZone = TimeZone.getDefault().getID();
336        SharedPreferences.Editor editor = prefs.edit();
337        editor.putString(SettingsActivity.KEY_HOME_TZ, homeTimeZone);
338        editor.apply();
339        Log.v(LOG_TAG, "Setting home time zone to " + homeTimeZone);
340    }
341
342    private void scheduleLightsOut() {
343        mHandy.removeMessages(LIGHTSOUT_TIMEOUT_MSG);
344        mHandy.sendMessageDelayed(Message.obtain(mHandy, LIGHTSOUT_TIMEOUT_MSG), LIGHTSOUT_TIMEOUT);
345    }
346
347    public void doLightsOut(boolean state) {
348
349        if (state) {
350            mViewPager.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
351            mActionBar.hide();
352        } else {
353            mViewPager.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
354            mActionBar.show();
355        }
356
357        // in clock view show/hide the buttons at the bottom
358        if (mViewPager.getCurrentItem() ==  CLOCK_TAB_INDEX) {
359            // TODO: switch to listeners
360/*            Fragment f = mTabsAdapter.getFragment(CLOCK_TAB_INDEX);
361            if (f != null) {
362                ((ClockFragment)f).showButtons(!state);
363            }*/
364        }
365        if (!state) {
366            // Make sure dim will not start before lights out
367            mHandy.removeMessages(DIM_TIMEOUT_MSG);
368            scheduleLightsOut();
369        }
370    }
371
372    private void doDim(boolean fade) {
373        if (mClockState == CLOCK_DIMMED) {
374            mViewPager.startAnimation(
375                    AnimationUtils.loadAnimation(this, fade ? R.anim.dim : R.anim.dim_instant));
376        } else {
377            mViewPager.startAnimation(
378                    AnimationUtils.loadAnimation(this, fade ? R.anim.undim : R.anim.undim_instant));
379        }
380    }
381
382    private void scheduleDim() {
383        mHandy.removeMessages(DIM_TIMEOUT_MSG);
384        mHandy.sendMessageDelayed(Message.obtain(mHandy, DIM_TIMEOUT_MSG), DIM_TIMEOUT);
385    }
386
387    @Override
388    public void onUserInteraction() {
389        super.onUserInteraction();
390        mHandy.removeMessages(BACK_TO_NORMAL_MSG);
391        mHandy.sendMessage(Message.obtain(mHandy, BACK_TO_NORMAL_MSG));
392    }
393
394    /***
395     * Adapter for wrapping together the ActionBar's tab with the ViewPager
396     */
397
398    private class TabsAdapter extends FragmentPagerAdapter
399            implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
400
401        private static final String KEY_TAB_POSITION = "tab_position";
402
403        final class TabInfo {
404            private final Class<?> clss;
405            private final Bundle args;
406
407            TabInfo(Class<?> _class, int position) {
408                clss = _class;
409                args = new Bundle();
410                args.putInt(KEY_TAB_POSITION, position);
411            }
412
413            public int getPosition() {
414                return args.getInt(KEY_TAB_POSITION, 0);
415            }
416        }
417
418        private final ArrayList<TabInfo> mTabs = new ArrayList <TabInfo>();
419        ActionBar mMainActionBar;
420        Context mContext;
421        ViewPager mPager;
422
423        public TabsAdapter(Activity activity, ViewPager pager) {
424            super(activity.getFragmentManager());
425            mContext = activity;
426            mMainActionBar = activity.getActionBar();
427            mPager = pager;
428            mPager.setAdapter(this);
429            mPager.setOnPageChangeListener(this);
430        }
431
432        @Override
433        public Fragment getItem(int position) {
434            TabInfo info = mTabs.get(position);
435            DeskClockFragment f = (DeskClockFragment) Fragment.instantiate(
436                    mContext, info.clss.getName(), info.args);
437            return f;
438        }
439
440        @Override
441        public int getCount() {
442            return mTabs.size();
443        }
444
445        public void addTab(ActionBar.Tab tab, Class<?> clss, int position) {
446            TabInfo info = new TabInfo(clss, position);
447            tab.setTag(info);
448            tab.setTabListener(this);
449            mTabs.add(info);
450            mMainActionBar.addTab(tab);
451            notifyDataSetChanged();
452        }
453
454        @Override
455        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
456            // Do nothing
457        }
458
459        @Override
460        public void onPageSelected(int position) {
461            mMainActionBar.setSelectedNavigationItem(position);
462            onUserInteraction();
463        }
464
465        @Override
466        public void onPageScrollStateChanged(int state) {
467            // Do nothing
468        }
469
470        @Override
471        public void onTabReselected(Tab arg0, FragmentTransaction arg1) {
472            // Do nothing
473        }
474
475        @Override
476        public void onTabSelected(Tab tab, FragmentTransaction ft) {
477            TabInfo info = (TabInfo)tab.getTag();
478            mPager.setCurrentItem(info.getPosition());
479            onUserInteraction();
480        }
481
482        @Override
483        public void onTabUnselected(Tab arg0, FragmentTransaction arg1) {
484            // Do nothing
485
486        }
487    }
488}
489