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