/* * 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.deskclock.uidata; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Typeface; import android.support.annotation.DrawableRes; import android.support.annotation.StringRes; import com.android.deskclock.AlarmClockFragment; import com.android.deskclock.ClockFragment; import com.android.deskclock.R; import com.android.deskclock.stopwatch.StopwatchFragment; import com.android.deskclock.timer.TimerFragment; import java.util.Calendar; import static com.android.deskclock.Utils.enforceMainLooper; /** * All application-wide user interface data is accessible through this singleton. */ public final class UiDataModel { /** Identifies each of the primary tabs within the application. */ public enum Tab { ALARMS(AlarmClockFragment.class, R.drawable.ic_tab_alarm, R.string.menu_alarm), CLOCKS(ClockFragment.class, R.drawable.ic_tab_clock, R.string.menu_clock), TIMERS(TimerFragment.class, R.drawable.ic_tab_timer, R.string.menu_timer), STOPWATCH(StopwatchFragment.class, R.drawable.ic_tab_stopwatch, R.string.menu_stopwatch); private final String mFragmentClassName; private final @DrawableRes int mIconResId; private final @StringRes int mLabelResId; Tab(Class fragmentClass, @DrawableRes int iconResId, @StringRes int labelResId) { mFragmentClassName = fragmentClass.getName(); mIconResId = iconResId; mLabelResId = labelResId; } public String getFragmentClassName() { return mFragmentClassName; } public @DrawableRes int getIconResId() { return mIconResId; } public @StringRes int getLabelResId() { return mLabelResId; } } /** The single instance of this data model that exists for the life of the application. */ private static final UiDataModel sUiDataModel = new UiDataModel(); public static UiDataModel getUiDataModel() { return sUiDataModel; } private Context mContext; /** The model from which tab data are fetched. */ private TabModel mTabModel; /** The model from which formatted strings are fetched. */ private FormattedStringModel mFormattedStringModel; /** The model from which timed callbacks originate. */ private PeriodicCallbackModel mPeriodicCallbackModel; private UiDataModel() {} /** * The context may be set precisely once during the application life. */ public void init(Context context, SharedPreferences prefs) { if (mContext != context) { mContext = context.getApplicationContext(); mPeriodicCallbackModel = new PeriodicCallbackModel(mContext); mFormattedStringModel = new FormattedStringModel(mContext); mTabModel = new TabModel(prefs); } } /** * To display the alarm clock in this font, use the character {@link R.string#clock_emoji}. * * @return a special font containing a glyph that draws an alarm clock */ public Typeface getAlarmIconTypeface() { return Typeface.createFromAsset(mContext.getAssets(), "fonts/clock.ttf"); } // // Formatted Strings // /** * This method is intended to be used when formatting numbers occurs in a hotspot such as the * update loop of a timer or stopwatch. It returns cached results when possible in order to * provide speed and limit garbage to be collected by the virtual machine. * * @param value a positive integer to format as a String * @return the {@code value} formatted as a String in the current locale * @throws IllegalArgumentException if {@code value} is negative */ public String getFormattedNumber(int value) { enforceMainLooper(); return mFormattedStringModel.getFormattedNumber(value); } /** * This method is intended to be used when formatting numbers occurs in a hotspot such as the * update loop of a timer or stopwatch. It returns cached results when possible in order to * provide speed and limit garbage to be collected by the virtual machine. * * @param value a positive integer to format as a String * @param length the length of the String; zeroes are padded to match this length * @return the {@code value} formatted as a String in the current locale and padded to the * requested {@code length} * @throws IllegalArgumentException if {@code value} is negative */ public String getFormattedNumber(int value, int length) { enforceMainLooper(); return mFormattedStringModel.getFormattedNumber(value, length); } /** * This method is intended to be used when formatting numbers occurs in a hotspot such as the * update loop of a timer or stopwatch. It returns cached results when possible in order to * provide speed and limit garbage to be collected by the virtual machine. * * @param negative force a minus sign (-) onto the display, even if {@code value} is {@code 0} * @param value a positive integer to format as a String * @param length the length of the String; zeroes are padded to match this length. If * {@code negative} is {@code true} the return value will contain a minus sign and a total * length of {@code length + 1}. * @return the {@code value} formatted as a String in the current locale and padded to the * requested {@code length} * @throws IllegalArgumentException if {@code value} is negative */ public String getFormattedNumber(boolean negative, int value, int length) { enforceMainLooper(); return mFormattedStringModel.getFormattedNumber(negative, value, length); } /** * @param calendarDay any of the following values * * @return single-character version of weekday name; e.g.: 'S', 'M', 'T', 'W', 'T', 'F', 'S' */ public String getShortWeekday(int calendarDay) { enforceMainLooper(); return mFormattedStringModel.getShortWeekday(calendarDay); } /** * @param calendarDay any of the following values * * @return full weekday name; e.g.: 'Sunday', 'Monday', 'Tuesday', etc. */ public String getLongWeekday(int calendarDay) { enforceMainLooper(); return mFormattedStringModel.getLongWeekday(calendarDay); } // // Animations // /** * @return the duration in milliseconds of short animations */ public long getShortAnimationDuration() { enforceMainLooper(); return mContext.getResources().getInteger(android.R.integer.config_shortAnimTime); } /** * @return the duration in milliseconds of long animations */ public long getLongAnimationDuration() { enforceMainLooper(); return mContext.getResources().getInteger(android.R.integer.config_longAnimTime); } // // Tabs // /** * @param tabListener to be notified when the selected tab changes */ public void addTabListener(TabListener tabListener) { enforceMainLooper(); mTabModel.addTabListener(tabListener); } /** * @param tabListener to no longer be notified when the selected tab changes */ public void removeTabListener(TabListener tabListener) { enforceMainLooper(); mTabModel.removeTabListener(tabListener); } /** * @return the number of tabs */ public int getTabCount() { enforceMainLooper(); return mTabModel.getTabCount(); } /** * @param ordinal the ordinal of the tab * @return the tab at the given {@code ordinal} */ public Tab getTab(int ordinal) { enforceMainLooper(); return mTabModel.getTab(ordinal); } /** * @param position the position of the tab in the user interface * @return the tab at the given {@code ordinal} */ public Tab getTabAt(int position) { enforceMainLooper(); return mTabModel.getTabAt(position); } /** * @return an enumerated value indicating the currently selected primary tab */ public Tab getSelectedTab() { enforceMainLooper(); return mTabModel.getSelectedTab(); } /** * @param tab an enumerated value indicating the newly selected primary tab */ public void setSelectedTab(Tab tab) { enforceMainLooper(); mTabModel.setSelectedTab(tab); } /** * @param tabScrollListener to be notified when the scroll position of the selected tab changes */ public void addTabScrollListener(TabScrollListener tabScrollListener) { enforceMainLooper(); mTabModel.addTabScrollListener(tabScrollListener); } /** * @param tabScrollListener to be notified when the scroll position of the selected tab changes */ public void removeTabScrollListener(TabScrollListener tabScrollListener) { enforceMainLooper(); mTabModel.removeTabScrollListener(tabScrollListener); } /** * Updates the scrolling state in the {@link UiDataModel} for this tab. * * @param tab an enumerated value indicating the tab reporting its vertical scroll position * @param scrolledToTop {@code true} iff the vertical scroll position of the tab is at the top */ public void setTabScrolledToTop(Tab tab, boolean scrolledToTop) { enforceMainLooper(); mTabModel.setTabScrolledToTop(tab, scrolledToTop); } /** * @return {@code true} iff the content in the selected tab is currently scrolled to the top */ public boolean isSelectedTabScrolledToTop() { enforceMainLooper(); return mTabModel.isTabScrolledToTop(getSelectedTab()); } // // Shortcut Ids // /** * @param category which category of shortcut of which to get the id * @param action the desired action to perform * @return the id of the shortcut */ public String getShortcutId(@StringRes int category, @StringRes int action) { if (category == R.string.category_stopwatch) { return mContext.getString(category); } return mContext.getString(category) + "_" + mContext.getString(action); } // // Timed Callbacks // /** * @param runnable to be called every minute * @param offset an offset applied to the minute to control when the callback occurs */ public void addMinuteCallback(Runnable runnable, long offset) { enforceMainLooper(); mPeriodicCallbackModel.addMinuteCallback(runnable, offset); } /** * @param runnable to be called every quarter-hour * @param offset an offset applied to the quarter-hour to control when the callback occurs */ public void addQuarterHourCallback(Runnable runnable, long offset) { enforceMainLooper(); mPeriodicCallbackModel.addQuarterHourCallback(runnable, offset); } /** * @param runnable to be called every hour * @param offset an offset applied to the hour to control when the callback occurs */ public void addHourCallback(Runnable runnable, long offset) { enforceMainLooper(); mPeriodicCallbackModel.addHourCallback(runnable, offset); } /** * @param runnable to be called every midnight * @param offset an offset applied to the midnight to control when the callback occurs */ public void addMidnightCallback(Runnable runnable, long offset) { enforceMainLooper(); mPeriodicCallbackModel.addMidnightCallback(runnable, offset); } /** * @param runnable to no longer be called periodically */ public void removePeriodicCallback(Runnable runnable) { enforceMainLooper(); mPeriodicCallbackModel.removePeriodicCallback(runnable); } }