Home | History | Annotate | Download | only in uidata
      1 /*
      2  * Copyright (C) 2015 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 
     17 package com.android.deskclock.uidata;
     18 
     19 import android.content.Context;
     20 import android.content.SharedPreferences;
     21 import android.graphics.Typeface;
     22 import android.support.annotation.DrawableRes;
     23 import android.support.annotation.StringRes;
     24 
     25 import com.android.deskclock.AlarmClockFragment;
     26 import com.android.deskclock.ClockFragment;
     27 import com.android.deskclock.R;
     28 import com.android.deskclock.stopwatch.StopwatchFragment;
     29 import com.android.deskclock.timer.TimerFragment;
     30 
     31 import java.util.Calendar;
     32 
     33 import static com.android.deskclock.Utils.enforceMainLooper;
     34 
     35 /**
     36  * All application-wide user interface data is accessible through this singleton.
     37  */
     38 public final class UiDataModel {
     39 
     40     /** Identifies each of the primary tabs within the application. */
     41     public enum Tab {
     42         ALARMS(AlarmClockFragment.class, R.drawable.ic_tab_alarm, R.string.menu_alarm),
     43         CLOCKS(ClockFragment.class, R.drawable.ic_tab_clock, R.string.menu_clock),
     44         TIMERS(TimerFragment.class, R.drawable.ic_tab_timer, R.string.menu_timer),
     45         STOPWATCH(StopwatchFragment.class, R.drawable.ic_tab_stopwatch, R.string.menu_stopwatch);
     46 
     47         private final String mFragmentClassName;
     48         private final @DrawableRes int mIconResId;
     49         private final @StringRes int mLabelResId;
     50 
     51         Tab(Class fragmentClass, @DrawableRes int iconResId, @StringRes int labelResId) {
     52             mFragmentClassName = fragmentClass.getName();
     53             mIconResId = iconResId;
     54             mLabelResId = labelResId;
     55         }
     56 
     57         public String getFragmentClassName() { return mFragmentClassName; }
     58         public @DrawableRes int getIconResId() { return mIconResId; }
     59         public @StringRes int getLabelResId() { return mLabelResId; }
     60     }
     61 
     62     /** The single instance of this data model that exists for the life of the application. */
     63     private static final UiDataModel sUiDataModel = new UiDataModel();
     64 
     65     public static UiDataModel getUiDataModel() {
     66         return sUiDataModel;
     67     }
     68 
     69     private Context mContext;
     70 
     71     /** The model from which tab data are fetched. */
     72     private TabModel mTabModel;
     73 
     74     /** The model from which formatted strings are fetched. */
     75     private FormattedStringModel mFormattedStringModel;
     76 
     77     /** The model from which timed callbacks originate. */
     78     private PeriodicCallbackModel mPeriodicCallbackModel;
     79 
     80     private UiDataModel() {}
     81 
     82     /**
     83      * The context may be set precisely once during the application life.
     84      */
     85     public void init(Context context, SharedPreferences prefs) {
     86         if (mContext != context) {
     87             mContext = context.getApplicationContext();
     88 
     89             mPeriodicCallbackModel = new PeriodicCallbackModel(mContext);
     90             mFormattedStringModel = new FormattedStringModel(mContext);
     91             mTabModel = new TabModel(prefs);
     92         }
     93     }
     94 
     95     /**
     96      * To display the alarm clock in this font, use the character {@link R.string#clock_emoji}.
     97      *
     98      * @return a special font containing a glyph that draws an alarm clock
     99      */
    100     public Typeface getAlarmIconTypeface() {
    101         return Typeface.createFromAsset(mContext.getAssets(), "fonts/clock.ttf");
    102     }
    103 
    104     //
    105     // Formatted Strings
    106     //
    107 
    108     /**
    109      * This method is intended to be used when formatting numbers occurs in a hotspot such as the
    110      * update loop of a timer or stopwatch. It returns cached results when possible in order to
    111      * provide speed and limit garbage to be collected by the virtual machine.
    112      *
    113      * @param value a positive integer to format as a String
    114      * @return the {@code value} formatted as a String in the current locale
    115      * @throws IllegalArgumentException if {@code value} is negative
    116      */
    117     public String getFormattedNumber(int value) {
    118         enforceMainLooper();
    119         return mFormattedStringModel.getFormattedNumber(value);
    120     }
    121 
    122     /**
    123      * This method is intended to be used when formatting numbers occurs in a hotspot such as the
    124      * update loop of a timer or stopwatch. It returns cached results when possible in order to
    125      * provide speed and limit garbage to be collected by the virtual machine.
    126      *
    127      * @param value a positive integer to format as a String
    128      * @param length the length of the String; zeroes are padded to match this length
    129      * @return the {@code value} formatted as a String in the current locale and padded to the
    130      *      requested {@code length}
    131      * @throws IllegalArgumentException if {@code value} is negative
    132      */
    133     public String getFormattedNumber(int value, int length) {
    134         enforceMainLooper();
    135         return mFormattedStringModel.getFormattedNumber(value, length);
    136     }
    137 
    138     /**
    139      * This method is intended to be used when formatting numbers occurs in a hotspot such as the
    140      * update loop of a timer or stopwatch. It returns cached results when possible in order to
    141      * provide speed and limit garbage to be collected by the virtual machine.
    142      *
    143      * @param negative force a minus sign (-) onto the display, even if {@code value} is {@code 0}
    144      * @param value a positive integer to format as a String
    145      * @param length the length of the String; zeroes are padded to match this length. If
    146      *      {@code negative} is {@code true} the return value will contain a minus sign and a total
    147      *      length of {@code length + 1}.
    148      * @return the {@code value} formatted as a String in the current locale and padded to the
    149      *      requested {@code length}
    150      * @throws IllegalArgumentException if {@code value} is negative
    151      */
    152     public String getFormattedNumber(boolean negative, int value, int length) {
    153         enforceMainLooper();
    154         return mFormattedStringModel.getFormattedNumber(negative, value, length);
    155     }
    156 
    157     /**
    158      * @param calendarDay any of the following values
    159      *                     <ul>
    160      *                     <li>{@link Calendar#SUNDAY}</li>
    161      *                     <li>{@link Calendar#MONDAY}</li>
    162      *                     <li>{@link Calendar#TUESDAY}</li>
    163      *                     <li>{@link Calendar#WEDNESDAY}</li>
    164      *                     <li>{@link Calendar#THURSDAY}</li>
    165      *                     <li>{@link Calendar#FRIDAY}</li>
    166      *                     <li>{@link Calendar#SATURDAY}</li>
    167      *                     </ul>
    168      * @return single-character version of weekday name; e.g.: 'S', 'M', 'T', 'W', 'T', 'F', 'S'
    169      */
    170     public String getShortWeekday(int calendarDay) {
    171         enforceMainLooper();
    172         return mFormattedStringModel.getShortWeekday(calendarDay);
    173     }
    174 
    175     /**
    176      * @param calendarDay any of the following values
    177      *                     <ul>
    178      *                     <li>{@link Calendar#SUNDAY}</li>
    179      *                     <li>{@link Calendar#MONDAY}</li>
    180      *                     <li>{@link Calendar#TUESDAY}</li>
    181      *                     <li>{@link Calendar#WEDNESDAY}</li>
    182      *                     <li>{@link Calendar#THURSDAY}</li>
    183      *                     <li>{@link Calendar#FRIDAY}</li>
    184      *                     <li>{@link Calendar#SATURDAY}</li>
    185      *                     </ul>
    186      * @return full weekday name; e.g.: 'Sunday', 'Monday', 'Tuesday', etc.
    187      */
    188     public String getLongWeekday(int calendarDay) {
    189         enforceMainLooper();
    190         return mFormattedStringModel.getLongWeekday(calendarDay);
    191     }
    192 
    193     //
    194     // Animations
    195     //
    196 
    197     /**
    198      * @return the duration in milliseconds of short animations
    199      */
    200     public long getShortAnimationDuration() {
    201         enforceMainLooper();
    202         return mContext.getResources().getInteger(android.R.integer.config_shortAnimTime);
    203     }
    204 
    205     /**
    206      * @return the duration in milliseconds of long animations
    207      */
    208     public long getLongAnimationDuration() {
    209         enforceMainLooper();
    210         return mContext.getResources().getInteger(android.R.integer.config_longAnimTime);
    211     }
    212 
    213     //
    214     // Tabs
    215     //
    216 
    217     /**
    218      * @param tabListener to be notified when the selected tab changes
    219      */
    220     public void addTabListener(TabListener tabListener) {
    221         enforceMainLooper();
    222         mTabModel.addTabListener(tabListener);
    223     }
    224 
    225     /**
    226      * @param tabListener to no longer be notified when the selected tab changes
    227      */
    228     public void removeTabListener(TabListener tabListener) {
    229         enforceMainLooper();
    230         mTabModel.removeTabListener(tabListener);
    231     }
    232 
    233     /**
    234      * @return the number of tabs
    235      */
    236     public int getTabCount() {
    237         enforceMainLooper();
    238         return mTabModel.getTabCount();
    239     }
    240 
    241     /**
    242      * @param ordinal the ordinal of the tab
    243      * @return the tab at the given {@code ordinal}
    244      */
    245     public Tab getTab(int ordinal) {
    246         enforceMainLooper();
    247         return mTabModel.getTab(ordinal);
    248     }
    249 
    250     /**
    251      * @param position the position of the tab in the user interface
    252      * @return the tab at the given {@code ordinal}
    253      */
    254     public Tab getTabAt(int position) {
    255         enforceMainLooper();
    256         return mTabModel.getTabAt(position);
    257     }
    258 
    259     /**
    260      * @return an enumerated value indicating the currently selected primary tab
    261      */
    262     public Tab getSelectedTab() {
    263         enforceMainLooper();
    264         return mTabModel.getSelectedTab();
    265     }
    266 
    267     /**
    268      * @param tab an enumerated value indicating the newly selected primary tab
    269      */
    270     public void setSelectedTab(Tab tab) {
    271         enforceMainLooper();
    272         mTabModel.setSelectedTab(tab);
    273     }
    274 
    275     /**
    276      * @param tabScrollListener to be notified when the scroll position of the selected tab changes
    277      */
    278     public void addTabScrollListener(TabScrollListener tabScrollListener) {
    279         enforceMainLooper();
    280         mTabModel.addTabScrollListener(tabScrollListener);
    281     }
    282 
    283     /**
    284      * @param tabScrollListener to be notified when the scroll position of the selected tab changes
    285      */
    286     public void removeTabScrollListener(TabScrollListener tabScrollListener) {
    287         enforceMainLooper();
    288         mTabModel.removeTabScrollListener(tabScrollListener);
    289     }
    290 
    291     /**
    292      * Updates the scrolling state in the {@link UiDataModel} for this tab.
    293      *
    294      * @param tab an enumerated value indicating the tab reporting its vertical scroll position
    295      * @param scrolledToTop {@code true} iff the vertical scroll position of the tab is at the top
    296      */
    297     public void setTabScrolledToTop(Tab tab, boolean scrolledToTop) {
    298         enforceMainLooper();
    299         mTabModel.setTabScrolledToTop(tab, scrolledToTop);
    300     }
    301 
    302     /**
    303      * @return {@code true} iff the content in the selected tab is currently scrolled to the top
    304      */
    305     public boolean isSelectedTabScrolledToTop() {
    306         enforceMainLooper();
    307         return mTabModel.isTabScrolledToTop(getSelectedTab());
    308     }
    309 
    310     //
    311     // Shortcut Ids
    312     //
    313 
    314     /**
    315      * @param category which category of shortcut of which to get the id
    316      * @param action the desired action to perform
    317      * @return the id of the shortcut
    318      */
    319     public String getShortcutId(@StringRes int category, @StringRes int action) {
    320         if (category == R.string.category_stopwatch) {
    321             return mContext.getString(category);
    322         }
    323         return mContext.getString(category) + "_" + mContext.getString(action);
    324     }
    325 
    326     //
    327     // Timed Callbacks
    328     //
    329 
    330     /**
    331      * @param runnable to be called every minute
    332      * @param offset an offset applied to the minute to control when the callback occurs
    333      */
    334     public void addMinuteCallback(Runnable runnable, long offset) {
    335         enforceMainLooper();
    336         mPeriodicCallbackModel.addMinuteCallback(runnable, offset);
    337     }
    338 
    339     /**
    340      * @param runnable to be called every quarter-hour
    341      * @param offset an offset applied to the quarter-hour to control when the callback occurs
    342      */
    343     public void addQuarterHourCallback(Runnable runnable, long offset) {
    344         enforceMainLooper();
    345         mPeriodicCallbackModel.addQuarterHourCallback(runnable, offset);
    346     }
    347 
    348     /**
    349      * @param runnable to be called every hour
    350      * @param offset an offset applied to the hour to control when the callback occurs
    351      */
    352     public void addHourCallback(Runnable runnable, long offset) {
    353         enforceMainLooper();
    354         mPeriodicCallbackModel.addHourCallback(runnable, offset);
    355     }
    356 
    357     /**
    358      * @param runnable to be called every midnight
    359      * @param offset an offset applied to the midnight to control when the callback occurs
    360      */
    361     public void addMidnightCallback(Runnable runnable, long offset) {
    362         enforceMainLooper();
    363         mPeriodicCallbackModel.addMidnightCallback(runnable, offset);
    364     }
    365 
    366     /**
    367      * @param runnable to no longer be called periodically
    368      */
    369     public void removePeriodicCallback(Runnable runnable) {
    370         enforceMainLooper();
    371         mPeriodicCallbackModel.removePeriodicCallback(runnable);
    372     }
    373 }
    374