Merge "AudioSystem JNI: fix AudioPortConfig mask usage" into lmp-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index b5055eb..c20ad64 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1056,6 +1056,7 @@
field public static final int scrollbars = 16842974; // 0x10100de
field public static final int scrollingCache = 16843006; // 0x10100fe
field public static final deprecated int searchButtonText = 16843269; // 0x1010205
+ field public static final int searchHintIcon = 16844038; // 0x1010506
field public static final int searchIcon = 16843907; // 0x1010483
field public static final int searchMode = 16843221; // 0x10101d5
field public static final int searchSettingsDescription = 16843402; // 0x101028a
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index f380d68..ed59ea6 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -17,42 +17,24 @@
package android.widget;
import android.annotation.Widget;
-import android.app.Service;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.Paint.Style;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.TypedValue;
-import android.view.GestureDetector;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.AbsListView.OnScrollListener;
import com.android.internal.R;
+import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
-import libcore.icu.LocaleData;
-
/**
* This class is a calendar widget for displaying and selecting dates. The range
* of dates supported by this calendar is configurable. A user can select a date
@@ -74,13 +56,12 @@
*/
@Widget
public class CalendarView extends FrameLayout {
+ private static final String LOG_TAG = "CalendarView";
- /**
- * Tag for logging.
- */
- private static final String LOG_TAG = CalendarView.class.getSimpleName();
+ private static final int MODE_HOLO = 0;
+ private static final int MODE_MATERIAL = 1;
- private CalendarViewDelegate mDelegate;
+ private final CalendarViewDelegate mDelegate;
/**
* The callback used to indicate the user changes the date.
@@ -113,7 +94,23 @@
public CalendarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mDelegate = new LegacyCalendarViewDelegate(this, context, attrs, defStyleAttr, defStyleRes);
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.CalendarView, defStyleAttr, defStyleRes);
+ final int mode = a.getInt(R.styleable.CalendarView_calendarViewMode, MODE_HOLO);
+ a.recycle();
+
+ switch (mode) {
+ case MODE_HOLO:
+ mDelegate = new CalendarViewLegacyDelegate(
+ this, context, attrs, defStyleAttr, defStyleRes);
+ break;
+ case MODE_MATERIAL:
+ mDelegate = new CalendarViewMaterialDelegate(
+ this, context, attrs, defStyleAttr, defStyleRes);
+ break;
+ default:
+ throw new IllegalArgumentException("invalid calendarViewMode attribute");
+ }
}
/**
@@ -326,16 +323,6 @@
return mDelegate.getDateTextAppearance();
}
- @Override
- public void setEnabled(boolean enabled) {
- mDelegate.setEnabled(enabled);
- }
-
- @Override
- public boolean isEnabled() {
- return mDelegate.isEnabled();
- }
-
/**
* Gets the minimal date supported by this {@link CalendarView} in milliseconds
* since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
@@ -516,14 +503,12 @@
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
- mDelegate.onInitializeAccessibilityEvent(event);
+ event.setClassName(CalendarView.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- mDelegate.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(CalendarView.class.getName());
}
/**
@@ -560,9 +545,6 @@
void setDateTextAppearance(int resourceId);
int getDateTextAppearance();
- void setEnabled(boolean enabled);
- boolean isEnabled();
-
void setMinDate(long minDate);
long getMinDate();
@@ -582,21 +564,26 @@
void setOnDateChangeListener(OnDateChangeListener listener);
void onConfigurationChanged(Configuration newConfig);
- void onInitializeAccessibilityEvent(AccessibilityEvent event);
- void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
}
/**
* An abstract class which can be used as a start for CalendarView implementations
*/
abstract static class AbstractCalendarViewDelegate implements CalendarViewDelegate {
- // The delegator
+ /** String for parsing dates. */
+ private static final String DATE_FORMAT = "MM/dd/yyyy";
+
+ /** The default minimal date. */
+ protected static final String DEFAULT_MIN_DATE = "01/01/1900";
+
+ /** The default maximal date. */
+ protected static final String DEFAULT_MAX_DATE = "01/01/2100";
+
+ /** Date format for parsing dates. */
+ protected static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
+
protected CalendarView mDelegator;
-
- // The context
protected Context mContext;
-
- // The current locale
protected Locale mCurrentLocale;
AbstractCalendarViewDelegate(CalendarView delegator, Context context) {
@@ -613,830 +600,6 @@
}
mCurrentLocale = locale;
}
- }
-
- /**
- * A delegate implementing the legacy CalendarView
- */
- private static class LegacyCalendarViewDelegate extends AbstractCalendarViewDelegate {
-
- /**
- * Default value whether to show week number.
- */
- private static final boolean DEFAULT_SHOW_WEEK_NUMBER = true;
-
- /**
- * The number of milliseconds in a day.e
- */
- private static final long MILLIS_IN_DAY = 86400000L;
-
- /**
- * The number of day in a week.
- */
- private static final int DAYS_PER_WEEK = 7;
-
- /**
- * The number of milliseconds in a week.
- */
- private static final long MILLIS_IN_WEEK = DAYS_PER_WEEK * MILLIS_IN_DAY;
-
- /**
- * Affects when the month selection will change while scrolling upe
- */
- private static final int SCROLL_HYST_WEEKS = 2;
-
- /**
- * How long the GoTo fling animation should last.
- */
- private static final int GOTO_SCROLL_DURATION = 1000;
-
- /**
- * The duration of the adjustment upon a user scroll in milliseconds.
- */
- private static final int ADJUSTMENT_SCROLL_DURATION = 500;
-
- /**
- * How long to wait after receiving an onScrollStateChanged notification
- * before acting on it.
- */
- private static final int SCROLL_CHANGE_DELAY = 40;
-
- /**
- * String for parsing dates.
- */
- private static final String DATE_FORMAT = "MM/dd/yyyy";
-
- /**
- * The default minimal date.
- */
- private static final String DEFAULT_MIN_DATE = "01/01/1900";
-
- /**
- * The default maximal date.
- */
- private static final String DEFAULT_MAX_DATE = "01/01/2100";
-
- private static final int DEFAULT_SHOWN_WEEK_COUNT = 6;
-
- private static final int DEFAULT_DATE_TEXT_SIZE = 14;
-
- private static final int UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH = 6;
-
- private static final int UNSCALED_WEEK_MIN_VISIBLE_HEIGHT = 12;
-
- private static final int UNSCALED_LIST_SCROLL_TOP_OFFSET = 2;
-
- private static final int UNSCALED_BOTTOM_BUFFER = 20;
-
- private static final int UNSCALED_WEEK_SEPARATOR_LINE_WIDTH = 1;
-
- private static final int DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID = -1;
-
- private final int mWeekSeperatorLineWidth;
-
- private int mDateTextSize;
-
- private Drawable mSelectedDateVerticalBar;
-
- private final int mSelectedDateVerticalBarWidth;
-
- private int mSelectedWeekBackgroundColor;
-
- private int mFocusedMonthDateColor;
-
- private int mUnfocusedMonthDateColor;
-
- private int mWeekSeparatorLineColor;
-
- private int mWeekNumberColor;
-
- private int mWeekDayTextAppearanceResId;
-
- private int mDateTextAppearanceResId;
-
- /**
- * The top offset of the weeks list.
- */
- private int mListScrollTopOffset = 2;
-
- /**
- * The visible height of a week view.
- */
- private int mWeekMinVisibleHeight = 12;
-
- /**
- * The visible height of a week view.
- */
- private int mBottomBuffer = 20;
-
- /**
- * The number of shown weeks.
- */
- private int mShownWeekCount;
-
- /**
- * Flag whether to show the week number.
- */
- private boolean mShowWeekNumber;
-
- /**
- * The number of day per week to be shown.
- */
- private int mDaysPerWeek = 7;
-
- /**
- * The friction of the week list while flinging.
- */
- private float mFriction = .05f;
-
- /**
- * Scale for adjusting velocity of the week list while flinging.
- */
- private float mVelocityScale = 0.333f;
-
- /**
- * The adapter for the weeks list.
- */
- private WeeksAdapter mAdapter;
-
- /**
- * The weeks list.
- */
- private ListView mListView;
-
- /**
- * The name of the month to display.
- */
- private TextView mMonthName;
-
- /**
- * The header with week day names.
- */
- private ViewGroup mDayNamesHeader;
-
- /**
- * Cached abbreviations for day of week names.
- */
- private String[] mDayNamesShort;
-
- /**
- * Cached full-length day of week names.
- */
- private String[] mDayNamesLong;
-
- /**
- * The first day of the week.
- */
- private int mFirstDayOfWeek;
-
- /**
- * Which month should be displayed/highlighted [0-11].
- */
- private int mCurrentMonthDisplayed = -1;
-
- /**
- * Used for tracking during a scroll.
- */
- private long mPreviousScrollPosition;
-
- /**
- * Used for tracking which direction the view is scrolling.
- */
- private boolean mIsScrollingUp = false;
-
- /**
- * The previous scroll state of the weeks ListView.
- */
- private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-
- /**
- * The current scroll state of the weeks ListView.
- */
- private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-
- /**
- * Listener for changes in the selected day.
- */
- private OnDateChangeListener mOnDateChangeListener;
-
- /**
- * Command for adjusting the position after a scroll/fling.
- */
- private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
-
- /**
- * Temporary instance to avoid multiple instantiations.
- */
- private Calendar mTempDate;
-
- /**
- * The first day of the focused month.
- */
- private Calendar mFirstDayOfMonth;
-
- /**
- * The start date of the range supported by this picker.
- */
- private Calendar mMinDate;
-
- /**
- * The end date of the range supported by this picker.
- */
- private Calendar mMaxDate;
-
- /**
- * Date format for parsing dates.
- */
- private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
-
- LegacyCalendarViewDelegate(CalendarView delegator, Context context, AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
- super(delegator, context);
-
- // initialization based on locale
- setCurrentLocale(Locale.getDefault());
-
- TypedArray attributesArray = context.obtainStyledAttributes(attrs,
- R.styleable.CalendarView, defStyleAttr, defStyleRes);
- mShowWeekNumber = attributesArray.getBoolean(R.styleable.CalendarView_showWeekNumber,
- DEFAULT_SHOW_WEEK_NUMBER);
- mFirstDayOfWeek = attributesArray.getInt(R.styleable.CalendarView_firstDayOfWeek,
- LocaleData.get(Locale.getDefault()).firstDayOfWeek);
- String minDate = attributesArray.getString(R.styleable.CalendarView_minDate);
- if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
- parseDate(DEFAULT_MIN_DATE, mMinDate);
- }
- String maxDate = attributesArray.getString(R.styleable.CalendarView_maxDate);
- if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
- parseDate(DEFAULT_MAX_DATE, mMaxDate);
- }
- if (mMaxDate.before(mMinDate)) {
- throw new IllegalArgumentException("Max date cannot be before min date.");
- }
- mShownWeekCount = attributesArray.getInt(R.styleable.CalendarView_shownWeekCount,
- DEFAULT_SHOWN_WEEK_COUNT);
- mSelectedWeekBackgroundColor = attributesArray.getColor(
- R.styleable.CalendarView_selectedWeekBackgroundColor, 0);
- mFocusedMonthDateColor = attributesArray.getColor(
- R.styleable.CalendarView_focusedMonthDateColor, 0);
- mUnfocusedMonthDateColor = attributesArray.getColor(
- R.styleable.CalendarView_unfocusedMonthDateColor, 0);
- mWeekSeparatorLineColor = attributesArray.getColor(
- R.styleable.CalendarView_weekSeparatorLineColor, 0);
- mWeekNumberColor = attributesArray.getColor(R.styleable.CalendarView_weekNumberColor, 0);
- mSelectedDateVerticalBar = attributesArray.getDrawable(
- R.styleable.CalendarView_selectedDateVerticalBar);
-
- mDateTextAppearanceResId = attributesArray.getResourceId(
- R.styleable.CalendarView_dateTextAppearance, R.style.TextAppearance_Small);
- updateDateTextSize();
-
- mWeekDayTextAppearanceResId = attributesArray.getResourceId(
- R.styleable.CalendarView_weekDayTextAppearance,
- DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
- attributesArray.recycle();
-
- DisplayMetrics displayMetrics = mDelegator.getResources().getDisplayMetrics();
- mWeekMinVisibleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- UNSCALED_WEEK_MIN_VISIBLE_HEIGHT, displayMetrics);
- mListScrollTopOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- UNSCALED_LIST_SCROLL_TOP_OFFSET, displayMetrics);
- mBottomBuffer = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- UNSCALED_BOTTOM_BUFFER, displayMetrics);
- mSelectedDateVerticalBarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH, displayMetrics);
- mWeekSeperatorLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- UNSCALED_WEEK_SEPARATOR_LINE_WIDTH, displayMetrics);
-
- LayoutInflater layoutInflater = (LayoutInflater) mContext
- .getSystemService(Service.LAYOUT_INFLATER_SERVICE);
- View content = layoutInflater.inflate(R.layout.calendar_view, null, false);
- mDelegator.addView(content);
-
- mListView = (ListView) mDelegator.findViewById(R.id.list);
- mDayNamesHeader = (ViewGroup) content.findViewById(com.android.internal.R.id.day_names);
- mMonthName = (TextView) content.findViewById(com.android.internal.R.id.month_name);
-
- setUpHeader();
- setUpListView();
- setUpAdapter();
-
- // go to today or whichever is close to today min or max date
- mTempDate.setTimeInMillis(System.currentTimeMillis());
- if (mTempDate.before(mMinDate)) {
- goTo(mMinDate, false, true, true);
- } else if (mMaxDate.before(mTempDate)) {
- goTo(mMaxDate, false, true, true);
- } else {
- goTo(mTempDate, false, true, true);
- }
-
- mDelegator.invalidate();
- }
-
- @Override
- public void setShownWeekCount(int count) {
- if (mShownWeekCount != count) {
- mShownWeekCount = count;
- mDelegator.invalidate();
- }
- }
-
- @Override
- public int getShownWeekCount() {
- return mShownWeekCount;
- }
-
- @Override
- public void setSelectedWeekBackgroundColor(int color) {
- if (mSelectedWeekBackgroundColor != color) {
- mSelectedWeekBackgroundColor = color;
- final int childCount = mListView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- WeekView weekView = (WeekView) mListView.getChildAt(i);
- if (weekView.mHasSelectedDay) {
- weekView.invalidate();
- }
- }
- }
- }
-
- @Override
- public int getSelectedWeekBackgroundColor() {
- return mSelectedWeekBackgroundColor;
- }
-
- @Override
- public void setFocusedMonthDateColor(int color) {
- if (mFocusedMonthDateColor != color) {
- mFocusedMonthDateColor = color;
- final int childCount = mListView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- WeekView weekView = (WeekView) mListView.getChildAt(i);
- if (weekView.mHasFocusedDay) {
- weekView.invalidate();
- }
- }
- }
- }
-
- @Override
- public int getFocusedMonthDateColor() {
- return mFocusedMonthDateColor;
- }
-
- @Override
- public void setUnfocusedMonthDateColor(int color) {
- if (mUnfocusedMonthDateColor != color) {
- mUnfocusedMonthDateColor = color;
- final int childCount = mListView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- WeekView weekView = (WeekView) mListView.getChildAt(i);
- if (weekView.mHasUnfocusedDay) {
- weekView.invalidate();
- }
- }
- }
- }
-
- @Override
- public int getUnfocusedMonthDateColor() {
- return mFocusedMonthDateColor;
- }
-
- @Override
- public void setWeekNumberColor(int color) {
- if (mWeekNumberColor != color) {
- mWeekNumberColor = color;
- if (mShowWeekNumber) {
- invalidateAllWeekViews();
- }
- }
- }
-
- @Override
- public int getWeekNumberColor() {
- return mWeekNumberColor;
- }
-
- @Override
- public void setWeekSeparatorLineColor(int color) {
- if (mWeekSeparatorLineColor != color) {
- mWeekSeparatorLineColor = color;
- invalidateAllWeekViews();
- }
- }
-
- @Override
- public int getWeekSeparatorLineColor() {
- return mWeekSeparatorLineColor;
- }
-
- @Override
- public void setSelectedDateVerticalBar(int resourceId) {
- Drawable drawable = mDelegator.getContext().getDrawable(resourceId);
- setSelectedDateVerticalBar(drawable);
- }
-
- @Override
- public void setSelectedDateVerticalBar(Drawable drawable) {
- if (mSelectedDateVerticalBar != drawable) {
- mSelectedDateVerticalBar = drawable;
- final int childCount = mListView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- WeekView weekView = (WeekView) mListView.getChildAt(i);
- if (weekView.mHasSelectedDay) {
- weekView.invalidate();
- }
- }
- }
- }
-
- @Override
- public Drawable getSelectedDateVerticalBar() {
- return mSelectedDateVerticalBar;
- }
-
- @Override
- public void setWeekDayTextAppearance(int resourceId) {
- if (mWeekDayTextAppearanceResId != resourceId) {
- mWeekDayTextAppearanceResId = resourceId;
- setUpHeader();
- }
- }
-
- @Override
- public int getWeekDayTextAppearance() {
- return mWeekDayTextAppearanceResId;
- }
-
- @Override
- public void setDateTextAppearance(int resourceId) {
- if (mDateTextAppearanceResId != resourceId) {
- mDateTextAppearanceResId = resourceId;
- updateDateTextSize();
- invalidateAllWeekViews();
- }
- }
-
- @Override
- public int getDateTextAppearance() {
- return mDateTextAppearanceResId;
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- mListView.setEnabled(enabled);
- }
-
- @Override
- public boolean isEnabled() {
- return mListView.isEnabled();
- }
-
- @Override
- public void setMinDate(long minDate) {
- mTempDate.setTimeInMillis(minDate);
- if (isSameDate(mTempDate, mMinDate)) {
- return;
- }
- mMinDate.setTimeInMillis(minDate);
- // make sure the current date is not earlier than
- // the new min date since the latter is used for
- // calculating the indices in the adapter thus
- // avoiding out of bounds error
- Calendar date = mAdapter.mSelectedDate;
- if (date.before(mMinDate)) {
- mAdapter.setSelectedDay(mMinDate);
- }
- // reinitialize the adapter since its range depends on min date
- mAdapter.init();
- if (date.before(mMinDate)) {
- setDate(mTempDate.getTimeInMillis());
- } else {
- // we go to the current date to force the ListView to query its
- // adapter for the shown views since we have changed the adapter
- // range and the base from which the later calculates item indices
- // note that calling setDate will not work since the date is the same
- goTo(date, false, true, false);
- }
- }
-
- @Override
- public long getMinDate() {
- return mMinDate.getTimeInMillis();
- }
-
- @Override
- public void setMaxDate(long maxDate) {
- mTempDate.setTimeInMillis(maxDate);
- if (isSameDate(mTempDate, mMaxDate)) {
- return;
- }
- mMaxDate.setTimeInMillis(maxDate);
- // reinitialize the adapter since its range depends on max date
- mAdapter.init();
- Calendar date = mAdapter.mSelectedDate;
- if (date.after(mMaxDate)) {
- setDate(mMaxDate.getTimeInMillis());
- } else {
- // we go to the current date to force the ListView to query its
- // adapter for the shown views since we have changed the adapter
- // range and the base from which the later calculates item indices
- // note that calling setDate will not work since the date is the same
- goTo(date, false, true, false);
- }
- }
-
- @Override
- public long getMaxDate() {
- return mMaxDate.getTimeInMillis();
- }
-
- @Override
- public void setShowWeekNumber(boolean showWeekNumber) {
- if (mShowWeekNumber == showWeekNumber) {
- return;
- }
- mShowWeekNumber = showWeekNumber;
- mAdapter.notifyDataSetChanged();
- setUpHeader();
- }
-
- @Override
- public boolean getShowWeekNumber() {
- return mShowWeekNumber;
- }
-
- @Override
- public void setFirstDayOfWeek(int firstDayOfWeek) {
- if (mFirstDayOfWeek == firstDayOfWeek) {
- return;
- }
- mFirstDayOfWeek = firstDayOfWeek;
- mAdapter.init();
- mAdapter.notifyDataSetChanged();
- setUpHeader();
- }
-
- @Override
- public int getFirstDayOfWeek() {
- return mFirstDayOfWeek;
- }
-
- @Override
- public void setDate(long date) {
- setDate(date, false, false);
- }
-
- @Override
- public void setDate(long date, boolean animate, boolean center) {
- mTempDate.setTimeInMillis(date);
- if (isSameDate(mTempDate, mAdapter.mSelectedDate)) {
- return;
- }
- goTo(mTempDate, animate, true, center);
- }
-
- @Override
- public long getDate() {
- return mAdapter.mSelectedDate.getTimeInMillis();
- }
-
- @Override
- public void setOnDateChangeListener(OnDateChangeListener listener) {
- mOnDateChangeListener = listener;
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- setCurrentLocale(newConfig.locale);
- }
-
- @Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- event.setClassName(CalendarView.class.getName());
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- info.setClassName(CalendarView.class.getName());
- }
-
- /**
- * Sets the current locale.
- *
- * @param locale The current locale.
- */
- @Override
- protected void setCurrentLocale(Locale locale) {
- super.setCurrentLocale(locale);
-
- mTempDate = getCalendarForLocale(mTempDate, locale);
- mFirstDayOfMonth = getCalendarForLocale(mFirstDayOfMonth, locale);
- mMinDate = getCalendarForLocale(mMinDate, locale);
- mMaxDate = getCalendarForLocale(mMaxDate, locale);
- }
- private void updateDateTextSize() {
- TypedArray dateTextAppearance = mDelegator.getContext().obtainStyledAttributes(
- mDateTextAppearanceResId, R.styleable.TextAppearance);
- mDateTextSize = dateTextAppearance.getDimensionPixelSize(
- R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
- dateTextAppearance.recycle();
- }
-
- /**
- * Invalidates all week views.
- */
- private void invalidateAllWeekViews() {
- final int childCount = mListView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View view = mListView.getChildAt(i);
- view.invalidate();
- }
- }
-
- /**
- * Gets a calendar for locale bootstrapped with the value of a given calendar.
- *
- * @param oldCalendar The old calendar.
- * @param locale The locale.
- */
- private static Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
- if (oldCalendar == null) {
- return Calendar.getInstance(locale);
- } else {
- final long currentTimeMillis = oldCalendar.getTimeInMillis();
- Calendar newCalendar = Calendar.getInstance(locale);
- newCalendar.setTimeInMillis(currentTimeMillis);
- return newCalendar;
- }
- }
-
- /**
- * @return True if the <code>firstDate</code> is the same as the <code>
- * secondDate</code>.
- */
- private static boolean isSameDate(Calendar firstDate, Calendar secondDate) {
- return (firstDate.get(Calendar.DAY_OF_YEAR) == secondDate.get(Calendar.DAY_OF_YEAR)
- && firstDate.get(Calendar.YEAR) == secondDate.get(Calendar.YEAR));
- }
-
- /**
- * Creates a new adapter if necessary and sets up its parameters.
- */
- private void setUpAdapter() {
- if (mAdapter == null) {
- mAdapter = new WeeksAdapter(mContext);
- mAdapter.registerDataSetObserver(new DataSetObserver() {
- @Override
- public void onChanged() {
- if (mOnDateChangeListener != null) {
- Calendar selectedDay = mAdapter.getSelectedDay();
- mOnDateChangeListener.onSelectedDayChange(mDelegator,
- selectedDay.get(Calendar.YEAR),
- selectedDay.get(Calendar.MONTH),
- selectedDay.get(Calendar.DAY_OF_MONTH));
- }
- }
- });
- mListView.setAdapter(mAdapter);
- }
-
- // refresh the view with the new parameters
- mAdapter.notifyDataSetChanged();
- }
-
- /**
- * Sets up the strings to be used by the header.
- */
- private void setUpHeader() {
- mDayNamesShort = new String[mDaysPerWeek];
- mDayNamesLong = new String[mDaysPerWeek];
- for (int i = mFirstDayOfWeek, count = mFirstDayOfWeek + mDaysPerWeek; i < count; i++) {
- int calendarDay = (i > Calendar.SATURDAY) ? i - Calendar.SATURDAY : i;
- mDayNamesShort[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
- DateUtils.LENGTH_SHORTEST);
- mDayNamesLong[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
- DateUtils.LENGTH_LONG);
- }
-
- TextView label = (TextView) mDayNamesHeader.getChildAt(0);
- if (mShowWeekNumber) {
- label.setVisibility(View.VISIBLE);
- } else {
- label.setVisibility(View.GONE);
- }
- for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) {
- label = (TextView) mDayNamesHeader.getChildAt(i);
- if (mWeekDayTextAppearanceResId > -1) {
- label.setTextAppearance(mContext, mWeekDayTextAppearanceResId);
- }
- if (i < mDaysPerWeek + 1) {
- label.setText(mDayNamesShort[i - 1]);
- label.setContentDescription(mDayNamesLong[i - 1]);
- label.setVisibility(View.VISIBLE);
- } else {
- label.setVisibility(View.GONE);
- }
- }
- mDayNamesHeader.invalidate();
- }
-
- /**
- * Sets all the required fields for the list view.
- */
- private void setUpListView() {
- // Configure the listview
- mListView.setDivider(null);
- mListView.setItemsCanFocus(true);
- mListView.setVerticalScrollBarEnabled(false);
- mListView.setOnScrollListener(new OnScrollListener() {
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- LegacyCalendarViewDelegate.this.onScrollStateChanged(view, scrollState);
- }
-
- public void onScroll(
- AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- LegacyCalendarViewDelegate.this.onScroll(view, firstVisibleItem,
- visibleItemCount, totalItemCount);
- }
- });
- // Make the scrolling behavior nicer
- mListView.setFriction(mFriction);
- mListView.setVelocityScale(mVelocityScale);
- }
-
- /**
- * This moves to the specified time in the view. If the time is not already
- * in range it will move the list so that the first of the month containing
- * the time is at the top of the view. If the new time is already in view
- * the list will not be scrolled unless forceScroll is true. This time may
- * optionally be highlighted as selected as well.
- *
- * @param date The time to move to.
- * @param animate Whether to scroll to the given time or just redraw at the
- * new location.
- * @param setSelected Whether to set the given time as selected.
- * @param forceScroll Whether to recenter even if the time is already
- * visible.
- *
- * @throws IllegalArgumentException of the provided date is before the
- * range start of after the range end.
- */
- private void goTo(Calendar date, boolean animate, boolean setSelected,
- boolean forceScroll) {
- if (date.before(mMinDate) || date.after(mMaxDate)) {
- throw new IllegalArgumentException("Time not between " + mMinDate.getTime()
- + " and " + mMaxDate.getTime());
- }
- // Find the first and last entirely visible weeks
- int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
- View firstChild = mListView.getChildAt(0);
- if (firstChild != null && firstChild.getTop() < 0) {
- firstFullyVisiblePosition++;
- }
- int lastFullyVisiblePosition = firstFullyVisiblePosition + mShownWeekCount - 1;
- if (firstChild != null && firstChild.getTop() > mBottomBuffer) {
- lastFullyVisiblePosition--;
- }
- if (setSelected) {
- mAdapter.setSelectedDay(date);
- }
- // Get the week we're going to
- int position = getWeeksSinceMinDate(date);
-
- // Check if the selected day is now outside of our visible range
- // and if so scroll to the month that contains it
- if (position < firstFullyVisiblePosition || position > lastFullyVisiblePosition
- || forceScroll) {
- mFirstDayOfMonth.setTimeInMillis(date.getTimeInMillis());
- mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
-
- setMonthDisplayed(mFirstDayOfMonth);
-
- // the earliest time we can scroll to is the min date
- if (mFirstDayOfMonth.before(mMinDate)) {
- position = 0;
- } else {
- position = getWeeksSinceMinDate(mFirstDayOfMonth);
- }
-
- mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
- if (animate) {
- mListView.smoothScrollToPositionFromTop(position, mListScrollTopOffset,
- GOTO_SCROLL_DURATION);
- } else {
- mListView.setSelectionFromTop(position, mListScrollTopOffset);
- // Perform any after scroll operations that are needed
- onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE);
- }
- } else if (setSelected) {
- // Otherwise just set the selection
- setMonthDisplayed(date);
- }
- }
/**
* Parses the given <code>date</code> and in case of success sets
@@ -1444,718 +607,15 @@
*
* @return True if the date was parsed.
*/
- private boolean parseDate(String date, Calendar outDate) {
+ protected boolean parseDate(String date, Calendar outDate) {
try {
- outDate.setTime(mDateFormat.parse(date));
+ outDate.setTime(DATE_FORMATTER.parse(date));
return true;
} catch (ParseException e) {
Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
return false;
}
}
-
- /**
- * Called when a <code>view</code> transitions to a new <code>scrollState
- * </code>.
- */
- private void onScrollStateChanged(AbsListView view, int scrollState) {
- mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
- }
-
- /**
- * Updates the title and selected month if the <code>view</code> has moved to a new
- * month.
- */
- private void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- WeekView child = (WeekView) view.getChildAt(0);
- if (child == null) {
- return;
- }
-
- // Figure out where we are
- long currScroll =
- view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
-
- // If we have moved since our last call update the direction
- if (currScroll < mPreviousScrollPosition) {
- mIsScrollingUp = true;
- } else if (currScroll > mPreviousScrollPosition) {
- mIsScrollingUp = false;
- } else {
- return;
- }
-
- // Use some hysteresis for checking which month to highlight. This
- // causes the month to transition when two full weeks of a month are
- // visible when scrolling up, and when the first day in a month reaches
- // the top of the screen when scrolling down.
- int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
- if (mIsScrollingUp) {
- child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
- } else if (offset != 0) {
- child = (WeekView) view.getChildAt(offset);
- }
-
- if (child != null) {
- // Find out which month we're moving into
- int month;
- if (mIsScrollingUp) {
- month = child.getMonthOfFirstWeekDay();
- } else {
- month = child.getMonthOfLastWeekDay();
- }
-
- // And how it relates to our current highlighted month
- int monthDiff;
- if (mCurrentMonthDisplayed == 11 && month == 0) {
- monthDiff = 1;
- } else if (mCurrentMonthDisplayed == 0 && month == 11) {
- monthDiff = -1;
- } else {
- monthDiff = month - mCurrentMonthDisplayed;
- }
-
- // Only switch months if we're scrolling away from the currently
- // selected month
- if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) {
- Calendar firstDay = child.getFirstDay();
- if (mIsScrollingUp) {
- firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK);
- } else {
- firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK);
- }
- setMonthDisplayed(firstDay);
- }
- }
- mPreviousScrollPosition = currScroll;
- mPreviousScrollState = mCurrentScrollState;
- }
-
- /**
- * Sets the month displayed at the top of this view based on time. Override
- * to add custom events when the title is changed.
- *
- * @param calendar A day in the new focus month.
- */
- private void setMonthDisplayed(Calendar calendar) {
- mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
- mAdapter.setFocusMonth(mCurrentMonthDisplayed);
- final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
- | DateUtils.FORMAT_SHOW_YEAR;
- final long millis = calendar.getTimeInMillis();
- String newMonthName = DateUtils.formatDateRange(mContext, millis, millis, flags);
- mMonthName.setText(newMonthName);
- mMonthName.invalidate();
- }
-
- /**
- * @return Returns the number of weeks between the current <code>date</code>
- * and the <code>mMinDate</code>.
- */
- private int getWeeksSinceMinDate(Calendar date) {
- if (date.before(mMinDate)) {
- throw new IllegalArgumentException("fromDate: " + mMinDate.getTime()
- + " does not precede toDate: " + date.getTime());
- }
- long endTimeMillis = date.getTimeInMillis()
- + date.getTimeZone().getOffset(date.getTimeInMillis());
- long startTimeMillis = mMinDate.getTimeInMillis()
- + mMinDate.getTimeZone().getOffset(mMinDate.getTimeInMillis());
- long dayOffsetMillis = (mMinDate.get(Calendar.DAY_OF_WEEK) - mFirstDayOfWeek)
- * MILLIS_IN_DAY;
- return (int) ((endTimeMillis - startTimeMillis + dayOffsetMillis) / MILLIS_IN_WEEK);
- }
-
- /**
- * Command responsible for acting upon scroll state changes.
- */
- private class ScrollStateRunnable implements Runnable {
- private AbsListView mView;
-
- private int mNewState;
-
- /**
- * Sets up the runnable with a short delay in case the scroll state
- * immediately changes again.
- *
- * @param view The list view that changed state
- * @param scrollState The new state it changed to
- */
- public void doScrollStateChange(AbsListView view, int scrollState) {
- mView = view;
- mNewState = scrollState;
- mDelegator.removeCallbacks(this);
- mDelegator.postDelayed(this, SCROLL_CHANGE_DELAY);
- }
-
- public void run() {
- mCurrentScrollState = mNewState;
- // Fix the position after a scroll or a fling ends
- if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
- && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
- View child = mView.getChildAt(0);
- if (child == null) {
- // The view is no longer visible, just return
- return;
- }
- int dist = child.getBottom() - mListScrollTopOffset;
- if (dist > mListScrollTopOffset) {
- if (mIsScrollingUp) {
- mView.smoothScrollBy(dist - child.getHeight(),
- ADJUSTMENT_SCROLL_DURATION);
- } else {
- mView.smoothScrollBy(dist, ADJUSTMENT_SCROLL_DURATION);
- }
- }
- }
- mPreviousScrollState = mNewState;
- }
- }
-
- /**
- * <p>
- * This is a specialized adapter for creating a list of weeks with
- * selectable days. It can be configured to display the week number, start
- * the week on a given day, show a reduced number of days, or display an
- * arbitrary number of weeks at a time.
- * </p>
- */
- private class WeeksAdapter extends BaseAdapter implements OnTouchListener {
-
- private int mSelectedWeek;
-
- private GestureDetector mGestureDetector;
-
- private int mFocusedMonth;
-
- private final Calendar mSelectedDate = Calendar.getInstance();
-
- private int mTotalWeekCount;
-
- public WeeksAdapter(Context context) {
- mContext = context;
- mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener());
- init();
- }
-
- /**
- * Set up the gesture detector and selected time
- */
- private void init() {
- mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
- mTotalWeekCount = getWeeksSinceMinDate(mMaxDate);
- if (mMinDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek
- || mMaxDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek) {
- mTotalWeekCount++;
- }
- notifyDataSetChanged();
- }
-
- /**
- * Updates the selected day and related parameters.
- *
- * @param selectedDay The time to highlight
- */
- public void setSelectedDay(Calendar selectedDay) {
- if (selectedDay.get(Calendar.DAY_OF_YEAR) == mSelectedDate.get(Calendar.DAY_OF_YEAR)
- && selectedDay.get(Calendar.YEAR) == mSelectedDate.get(Calendar.YEAR)) {
- return;
- }
- mSelectedDate.setTimeInMillis(selectedDay.getTimeInMillis());
- mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
- mFocusedMonth = mSelectedDate.get(Calendar.MONTH);
- notifyDataSetChanged();
- }
-
- /**
- * @return The selected day of month.
- */
- public Calendar getSelectedDay() {
- return mSelectedDate;
- }
-
- @Override
- public int getCount() {
- return mTotalWeekCount;
- }
-
- @Override
- public Object getItem(int position) {
- return null;
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- WeekView weekView = null;
- if (convertView != null) {
- weekView = (WeekView) convertView;
- } else {
- weekView = new WeekView(mContext);
- android.widget.AbsListView.LayoutParams params =
- new android.widget.AbsListView.LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- weekView.setLayoutParams(params);
- weekView.setClickable(true);
- weekView.setOnTouchListener(this);
- }
-
- int selectedWeekDay = (mSelectedWeek == position) ? mSelectedDate.get(
- Calendar.DAY_OF_WEEK) : -1;
- weekView.init(position, selectedWeekDay, mFocusedMonth);
-
- return weekView;
- }
-
- /**
- * Changes which month is in focus and updates the view.
- *
- * @param month The month to show as in focus [0-11]
- */
- public void setFocusMonth(int month) {
- if (mFocusedMonth == month) {
- return;
- }
- mFocusedMonth = month;
- notifyDataSetChanged();
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (mListView.isEnabled() && mGestureDetector.onTouchEvent(event)) {
- WeekView weekView = (WeekView) v;
- // if we cannot find a day for the given location we are done
- if (!weekView.getDayFromLocation(event.getX(), mTempDate)) {
- return true;
- }
- // it is possible that the touched day is outside the valid range
- // we draw whole weeks but range end can fall not on the week end
- if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
- return true;
- }
- onDateTapped(mTempDate);
- return true;
- }
- return false;
- }
-
- /**
- * Maintains the same hour/min/sec but moves the day to the tapped day.
- *
- * @param day The day that was tapped
- */
- private void onDateTapped(Calendar day) {
- setSelectedDay(day);
- setMonthDisplayed(day);
- }
-
- /**
- * This is here so we can identify single tap events and set the
- * selected day correctly
- */
- class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- return true;
- }
- }
- }
-
- /**
- * <p>
- * This is a dynamic view for drawing a single week. It can be configured to
- * display the week number, start the week on a given day, or show a reduced
- * number of days. It is intended for use as a single view within a
- * ListView. See {@link WeeksAdapter} for usage.
- * </p>
- */
- private class WeekView extends View {
-
- private final Rect mTempRect = new Rect();
-
- private final Paint mDrawPaint = new Paint();
-
- private final Paint mMonthNumDrawPaint = new Paint();
-
- // Cache the number strings so we don't have to recompute them each time
- private String[] mDayNumbers;
-
- // Quick lookup for checking which days are in the focus month
- private boolean[] mFocusDay;
-
- // Whether this view has a focused day.
- private boolean mHasFocusedDay;
-
- // Whether this view has only focused days.
- private boolean mHasUnfocusedDay;
-
- // The first day displayed by this item
- private Calendar mFirstDay;
-
- // The month of the first day in this week
- private int mMonthOfFirstWeekDay = -1;
-
- // The month of the last day in this week
- private int mLastWeekDayMonth = -1;
-
- // The position of this week, equivalent to weeks since the week of Jan
- // 1st, 1900
- private int mWeek = -1;
-
- // Quick reference to the width of this view, matches parent
- private int mWidth;
-
- // The height this view should draw at in pixels, set by height param
- private int mHeight;
-
- // If this view contains the selected day
- private boolean mHasSelectedDay = false;
-
- // Which day is selected [0-6] or -1 if no day is selected
- private int mSelectedDay = -1;
-
- // The number of days + a spot for week number if it is displayed
- private int mNumCells;
-
- // The left edge of the selected day
- private int mSelectedLeft = -1;
-
- // The right edge of the selected day
- private int mSelectedRight = -1;
-
- public WeekView(Context context) {
- super(context);
-
- // Sets up any standard paints that will be used
- initilaizePaints();
- }
-
- /**
- * Initializes this week view.
- *
- * @param weekNumber The number of the week this view represents. The
- * week number is a zero based index of the weeks since
- * {@link CalendarView#getMinDate()}.
- * @param selectedWeekDay The selected day of the week from 0 to 6, -1 if no
- * selected day.
- * @param focusedMonth The month that is currently in focus i.e.
- * highlighted.
- */
- public void init(int weekNumber, int selectedWeekDay, int focusedMonth) {
- mSelectedDay = selectedWeekDay;
- mHasSelectedDay = mSelectedDay != -1;
- mNumCells = mShowWeekNumber ? mDaysPerWeek + 1 : mDaysPerWeek;
- mWeek = weekNumber;
- mTempDate.setTimeInMillis(mMinDate.getTimeInMillis());
-
- mTempDate.add(Calendar.WEEK_OF_YEAR, mWeek);
- mTempDate.setFirstDayOfWeek(mFirstDayOfWeek);
-
- // Allocate space for caching the day numbers and focus values
- mDayNumbers = new String[mNumCells];
- mFocusDay = new boolean[mNumCells];
-
- // If we're showing the week number calculate it based on Monday
- int i = 0;
- if (mShowWeekNumber) {
- mDayNumbers[0] = String.format(Locale.getDefault(), "%d",
- mTempDate.get(Calendar.WEEK_OF_YEAR));
- i++;
- }
-
- // Now adjust our starting day based on the start day of the week
- int diff = mFirstDayOfWeek - mTempDate.get(Calendar.DAY_OF_WEEK);
- mTempDate.add(Calendar.DAY_OF_MONTH, diff);
-
- mFirstDay = (Calendar) mTempDate.clone();
- mMonthOfFirstWeekDay = mTempDate.get(Calendar.MONTH);
-
- mHasUnfocusedDay = true;
- for (; i < mNumCells; i++) {
- final boolean isFocusedDay = (mTempDate.get(Calendar.MONTH) == focusedMonth);
- mFocusDay[i] = isFocusedDay;
- mHasFocusedDay |= isFocusedDay;
- mHasUnfocusedDay &= !isFocusedDay;
- // do not draw dates outside the valid range to avoid user confusion
- if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
- mDayNumbers[i] = "";
- } else {
- mDayNumbers[i] = String.format(Locale.getDefault(), "%d",
- mTempDate.get(Calendar.DAY_OF_MONTH));
- }
- mTempDate.add(Calendar.DAY_OF_MONTH, 1);
- }
- // We do one extra add at the end of the loop, if that pushed us to
- // new month undo it
- if (mTempDate.get(Calendar.DAY_OF_MONTH) == 1) {
- mTempDate.add(Calendar.DAY_OF_MONTH, -1);
- }
- mLastWeekDayMonth = mTempDate.get(Calendar.MONTH);
-
- updateSelectionPositions();
- }
-
- /**
- * Initialize the paint instances.
- */
- private void initilaizePaints() {
- mDrawPaint.setFakeBoldText(false);
- mDrawPaint.setAntiAlias(true);
- mDrawPaint.setStyle(Style.FILL);
-
- mMonthNumDrawPaint.setFakeBoldText(true);
- mMonthNumDrawPaint.setAntiAlias(true);
- mMonthNumDrawPaint.setStyle(Style.FILL);
- mMonthNumDrawPaint.setTextAlign(Align.CENTER);
- mMonthNumDrawPaint.setTextSize(mDateTextSize);
- }
-
- /**
- * Returns the month of the first day in this week.
- *
- * @return The month the first day of this view is in.
- */
- public int getMonthOfFirstWeekDay() {
- return mMonthOfFirstWeekDay;
- }
-
- /**
- * Returns the month of the last day in this week
- *
- * @return The month the last day of this view is in
- */
- public int getMonthOfLastWeekDay() {
- return mLastWeekDayMonth;
- }
-
- /**
- * Returns the first day in this view.
- *
- * @return The first day in the view.
- */
- public Calendar getFirstDay() {
- return mFirstDay;
- }
-
- /**
- * Calculates the day that the given x position is in, accounting for
- * week number.
- *
- * @param x The x position of the touch event.
- * @return True if a day was found for the given location.
- */
- public boolean getDayFromLocation(float x, Calendar outCalendar) {
- final boolean isLayoutRtl = isLayoutRtl();
-
- int start;
- int end;
-
- if (isLayoutRtl) {
- start = 0;
- end = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
- } else {
- start = mShowWeekNumber ? mWidth / mNumCells : 0;
- end = mWidth;
- }
-
- if (x < start || x > end) {
- outCalendar.clear();
- return false;
- }
-
- // Selection is (x - start) / (pixels/day) which is (x - start) * day / pixels
- int dayPosition = (int) ((x - start) * mDaysPerWeek / (end - start));
-
- if (isLayoutRtl) {
- dayPosition = mDaysPerWeek - 1 - dayPosition;
- }
-
- outCalendar.setTimeInMillis(mFirstDay.getTimeInMillis());
- outCalendar.add(Calendar.DAY_OF_MONTH, dayPosition);
-
- return true;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- drawBackground(canvas);
- drawWeekNumbersAndDates(canvas);
- drawWeekSeparators(canvas);
- drawSelectedDateVerticalBars(canvas);
- }
-
- /**
- * This draws the selection highlight if a day is selected in this week.
- *
- * @param canvas The canvas to draw on
- */
- private void drawBackground(Canvas canvas) {
- if (!mHasSelectedDay) {
- return;
- }
- mDrawPaint.setColor(mSelectedWeekBackgroundColor);
-
- mTempRect.top = mWeekSeperatorLineWidth;
- mTempRect.bottom = mHeight;
-
- final boolean isLayoutRtl = isLayoutRtl();
-
- if (isLayoutRtl) {
- mTempRect.left = 0;
- mTempRect.right = mSelectedLeft - 2;
- } else {
- mTempRect.left = mShowWeekNumber ? mWidth / mNumCells : 0;
- mTempRect.right = mSelectedLeft - 2;
- }
- canvas.drawRect(mTempRect, mDrawPaint);
-
- if (isLayoutRtl) {
- mTempRect.left = mSelectedRight + 3;
- mTempRect.right = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
- } else {
- mTempRect.left = mSelectedRight + 3;
- mTempRect.right = mWidth;
- }
- canvas.drawRect(mTempRect, mDrawPaint);
- }
-
- /**
- * Draws the week and month day numbers for this week.
- *
- * @param canvas The canvas to draw on
- */
- private void drawWeekNumbersAndDates(Canvas canvas) {
- final float textHeight = mDrawPaint.getTextSize();
- final int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorLineWidth;
- final int nDays = mNumCells;
- final int divisor = 2 * nDays;
-
- mDrawPaint.setTextAlign(Align.CENTER);
- mDrawPaint.setTextSize(mDateTextSize);
-
- int i = 0;
-
- if (isLayoutRtl()) {
- for (; i < nDays - 1; i++) {
- mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
- : mUnfocusedMonthDateColor);
- int x = (2 * i + 1) * mWidth / divisor;
- canvas.drawText(mDayNumbers[nDays - 1 - i], x, y, mMonthNumDrawPaint);
- }
- if (mShowWeekNumber) {
- mDrawPaint.setColor(mWeekNumberColor);
- int x = mWidth - mWidth / divisor;
- canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
- }
- } else {
- if (mShowWeekNumber) {
- mDrawPaint.setColor(mWeekNumberColor);
- int x = mWidth / divisor;
- canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
- i++;
- }
- for (; i < nDays; i++) {
- mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
- : mUnfocusedMonthDateColor);
- int x = (2 * i + 1) * mWidth / divisor;
- canvas.drawText(mDayNumbers[i], x, y, mMonthNumDrawPaint);
- }
- }
- }
-
- /**
- * Draws a horizontal line for separating the weeks.
- *
- * @param canvas The canvas to draw on.
- */
- private void drawWeekSeparators(Canvas canvas) {
- // If it is the topmost fully visible child do not draw separator line
- int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
- if (mListView.getChildAt(0).getTop() < 0) {
- firstFullyVisiblePosition++;
- }
- if (firstFullyVisiblePosition == mWeek) {
- return;
- }
- mDrawPaint.setColor(mWeekSeparatorLineColor);
- mDrawPaint.setStrokeWidth(mWeekSeperatorLineWidth);
- float startX;
- float stopX;
- if (isLayoutRtl()) {
- startX = 0;
- stopX = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
- } else {
- startX = mShowWeekNumber ? mWidth / mNumCells : 0;
- stopX = mWidth;
- }
- canvas.drawLine(startX, 0, stopX, 0, mDrawPaint);
- }
-
- /**
- * Draws the selected date bars if this week has a selected day.
- *
- * @param canvas The canvas to draw on
- */
- private void drawSelectedDateVerticalBars(Canvas canvas) {
- if (!mHasSelectedDay) {
- return;
- }
- mSelectedDateVerticalBar.setBounds(
- mSelectedLeft - mSelectedDateVerticalBarWidth / 2,
- mWeekSeperatorLineWidth,
- mSelectedLeft + mSelectedDateVerticalBarWidth / 2,
- mHeight);
- mSelectedDateVerticalBar.draw(canvas);
- mSelectedDateVerticalBar.setBounds(
- mSelectedRight - mSelectedDateVerticalBarWidth / 2,
- mWeekSeperatorLineWidth,
- mSelectedRight + mSelectedDateVerticalBarWidth / 2,
- mHeight);
- mSelectedDateVerticalBar.draw(canvas);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- mWidth = w;
- updateSelectionPositions();
- }
-
- /**
- * This calculates the positions for the selected day lines.
- */
- private void updateSelectionPositions() {
- if (mHasSelectedDay) {
- final boolean isLayoutRtl = isLayoutRtl();
- int selectedPosition = mSelectedDay - mFirstDayOfWeek;
- if (selectedPosition < 0) {
- selectedPosition += 7;
- }
- if (mShowWeekNumber && !isLayoutRtl) {
- selectedPosition++;
- }
- if (isLayoutRtl) {
- mSelectedLeft = (mDaysPerWeek - 1 - selectedPosition) * mWidth / mNumCells;
-
- } else {
- mSelectedLeft = selectedPosition * mWidth / mNumCells;
- }
- mSelectedRight = mSelectedLeft + mWidth / mNumCells;
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mHeight = (mListView.getHeight() - mListView.getPaddingTop() - mListView
- .getPaddingBottom()) / mShownWeekCount;
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
- }
- }
-
}
}
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
new file mode 100644
index 0000000..2ab3548
--- /dev/null
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -0,0 +1,1527 @@
+/*
+ * Copyright (C) 2014 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 android.widget;
+
+import com.android.internal.R;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.Calendar;
+import java.util.Locale;
+
+import libcore.icu.LocaleData;
+
+/**
+ * A delegate implementing the legacy CalendarView
+ */
+class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelegate {
+ /**
+ * Default value whether to show week number.
+ */
+ private static final boolean DEFAULT_SHOW_WEEK_NUMBER = true;
+
+ /**
+ * The number of milliseconds in a day.e
+ */
+ private static final long MILLIS_IN_DAY = 86400000L;
+
+ /**
+ * The number of day in a week.
+ */
+ private static final int DAYS_PER_WEEK = 7;
+
+ /**
+ * The number of milliseconds in a week.
+ */
+ private static final long MILLIS_IN_WEEK = DAYS_PER_WEEK * MILLIS_IN_DAY;
+
+ /**
+ * Affects when the month selection will change while scrolling upe
+ */
+ private static final int SCROLL_HYST_WEEKS = 2;
+
+ /**
+ * How long the GoTo fling animation should last.
+ */
+ private static final int GOTO_SCROLL_DURATION = 1000;
+
+ /**
+ * The duration of the adjustment upon a user scroll in milliseconds.
+ */
+ private static final int ADJUSTMENT_SCROLL_DURATION = 500;
+
+ /**
+ * How long to wait after receiving an onScrollStateChanged notification
+ * before acting on it.
+ */
+ private static final int SCROLL_CHANGE_DELAY = 40;
+
+ private static final int DEFAULT_SHOWN_WEEK_COUNT = 6;
+
+ private static final int DEFAULT_DATE_TEXT_SIZE = 14;
+
+ private static final int UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH = 6;
+
+ private static final int UNSCALED_WEEK_MIN_VISIBLE_HEIGHT = 12;
+
+ private static final int UNSCALED_LIST_SCROLL_TOP_OFFSET = 2;
+
+ private static final int UNSCALED_BOTTOM_BUFFER = 20;
+
+ private static final int UNSCALED_WEEK_SEPARATOR_LINE_WIDTH = 1;
+
+ private static final int DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID = -1;
+
+ private final int mWeekSeperatorLineWidth;
+
+ private int mDateTextSize;
+
+ private Drawable mSelectedDateVerticalBar;
+
+ private final int mSelectedDateVerticalBarWidth;
+
+ private int mSelectedWeekBackgroundColor;
+
+ private int mFocusedMonthDateColor;
+
+ private int mUnfocusedMonthDateColor;
+
+ private int mWeekSeparatorLineColor;
+
+ private int mWeekNumberColor;
+
+ private int mWeekDayTextAppearanceResId;
+
+ private int mDateTextAppearanceResId;
+
+ /**
+ * The top offset of the weeks list.
+ */
+ private int mListScrollTopOffset = 2;
+
+ /**
+ * The visible height of a week view.
+ */
+ private int mWeekMinVisibleHeight = 12;
+
+ /**
+ * The visible height of a week view.
+ */
+ private int mBottomBuffer = 20;
+
+ /**
+ * The number of shown weeks.
+ */
+ private int mShownWeekCount;
+
+ /**
+ * Flag whether to show the week number.
+ */
+ private boolean mShowWeekNumber;
+
+ /**
+ * The number of day per week to be shown.
+ */
+ private int mDaysPerWeek = 7;
+
+ /**
+ * The friction of the week list while flinging.
+ */
+ private float mFriction = .05f;
+
+ /**
+ * Scale for adjusting velocity of the week list while flinging.
+ */
+ private float mVelocityScale = 0.333f;
+
+ /**
+ * The adapter for the weeks list.
+ */
+ private WeeksAdapter mAdapter;
+
+ /**
+ * The weeks list.
+ */
+ private ListView mListView;
+
+ /**
+ * The name of the month to display.
+ */
+ private TextView mMonthName;
+
+ /**
+ * The header with week day names.
+ */
+ private ViewGroup mDayNamesHeader;
+
+ /**
+ * Cached abbreviations for day of week names.
+ */
+ private String[] mDayNamesShort;
+
+ /**
+ * Cached full-length day of week names.
+ */
+ private String[] mDayNamesLong;
+
+ /**
+ * The first day of the week.
+ */
+ private int mFirstDayOfWeek;
+
+ /**
+ * Which month should be displayed/highlighted [0-11].
+ */
+ private int mCurrentMonthDisplayed = -1;
+
+ /**
+ * Used for tracking during a scroll.
+ */
+ private long mPreviousScrollPosition;
+
+ /**
+ * Used for tracking which direction the view is scrolling.
+ */
+ private boolean mIsScrollingUp = false;
+
+ /**
+ * The previous scroll state of the weeks ListView.
+ */
+ private int mPreviousScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
+
+ /**
+ * The current scroll state of the weeks ListView.
+ */
+ private int mCurrentScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
+
+ /**
+ * Listener for changes in the selected day.
+ */
+ private CalendarView.OnDateChangeListener mOnDateChangeListener;
+
+ /**
+ * Command for adjusting the position after a scroll/fling.
+ */
+ private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
+
+ /**
+ * Temporary instance to avoid multiple instantiations.
+ */
+ private Calendar mTempDate;
+
+ /**
+ * The first day of the focused month.
+ */
+ private Calendar mFirstDayOfMonth;
+
+ /**
+ * The start date of the range supported by this picker.
+ */
+ private Calendar mMinDate;
+
+ /**
+ * The end date of the range supported by this picker.
+ */
+ private Calendar mMaxDate;
+
+ CalendarViewLegacyDelegate(CalendarView delegator, Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(delegator, context);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.CalendarView, defStyleAttr, defStyleRes);
+ mShowWeekNumber = a.getBoolean(R.styleable.CalendarView_showWeekNumber,
+ DEFAULT_SHOW_WEEK_NUMBER);
+ mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
+ LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+ final String minDate = a.getString(R.styleable.CalendarView_minDate);
+ if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
+ parseDate(DEFAULT_MIN_DATE, mMinDate);
+ }
+ final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
+ if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
+ parseDate(DEFAULT_MAX_DATE, mMaxDate);
+ }
+ if (mMaxDate.before(mMinDate)) {
+ throw new IllegalArgumentException("Max date cannot be before min date.");
+ }
+ mShownWeekCount = a.getInt(R.styleable.CalendarView_shownWeekCount,
+ DEFAULT_SHOWN_WEEK_COUNT);
+ mSelectedWeekBackgroundColor = a.getColor(
+ R.styleable.CalendarView_selectedWeekBackgroundColor, 0);
+ mFocusedMonthDateColor = a.getColor(
+ R.styleable.CalendarView_focusedMonthDateColor, 0);
+ mUnfocusedMonthDateColor = a.getColor(
+ R.styleable.CalendarView_unfocusedMonthDateColor, 0);
+ mWeekSeparatorLineColor = a.getColor(
+ R.styleable.CalendarView_weekSeparatorLineColor, 0);
+ mWeekNumberColor = a.getColor(R.styleable.CalendarView_weekNumberColor, 0);
+ mSelectedDateVerticalBar = a.getDrawable(
+ R.styleable.CalendarView_selectedDateVerticalBar);
+
+ mDateTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_dateTextAppearance, R.style.TextAppearance_Small);
+ updateDateTextSize();
+
+ mWeekDayTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_weekDayTextAppearance,
+ DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
+ a.recycle();
+
+ DisplayMetrics displayMetrics = mDelegator.getResources().getDisplayMetrics();
+ mWeekMinVisibleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_WEEK_MIN_VISIBLE_HEIGHT, displayMetrics);
+ mListScrollTopOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_LIST_SCROLL_TOP_OFFSET, displayMetrics);
+ mBottomBuffer = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_BOTTOM_BUFFER, displayMetrics);
+ mSelectedDateVerticalBarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH, displayMetrics);
+ mWeekSeperatorLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ UNSCALED_WEEK_SEPARATOR_LINE_WIDTH, displayMetrics);
+
+ LayoutInflater layoutInflater = (LayoutInflater) mContext
+ .getSystemService(Service.LAYOUT_INFLATER_SERVICE);
+ View content = layoutInflater.inflate(R.layout.calendar_view, null, false);
+ mDelegator.addView(content);
+
+ mListView = (ListView) mDelegator.findViewById(R.id.list);
+ mDayNamesHeader = (ViewGroup) content.findViewById(R.id.day_names);
+ mMonthName = (TextView) content.findViewById(R.id.month_name);
+
+ setUpHeader();
+ setUpListView();
+ setUpAdapter();
+
+ // go to today or whichever is close to today min or max date
+ mTempDate.setTimeInMillis(System.currentTimeMillis());
+ if (mTempDate.before(mMinDate)) {
+ goTo(mMinDate, false, true, true);
+ } else if (mMaxDate.before(mTempDate)) {
+ goTo(mMaxDate, false, true, true);
+ } else {
+ goTo(mTempDate, false, true, true);
+ }
+
+ mDelegator.invalidate();
+ }
+
+ @Override
+ public void setShownWeekCount(int count) {
+ if (mShownWeekCount != count) {
+ mShownWeekCount = count;
+ mDelegator.invalidate();
+ }
+ }
+
+ @Override
+ public int getShownWeekCount() {
+ return mShownWeekCount;
+ }
+
+ @Override
+ public void setSelectedWeekBackgroundColor(int color) {
+ if (mSelectedWeekBackgroundColor != color) {
+ mSelectedWeekBackgroundColor = color;
+ final int childCount = mListView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ WeekView weekView = (WeekView) mListView.getChildAt(i);
+ if (weekView.mHasSelectedDay) {
+ weekView.invalidate();
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getSelectedWeekBackgroundColor() {
+ return mSelectedWeekBackgroundColor;
+ }
+
+ @Override
+ public void setFocusedMonthDateColor(int color) {
+ if (mFocusedMonthDateColor != color) {
+ mFocusedMonthDateColor = color;
+ final int childCount = mListView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ WeekView weekView = (WeekView) mListView.getChildAt(i);
+ if (weekView.mHasFocusedDay) {
+ weekView.invalidate();
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getFocusedMonthDateColor() {
+ return mFocusedMonthDateColor;
+ }
+
+ @Override
+ public void setUnfocusedMonthDateColor(int color) {
+ if (mUnfocusedMonthDateColor != color) {
+ mUnfocusedMonthDateColor = color;
+ final int childCount = mListView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ WeekView weekView = (WeekView) mListView.getChildAt(i);
+ if (weekView.mHasUnfocusedDay) {
+ weekView.invalidate();
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getUnfocusedMonthDateColor() {
+ return mFocusedMonthDateColor;
+ }
+
+ @Override
+ public void setWeekNumberColor(int color) {
+ if (mWeekNumberColor != color) {
+ mWeekNumberColor = color;
+ if (mShowWeekNumber) {
+ invalidateAllWeekViews();
+ }
+ }
+ }
+
+ @Override
+ public int getWeekNumberColor() {
+ return mWeekNumberColor;
+ }
+
+ @Override
+ public void setWeekSeparatorLineColor(int color) {
+ if (mWeekSeparatorLineColor != color) {
+ mWeekSeparatorLineColor = color;
+ invalidateAllWeekViews();
+ }
+ }
+
+ @Override
+ public int getWeekSeparatorLineColor() {
+ return mWeekSeparatorLineColor;
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(int resourceId) {
+ Drawable drawable = mDelegator.getContext().getDrawable(resourceId);
+ setSelectedDateVerticalBar(drawable);
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(Drawable drawable) {
+ if (mSelectedDateVerticalBar != drawable) {
+ mSelectedDateVerticalBar = drawable;
+ final int childCount = mListView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ WeekView weekView = (WeekView) mListView.getChildAt(i);
+ if (weekView.mHasSelectedDay) {
+ weekView.invalidate();
+ }
+ }
+ }
+ }
+
+ @Override
+ public Drawable getSelectedDateVerticalBar() {
+ return mSelectedDateVerticalBar;
+ }
+
+ @Override
+ public void setWeekDayTextAppearance(int resourceId) {
+ if (mWeekDayTextAppearanceResId != resourceId) {
+ mWeekDayTextAppearanceResId = resourceId;
+ setUpHeader();
+ }
+ }
+
+ @Override
+ public int getWeekDayTextAppearance() {
+ return mWeekDayTextAppearanceResId;
+ }
+
+ @Override
+ public void setDateTextAppearance(int resourceId) {
+ if (mDateTextAppearanceResId != resourceId) {
+ mDateTextAppearanceResId = resourceId;
+ updateDateTextSize();
+ invalidateAllWeekViews();
+ }
+ }
+
+ @Override
+ public int getDateTextAppearance() {
+ return mDateTextAppearanceResId;
+ }
+
+ @Override
+ public void setMinDate(long minDate) {
+ mTempDate.setTimeInMillis(minDate);
+ if (isSameDate(mTempDate, mMinDate)) {
+ return;
+ }
+ mMinDate.setTimeInMillis(minDate);
+ // make sure the current date is not earlier than
+ // the new min date since the latter is used for
+ // calculating the indices in the adapter thus
+ // avoiding out of bounds error
+ Calendar date = mAdapter.mSelectedDate;
+ if (date.before(mMinDate)) {
+ mAdapter.setSelectedDay(mMinDate);
+ }
+ // reinitialize the adapter since its range depends on min date
+ mAdapter.init();
+ if (date.before(mMinDate)) {
+ setDate(mTempDate.getTimeInMillis());
+ } else {
+ // we go to the current date to force the ListView to query its
+ // adapter for the shown views since we have changed the adapter
+ // range and the base from which the later calculates item indices
+ // note that calling setDate will not work since the date is the same
+ goTo(date, false, true, false);
+ }
+ }
+
+ @Override
+ public long getMinDate() {
+ return mMinDate.getTimeInMillis();
+ }
+
+ @Override
+ public void setMaxDate(long maxDate) {
+ mTempDate.setTimeInMillis(maxDate);
+ if (isSameDate(mTempDate, mMaxDate)) {
+ return;
+ }
+ mMaxDate.setTimeInMillis(maxDate);
+ // reinitialize the adapter since its range depends on max date
+ mAdapter.init();
+ Calendar date = mAdapter.mSelectedDate;
+ if (date.after(mMaxDate)) {
+ setDate(mMaxDate.getTimeInMillis());
+ } else {
+ // we go to the current date to force the ListView to query its
+ // adapter for the shown views since we have changed the adapter
+ // range and the base from which the later calculates item indices
+ // note that calling setDate will not work since the date is the same
+ goTo(date, false, true, false);
+ }
+ }
+
+ @Override
+ public long getMaxDate() {
+ return mMaxDate.getTimeInMillis();
+ }
+
+ @Override
+ public void setShowWeekNumber(boolean showWeekNumber) {
+ if (mShowWeekNumber == showWeekNumber) {
+ return;
+ }
+ mShowWeekNumber = showWeekNumber;
+ mAdapter.notifyDataSetChanged();
+ setUpHeader();
+ }
+
+ @Override
+ public boolean getShowWeekNumber() {
+ return mShowWeekNumber;
+ }
+
+ @Override
+ public void setFirstDayOfWeek(int firstDayOfWeek) {
+ if (mFirstDayOfWeek == firstDayOfWeek) {
+ return;
+ }
+ mFirstDayOfWeek = firstDayOfWeek;
+ mAdapter.init();
+ mAdapter.notifyDataSetChanged();
+ setUpHeader();
+ }
+
+ @Override
+ public int getFirstDayOfWeek() {
+ return mFirstDayOfWeek;
+ }
+
+ @Override
+ public void setDate(long date) {
+ setDate(date, false, false);
+ }
+
+ @Override
+ public void setDate(long date, boolean animate, boolean center) {
+ mTempDate.setTimeInMillis(date);
+ if (isSameDate(mTempDate, mAdapter.mSelectedDate)) {
+ return;
+ }
+ goTo(mTempDate, animate, true, center);
+ }
+
+ @Override
+ public long getDate() {
+ return mAdapter.mSelectedDate.getTimeInMillis();
+ }
+
+ @Override
+ public void setOnDateChangeListener(CalendarView.OnDateChangeListener listener) {
+ mOnDateChangeListener = listener;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ setCurrentLocale(newConfig.locale);
+ }
+
+ /**
+ * Sets the current locale.
+ *
+ * @param locale The current locale.
+ */
+ @Override
+ protected void setCurrentLocale(Locale locale) {
+ super.setCurrentLocale(locale);
+
+ mTempDate = getCalendarForLocale(mTempDate, locale);
+ mFirstDayOfMonth = getCalendarForLocale(mFirstDayOfMonth, locale);
+ mMinDate = getCalendarForLocale(mMinDate, locale);
+ mMaxDate = getCalendarForLocale(mMaxDate, locale);
+ }
+ private void updateDateTextSize() {
+ TypedArray dateTextAppearance = mDelegator.getContext().obtainStyledAttributes(
+ mDateTextAppearanceResId, R.styleable.TextAppearance);
+ mDateTextSize = dateTextAppearance.getDimensionPixelSize(
+ R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
+ dateTextAppearance.recycle();
+ }
+
+ /**
+ * Invalidates all week views.
+ */
+ private void invalidateAllWeekViews() {
+ final int childCount = mListView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View view = mListView.getChildAt(i);
+ view.invalidate();
+ }
+ }
+
+ /**
+ * Gets a calendar for locale bootstrapped with the value of a given calendar.
+ *
+ * @param oldCalendar The old calendar.
+ * @param locale The locale.
+ */
+ private static Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
+ if (oldCalendar == null) {
+ return Calendar.getInstance(locale);
+ } else {
+ final long currentTimeMillis = oldCalendar.getTimeInMillis();
+ Calendar newCalendar = Calendar.getInstance(locale);
+ newCalendar.setTimeInMillis(currentTimeMillis);
+ return newCalendar;
+ }
+ }
+
+ /**
+ * @return True if the <code>firstDate</code> is the same as the <code>
+ * secondDate</code>.
+ */
+ private static boolean isSameDate(Calendar firstDate, Calendar secondDate) {
+ return (firstDate.get(Calendar.DAY_OF_YEAR) == secondDate.get(Calendar.DAY_OF_YEAR)
+ && firstDate.get(Calendar.YEAR) == secondDate.get(Calendar.YEAR));
+ }
+
+ /**
+ * Creates a new adapter if necessary and sets up its parameters.
+ */
+ private void setUpAdapter() {
+ if (mAdapter == null) {
+ mAdapter = new WeeksAdapter(mContext);
+ mAdapter.registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ if (mOnDateChangeListener != null) {
+ Calendar selectedDay = mAdapter.getSelectedDay();
+ mOnDateChangeListener.onSelectedDayChange(mDelegator,
+ selectedDay.get(Calendar.YEAR),
+ selectedDay.get(Calendar.MONTH),
+ selectedDay.get(Calendar.DAY_OF_MONTH));
+ }
+ }
+ });
+ mListView.setAdapter(mAdapter);
+ }
+
+ // refresh the view with the new parameters
+ mAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * Sets up the strings to be used by the header.
+ */
+ private void setUpHeader() {
+ mDayNamesShort = new String[mDaysPerWeek];
+ mDayNamesLong = new String[mDaysPerWeek];
+ for (int i = mFirstDayOfWeek, count = mFirstDayOfWeek + mDaysPerWeek; i < count; i++) {
+ int calendarDay = (i > Calendar.SATURDAY) ? i - Calendar.SATURDAY : i;
+ mDayNamesShort[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
+ DateUtils.LENGTH_SHORTEST);
+ mDayNamesLong[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
+ DateUtils.LENGTH_LONG);
+ }
+
+ TextView label = (TextView) mDayNamesHeader.getChildAt(0);
+ if (mShowWeekNumber) {
+ label.setVisibility(View.VISIBLE);
+ } else {
+ label.setVisibility(View.GONE);
+ }
+ for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) {
+ label = (TextView) mDayNamesHeader.getChildAt(i);
+ if (mWeekDayTextAppearanceResId > -1) {
+ label.setTextAppearance(mContext, mWeekDayTextAppearanceResId);
+ }
+ if (i < mDaysPerWeek + 1) {
+ label.setText(mDayNamesShort[i - 1]);
+ label.setContentDescription(mDayNamesLong[i - 1]);
+ label.setVisibility(View.VISIBLE);
+ } else {
+ label.setVisibility(View.GONE);
+ }
+ }
+ mDayNamesHeader.invalidate();
+ }
+
+ /**
+ * Sets all the required fields for the list view.
+ */
+ private void setUpListView() {
+ // Configure the listview
+ mListView.setDivider(null);
+ mListView.setItemsCanFocus(true);
+ mListView.setVerticalScrollBarEnabled(false);
+ mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ CalendarViewLegacyDelegate.this.onScrollStateChanged(view, scrollState);
+ }
+
+ public void onScroll(
+ AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ CalendarViewLegacyDelegate.this.onScroll(view, firstVisibleItem,
+ visibleItemCount, totalItemCount);
+ }
+ });
+ // Make the scrolling behavior nicer
+ mListView.setFriction(mFriction);
+ mListView.setVelocityScale(mVelocityScale);
+ }
+
+ /**
+ * This moves to the specified time in the view. If the time is not already
+ * in range it will move the list so that the first of the month containing
+ * the time is at the top of the view. If the new time is already in view
+ * the list will not be scrolled unless forceScroll is true. This time may
+ * optionally be highlighted as selected as well.
+ *
+ * @param date The time to move to.
+ * @param animate Whether to scroll to the given time or just redraw at the
+ * new location.
+ * @param setSelected Whether to set the given time as selected.
+ * @param forceScroll Whether to recenter even if the time is already
+ * visible.
+ *
+ * @throws IllegalArgumentException of the provided date is before the
+ * range start of after the range end.
+ */
+ private void goTo(Calendar date, boolean animate, boolean setSelected,
+ boolean forceScroll) {
+ if (date.before(mMinDate) || date.after(mMaxDate)) {
+ throw new IllegalArgumentException("Time not between " + mMinDate.getTime()
+ + " and " + mMaxDate.getTime());
+ }
+ // Find the first and last entirely visible weeks
+ int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
+ View firstChild = mListView.getChildAt(0);
+ if (firstChild != null && firstChild.getTop() < 0) {
+ firstFullyVisiblePosition++;
+ }
+ int lastFullyVisiblePosition = firstFullyVisiblePosition + mShownWeekCount - 1;
+ if (firstChild != null && firstChild.getTop() > mBottomBuffer) {
+ lastFullyVisiblePosition--;
+ }
+ if (setSelected) {
+ mAdapter.setSelectedDay(date);
+ }
+ // Get the week we're going to
+ int position = getWeeksSinceMinDate(date);
+
+ // Check if the selected day is now outside of our visible range
+ // and if so scroll to the month that contains it
+ if (position < firstFullyVisiblePosition || position > lastFullyVisiblePosition
+ || forceScroll) {
+ mFirstDayOfMonth.setTimeInMillis(date.getTimeInMillis());
+ mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
+
+ setMonthDisplayed(mFirstDayOfMonth);
+
+ // the earliest time we can scroll to is the min date
+ if (mFirstDayOfMonth.before(mMinDate)) {
+ position = 0;
+ } else {
+ position = getWeeksSinceMinDate(mFirstDayOfMonth);
+ }
+
+ mPreviousScrollState = AbsListView.OnScrollListener.SCROLL_STATE_FLING;
+ if (animate) {
+ mListView.smoothScrollToPositionFromTop(position, mListScrollTopOffset,
+ GOTO_SCROLL_DURATION);
+ } else {
+ mListView.setSelectionFromTop(position, mListScrollTopOffset);
+ // Perform any after scroll operations that are needed
+ onScrollStateChanged(mListView, AbsListView.OnScrollListener.SCROLL_STATE_IDLE);
+ }
+ } else if (setSelected) {
+ // Otherwise just set the selection
+ setMonthDisplayed(date);
+ }
+ }
+
+ /**
+ * Called when a <code>view</code> transitions to a new <code>scrollState
+ * </code>.
+ */
+ private void onScrollStateChanged(AbsListView view, int scrollState) {
+ mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
+ }
+
+ /**
+ * Updates the title and selected month if the <code>view</code> has moved to a new
+ * month.
+ */
+ private void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ WeekView child = (WeekView) view.getChildAt(0);
+ if (child == null) {
+ return;
+ }
+
+ // Figure out where we are
+ long currScroll =
+ view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
+
+ // If we have moved since our last call update the direction
+ if (currScroll < mPreviousScrollPosition) {
+ mIsScrollingUp = true;
+ } else if (currScroll > mPreviousScrollPosition) {
+ mIsScrollingUp = false;
+ } else {
+ return;
+ }
+
+ // Use some hysteresis for checking which month to highlight. This
+ // causes the month to transition when two full weeks of a month are
+ // visible when scrolling up, and when the first day in a month reaches
+ // the top of the screen when scrolling down.
+ int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
+ if (mIsScrollingUp) {
+ child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
+ } else if (offset != 0) {
+ child = (WeekView) view.getChildAt(offset);
+ }
+
+ if (child != null) {
+ // Find out which month we're moving into
+ int month;
+ if (mIsScrollingUp) {
+ month = child.getMonthOfFirstWeekDay();
+ } else {
+ month = child.getMonthOfLastWeekDay();
+ }
+
+ // And how it relates to our current highlighted month
+ int monthDiff;
+ if (mCurrentMonthDisplayed == 11 && month == 0) {
+ monthDiff = 1;
+ } else if (mCurrentMonthDisplayed == 0 && month == 11) {
+ monthDiff = -1;
+ } else {
+ monthDiff = month - mCurrentMonthDisplayed;
+ }
+
+ // Only switch months if we're scrolling away from the currently
+ // selected month
+ if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) {
+ Calendar firstDay = child.getFirstDay();
+ if (mIsScrollingUp) {
+ firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK);
+ } else {
+ firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK);
+ }
+ setMonthDisplayed(firstDay);
+ }
+ }
+ mPreviousScrollPosition = currScroll;
+ mPreviousScrollState = mCurrentScrollState;
+ }
+
+ /**
+ * Sets the month displayed at the top of this view based on time. Override
+ * to add custom events when the title is changed.
+ *
+ * @param calendar A day in the new focus month.
+ */
+ private void setMonthDisplayed(Calendar calendar) {
+ mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
+ mAdapter.setFocusMonth(mCurrentMonthDisplayed);
+ final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
+ | DateUtils.FORMAT_SHOW_YEAR;
+ final long millis = calendar.getTimeInMillis();
+ String newMonthName = DateUtils.formatDateRange(mContext, millis, millis, flags);
+ mMonthName.setText(newMonthName);
+ mMonthName.invalidate();
+ }
+
+ /**
+ * @return Returns the number of weeks between the current <code>date</code>
+ * and the <code>mMinDate</code>.
+ */
+ private int getWeeksSinceMinDate(Calendar date) {
+ if (date.before(mMinDate)) {
+ throw new IllegalArgumentException("fromDate: " + mMinDate.getTime()
+ + " does not precede toDate: " + date.getTime());
+ }
+ long endTimeMillis = date.getTimeInMillis()
+ + date.getTimeZone().getOffset(date.getTimeInMillis());
+ long startTimeMillis = mMinDate.getTimeInMillis()
+ + mMinDate.getTimeZone().getOffset(mMinDate.getTimeInMillis());
+ long dayOffsetMillis = (mMinDate.get(Calendar.DAY_OF_WEEK) - mFirstDayOfWeek)
+ * MILLIS_IN_DAY;
+ return (int) ((endTimeMillis - startTimeMillis + dayOffsetMillis) / MILLIS_IN_WEEK);
+ }
+
+ /**
+ * Command responsible for acting upon scroll state changes.
+ */
+ private class ScrollStateRunnable implements Runnable {
+ private AbsListView mView;
+
+ private int mNewState;
+
+ /**
+ * Sets up the runnable with a short delay in case the scroll state
+ * immediately changes again.
+ *
+ * @param view The list view that changed state
+ * @param scrollState The new state it changed to
+ */
+ public void doScrollStateChange(AbsListView view, int scrollState) {
+ mView = view;
+ mNewState = scrollState;
+ mDelegator.removeCallbacks(this);
+ mDelegator.postDelayed(this, SCROLL_CHANGE_DELAY);
+ }
+
+ public void run() {
+ mCurrentScrollState = mNewState;
+ // Fix the position after a scroll or a fling ends
+ if (mNewState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE
+ && mPreviousScrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
+ View child = mView.getChildAt(0);
+ if (child == null) {
+ // The view is no longer visible, just return
+ return;
+ }
+ int dist = child.getBottom() - mListScrollTopOffset;
+ if (dist > mListScrollTopOffset) {
+ if (mIsScrollingUp) {
+ mView.smoothScrollBy(dist - child.getHeight(),
+ ADJUSTMENT_SCROLL_DURATION);
+ } else {
+ mView.smoothScrollBy(dist, ADJUSTMENT_SCROLL_DURATION);
+ }
+ }
+ }
+ mPreviousScrollState = mNewState;
+ }
+ }
+
+ /**
+ * <p>
+ * This is a specialized adapter for creating a list of weeks with
+ * selectable days. It can be configured to display the week number, start
+ * the week on a given day, show a reduced number of days, or display an
+ * arbitrary number of weeks at a time.
+ * </p>
+ */
+ private class WeeksAdapter extends BaseAdapter implements View.OnTouchListener {
+
+ private int mSelectedWeek;
+
+ private GestureDetector mGestureDetector;
+
+ private int mFocusedMonth;
+
+ private final Calendar mSelectedDate = Calendar.getInstance();
+
+ private int mTotalWeekCount;
+
+ public WeeksAdapter(Context context) {
+ mContext = context;
+ mGestureDetector = new GestureDetector(mContext, new WeeksAdapter.CalendarGestureListener());
+ init();
+ }
+
+ /**
+ * Set up the gesture detector and selected time
+ */
+ private void init() {
+ mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
+ mTotalWeekCount = getWeeksSinceMinDate(mMaxDate);
+ if (mMinDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek
+ || mMaxDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek) {
+ mTotalWeekCount++;
+ }
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Updates the selected day and related parameters.
+ *
+ * @param selectedDay The time to highlight
+ */
+ public void setSelectedDay(Calendar selectedDay) {
+ if (selectedDay.get(Calendar.DAY_OF_YEAR) == mSelectedDate.get(Calendar.DAY_OF_YEAR)
+ && selectedDay.get(Calendar.YEAR) == mSelectedDate.get(Calendar.YEAR)) {
+ return;
+ }
+ mSelectedDate.setTimeInMillis(selectedDay.getTimeInMillis());
+ mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
+ mFocusedMonth = mSelectedDate.get(Calendar.MONTH);
+ notifyDataSetChanged();
+ }
+
+ /**
+ * @return The selected day of month.
+ */
+ public Calendar getSelectedDay() {
+ return mSelectedDate;
+ }
+
+ @Override
+ public int getCount() {
+ return mTotalWeekCount;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ WeekView weekView = null;
+ if (convertView != null) {
+ weekView = (WeekView) convertView;
+ } else {
+ weekView = new WeekView(mContext);
+ AbsListView.LayoutParams params =
+ new AbsListView.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT);
+ weekView.setLayoutParams(params);
+ weekView.setClickable(true);
+ weekView.setOnTouchListener(this);
+ }
+
+ int selectedWeekDay = (mSelectedWeek == position) ? mSelectedDate.get(
+ Calendar.DAY_OF_WEEK) : -1;
+ weekView.init(position, selectedWeekDay, mFocusedMonth);
+
+ return weekView;
+ }
+
+ /**
+ * Changes which month is in focus and updates the view.
+ *
+ * @param month The month to show as in focus [0-11]
+ */
+ public void setFocusMonth(int month) {
+ if (mFocusedMonth == month) {
+ return;
+ }
+ mFocusedMonth = month;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (mListView.isEnabled() && mGestureDetector.onTouchEvent(event)) {
+ WeekView weekView = (WeekView) v;
+ // if we cannot find a day for the given location we are done
+ if (!weekView.getDayFromLocation(event.getX(), mTempDate)) {
+ return true;
+ }
+ // it is possible that the touched day is outside the valid range
+ // we draw whole weeks but range end can fall not on the week end
+ if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
+ return true;
+ }
+ onDateTapped(mTempDate);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Maintains the same hour/min/sec but moves the day to the tapped day.
+ *
+ * @param day The day that was tapped
+ */
+ private void onDateTapped(Calendar day) {
+ setSelectedDay(day);
+ setMonthDisplayed(day);
+ }
+
+ /**
+ * This is here so we can identify single tap events and set the
+ * selected day correctly
+ */
+ class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * This is a dynamic view for drawing a single week. It can be configured to
+ * display the week number, start the week on a given day, or show a reduced
+ * number of days. It is intended for use as a single view within a
+ * ListView. See {@link WeeksAdapter} for usage.
+ * </p>
+ */
+ private class WeekView extends View {
+
+ private final Rect mTempRect = new Rect();
+
+ private final Paint mDrawPaint = new Paint();
+
+ private final Paint mMonthNumDrawPaint = new Paint();
+
+ // Cache the number strings so we don't have to recompute them each time
+ private String[] mDayNumbers;
+
+ // Quick lookup for checking which days are in the focus month
+ private boolean[] mFocusDay;
+
+ // Whether this view has a focused day.
+ private boolean mHasFocusedDay;
+
+ // Whether this view has only focused days.
+ private boolean mHasUnfocusedDay;
+
+ // The first day displayed by this item
+ private Calendar mFirstDay;
+
+ // The month of the first day in this week
+ private int mMonthOfFirstWeekDay = -1;
+
+ // The month of the last day in this week
+ private int mLastWeekDayMonth = -1;
+
+ // The position of this week, equivalent to weeks since the week of Jan
+ // 1st, 1900
+ private int mWeek = -1;
+
+ // Quick reference to the width of this view, matches parent
+ private int mWidth;
+
+ // The height this view should draw at in pixels, set by height param
+ private int mHeight;
+
+ // If this view contains the selected day
+ private boolean mHasSelectedDay = false;
+
+ // Which day is selected [0-6] or -1 if no day is selected
+ private int mSelectedDay = -1;
+
+ // The number of days + a spot for week number if it is displayed
+ private int mNumCells;
+
+ // The left edge of the selected day
+ private int mSelectedLeft = -1;
+
+ // The right edge of the selected day
+ private int mSelectedRight = -1;
+
+ public WeekView(Context context) {
+ super(context);
+
+ // Sets up any standard paints that will be used
+ initilaizePaints();
+ }
+
+ /**
+ * Initializes this week view.
+ *
+ * @param weekNumber The number of the week this view represents. The
+ * week number is a zero based index of the weeks since
+ * {@link android.widget.CalendarView#getMinDate()}.
+ * @param selectedWeekDay The selected day of the week from 0 to 6, -1 if no
+ * selected day.
+ * @param focusedMonth The month that is currently in focus i.e.
+ * highlighted.
+ */
+ public void init(int weekNumber, int selectedWeekDay, int focusedMonth) {
+ mSelectedDay = selectedWeekDay;
+ mHasSelectedDay = mSelectedDay != -1;
+ mNumCells = mShowWeekNumber ? mDaysPerWeek + 1 : mDaysPerWeek;
+ mWeek = weekNumber;
+ mTempDate.setTimeInMillis(mMinDate.getTimeInMillis());
+
+ mTempDate.add(Calendar.WEEK_OF_YEAR, mWeek);
+ mTempDate.setFirstDayOfWeek(mFirstDayOfWeek);
+
+ // Allocate space for caching the day numbers and focus values
+ mDayNumbers = new String[mNumCells];
+ mFocusDay = new boolean[mNumCells];
+
+ // If we're showing the week number calculate it based on Monday
+ int i = 0;
+ if (mShowWeekNumber) {
+ mDayNumbers[0] = String.format(Locale.getDefault(), "%d",
+ mTempDate.get(Calendar.WEEK_OF_YEAR));
+ i++;
+ }
+
+ // Now adjust our starting day based on the start day of the week
+ int diff = mFirstDayOfWeek - mTempDate.get(Calendar.DAY_OF_WEEK);
+ mTempDate.add(Calendar.DAY_OF_MONTH, diff);
+
+ mFirstDay = (Calendar) mTempDate.clone();
+ mMonthOfFirstWeekDay = mTempDate.get(Calendar.MONTH);
+
+ mHasUnfocusedDay = true;
+ for (; i < mNumCells; i++) {
+ final boolean isFocusedDay = (mTempDate.get(Calendar.MONTH) == focusedMonth);
+ mFocusDay[i] = isFocusedDay;
+ mHasFocusedDay |= isFocusedDay;
+ mHasUnfocusedDay &= !isFocusedDay;
+ // do not draw dates outside the valid range to avoid user confusion
+ if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
+ mDayNumbers[i] = "";
+ } else {
+ mDayNumbers[i] = String.format(Locale.getDefault(), "%d",
+ mTempDate.get(Calendar.DAY_OF_MONTH));
+ }
+ mTempDate.add(Calendar.DAY_OF_MONTH, 1);
+ }
+ // We do one extra add at the end of the loop, if that pushed us to
+ // new month undo it
+ if (mTempDate.get(Calendar.DAY_OF_MONTH) == 1) {
+ mTempDate.add(Calendar.DAY_OF_MONTH, -1);
+ }
+ mLastWeekDayMonth = mTempDate.get(Calendar.MONTH);
+
+ updateSelectionPositions();
+ }
+
+ /**
+ * Initialize the paint instances.
+ */
+ private void initilaizePaints() {
+ mDrawPaint.setFakeBoldText(false);
+ mDrawPaint.setAntiAlias(true);
+ mDrawPaint.setStyle(Paint.Style.FILL);
+
+ mMonthNumDrawPaint.setFakeBoldText(true);
+ mMonthNumDrawPaint.setAntiAlias(true);
+ mMonthNumDrawPaint.setStyle(Paint.Style.FILL);
+ mMonthNumDrawPaint.setTextAlign(Paint.Align.CENTER);
+ mMonthNumDrawPaint.setTextSize(mDateTextSize);
+ }
+
+ /**
+ * Returns the month of the first day in this week.
+ *
+ * @return The month the first day of this view is in.
+ */
+ public int getMonthOfFirstWeekDay() {
+ return mMonthOfFirstWeekDay;
+ }
+
+ /**
+ * Returns the month of the last day in this week
+ *
+ * @return The month the last day of this view is in
+ */
+ public int getMonthOfLastWeekDay() {
+ return mLastWeekDayMonth;
+ }
+
+ /**
+ * Returns the first day in this view.
+ *
+ * @return The first day in the view.
+ */
+ public Calendar getFirstDay() {
+ return mFirstDay;
+ }
+
+ /**
+ * Calculates the day that the given x position is in, accounting for
+ * week number.
+ *
+ * @param x The x position of the touch event.
+ * @return True if a day was found for the given location.
+ */
+ public boolean getDayFromLocation(float x, Calendar outCalendar) {
+ final boolean isLayoutRtl = isLayoutRtl();
+
+ int start;
+ int end;
+
+ if (isLayoutRtl) {
+ start = 0;
+ end = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
+ } else {
+ start = mShowWeekNumber ? mWidth / mNumCells : 0;
+ end = mWidth;
+ }
+
+ if (x < start || x > end) {
+ outCalendar.clear();
+ return false;
+ }
+
+ // Selection is (x - start) / (pixels/day) which is (x - start) * day / pixels
+ int dayPosition = (int) ((x - start) * mDaysPerWeek / (end - start));
+
+ if (isLayoutRtl) {
+ dayPosition = mDaysPerWeek - 1 - dayPosition;
+ }
+
+ outCalendar.setTimeInMillis(mFirstDay.getTimeInMillis());
+ outCalendar.add(Calendar.DAY_OF_MONTH, dayPosition);
+
+ return true;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ drawBackground(canvas);
+ drawWeekNumbersAndDates(canvas);
+ drawWeekSeparators(canvas);
+ drawSelectedDateVerticalBars(canvas);
+ }
+
+ /**
+ * This draws the selection highlight if a day is selected in this week.
+ *
+ * @param canvas The canvas to draw on
+ */
+ private void drawBackground(Canvas canvas) {
+ if (!mHasSelectedDay) {
+ return;
+ }
+ mDrawPaint.setColor(mSelectedWeekBackgroundColor);
+
+ mTempRect.top = mWeekSeperatorLineWidth;
+ mTempRect.bottom = mHeight;
+
+ final boolean isLayoutRtl = isLayoutRtl();
+
+ if (isLayoutRtl) {
+ mTempRect.left = 0;
+ mTempRect.right = mSelectedLeft - 2;
+ } else {
+ mTempRect.left = mShowWeekNumber ? mWidth / mNumCells : 0;
+ mTempRect.right = mSelectedLeft - 2;
+ }
+ canvas.drawRect(mTempRect, mDrawPaint);
+
+ if (isLayoutRtl) {
+ mTempRect.left = mSelectedRight + 3;
+ mTempRect.right = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
+ } else {
+ mTempRect.left = mSelectedRight + 3;
+ mTempRect.right = mWidth;
+ }
+ canvas.drawRect(mTempRect, mDrawPaint);
+ }
+
+ /**
+ * Draws the week and month day numbers for this week.
+ *
+ * @param canvas The canvas to draw on
+ */
+ private void drawWeekNumbersAndDates(Canvas canvas) {
+ final float textHeight = mDrawPaint.getTextSize();
+ final int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorLineWidth;
+ final int nDays = mNumCells;
+ final int divisor = 2 * nDays;
+
+ mDrawPaint.setTextAlign(Paint.Align.CENTER);
+ mDrawPaint.setTextSize(mDateTextSize);
+
+ int i = 0;
+
+ if (isLayoutRtl()) {
+ for (; i < nDays - 1; i++) {
+ mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
+ : mUnfocusedMonthDateColor);
+ int x = (2 * i + 1) * mWidth / divisor;
+ canvas.drawText(mDayNumbers[nDays - 1 - i], x, y, mMonthNumDrawPaint);
+ }
+ if (mShowWeekNumber) {
+ mDrawPaint.setColor(mWeekNumberColor);
+ int x = mWidth - mWidth / divisor;
+ canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
+ }
+ } else {
+ if (mShowWeekNumber) {
+ mDrawPaint.setColor(mWeekNumberColor);
+ int x = mWidth / divisor;
+ canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
+ i++;
+ }
+ for (; i < nDays; i++) {
+ mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
+ : mUnfocusedMonthDateColor);
+ int x = (2 * i + 1) * mWidth / divisor;
+ canvas.drawText(mDayNumbers[i], x, y, mMonthNumDrawPaint);
+ }
+ }
+ }
+
+ /**
+ * Draws a horizontal line for separating the weeks.
+ *
+ * @param canvas The canvas to draw on.
+ */
+ private void drawWeekSeparators(Canvas canvas) {
+ // If it is the topmost fully visible child do not draw separator line
+ int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
+ if (mListView.getChildAt(0).getTop() < 0) {
+ firstFullyVisiblePosition++;
+ }
+ if (firstFullyVisiblePosition == mWeek) {
+ return;
+ }
+ mDrawPaint.setColor(mWeekSeparatorLineColor);
+ mDrawPaint.setStrokeWidth(mWeekSeperatorLineWidth);
+ float startX;
+ float stopX;
+ if (isLayoutRtl()) {
+ startX = 0;
+ stopX = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
+ } else {
+ startX = mShowWeekNumber ? mWidth / mNumCells : 0;
+ stopX = mWidth;
+ }
+ canvas.drawLine(startX, 0, stopX, 0, mDrawPaint);
+ }
+
+ /**
+ * Draws the selected date bars if this week has a selected day.
+ *
+ * @param canvas The canvas to draw on
+ */
+ private void drawSelectedDateVerticalBars(Canvas canvas) {
+ if (!mHasSelectedDay) {
+ return;
+ }
+ mSelectedDateVerticalBar.setBounds(
+ mSelectedLeft - mSelectedDateVerticalBarWidth / 2,
+ mWeekSeperatorLineWidth,
+ mSelectedLeft + mSelectedDateVerticalBarWidth / 2,
+ mHeight);
+ mSelectedDateVerticalBar.draw(canvas);
+ mSelectedDateVerticalBar.setBounds(
+ mSelectedRight - mSelectedDateVerticalBarWidth / 2,
+ mWeekSeperatorLineWidth,
+ mSelectedRight + mSelectedDateVerticalBarWidth / 2,
+ mHeight);
+ mSelectedDateVerticalBar.draw(canvas);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ mWidth = w;
+ updateSelectionPositions();
+ }
+
+ /**
+ * This calculates the positions for the selected day lines.
+ */
+ private void updateSelectionPositions() {
+ if (mHasSelectedDay) {
+ final boolean isLayoutRtl = isLayoutRtl();
+ int selectedPosition = mSelectedDay - mFirstDayOfWeek;
+ if (selectedPosition < 0) {
+ selectedPosition += 7;
+ }
+ if (mShowWeekNumber && !isLayoutRtl) {
+ selectedPosition++;
+ }
+ if (isLayoutRtl) {
+ mSelectedLeft = (mDaysPerWeek - 1 - selectedPosition) * mWidth / mNumCells;
+
+ } else {
+ mSelectedLeft = selectedPosition * mWidth / mNumCells;
+ }
+ mSelectedRight = mSelectedLeft + mWidth / mNumCells;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mHeight = (mListView.getHeight() - mListView.getPaddingTop() - mListView
+ .getPaddingBottom()) / mShownWeekCount;
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
+ }
+ }
+
+}
diff --git a/core/java/android/widget/CalendarViewMaterialDelegate.java b/core/java/android/widget/CalendarViewMaterialDelegate.java
new file mode 100644
index 0000000..b0f3740
--- /dev/null
+++ b/core/java/android/widget/CalendarViewMaterialDelegate.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2014 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 android.widget;
+
+import com.android.internal.R;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.MathUtils;
+
+import java.util.Calendar;
+import java.util.Locale;
+
+import libcore.icu.LocaleData;
+
+class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDelegate {
+ private final DayPickerView mDayPickerView;
+
+ private CalendarView.OnDateChangeListener mOnDateChangeListener;
+
+ public CalendarViewMaterialDelegate(CalendarView delegator, Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(delegator, context);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.CalendarView, defStyleAttr, defStyleRes);
+ final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
+ LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+
+ final long minDate = parseDateToMillis(a.getString(
+ R.styleable.CalendarView_minDate), DEFAULT_MIN_DATE);
+ final long maxDate = parseDateToMillis(a.getString(
+ R.styleable.CalendarView_maxDate), DEFAULT_MAX_DATE);
+ if (maxDate < minDate) {
+ throw new IllegalArgumentException("max date cannot be before min date");
+ }
+
+ final long setDate = MathUtils.constrain(System.currentTimeMillis(), minDate, maxDate);
+ final int dateTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_dateTextAppearance,
+ R.style.TextAppearance_DeviceDefault_Small);
+
+ a.recycle();
+
+ mDayPickerView = new DayPickerView(context);
+ mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
+ mDayPickerView.setCalendarTextAppearance(dateTextAppearanceResId);
+ mDayPickerView.setMinDate(minDate);
+ mDayPickerView.setMaxDate(maxDate);
+ mDayPickerView.setDate(setDate, false, true);
+ mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
+
+ delegator.addView(mDayPickerView);
+ }
+
+ private long parseDateToMillis(String dateStr, String defaultDateStr) {
+ final Calendar tempCalendar = Calendar.getInstance();
+ if (TextUtils.isEmpty(dateStr) || !parseDate(dateStr, tempCalendar)) {
+ parseDate(defaultDateStr, tempCalendar);
+ }
+ return tempCalendar.getTimeInMillis();
+ }
+
+ @Override
+ public void setShownWeekCount(int count) {
+ // Deprecated.
+ }
+
+ @Override
+ public int getShownWeekCount() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setSelectedWeekBackgroundColor(int color) {
+ // TODO: Should use a ColorStateList. Deprecate?
+ }
+
+ @Override
+ public int getSelectedWeekBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public void setFocusedMonthDateColor(int color) {
+ // TODO: Should use a ColorStateList. Deprecate?
+ }
+
+ @Override
+ public int getFocusedMonthDateColor() {
+ return 0;
+ }
+
+ @Override
+ public void setUnfocusedMonthDateColor(int color) {
+ // TODO: Should use a ColorStateList. Deprecate?
+ }
+
+ @Override
+ public int getUnfocusedMonthDateColor() {
+ return 0;
+ }
+
+ @Override
+ public void setWeekDayTextAppearance(int resourceId) {
+
+ }
+
+ @Override
+ public int getWeekDayTextAppearance() {
+ return 0;
+ }
+
+ @Override
+ public void setDateTextAppearance(int resourceId) {
+
+ }
+
+ @Override
+ public int getDateTextAppearance() {
+ return 0;
+ }
+
+ @Override
+ public void setWeekNumberColor(int color) {
+ // Deprecated.
+ }
+
+ @Override
+ public int getWeekNumberColor() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setWeekSeparatorLineColor(int color) {
+ // Deprecated.
+ }
+
+ @Override
+ public int getWeekSeparatorLineColor() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(int resourceId) {
+ // Deprecated.
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(Drawable drawable) {
+ // Deprecated.
+ }
+
+ @Override
+ public Drawable getSelectedDateVerticalBar() {
+ // Deprecated.
+ return null;
+ }
+
+ @Override
+ public void setMinDate(long minDate) {
+ mDayPickerView.setMinDate(minDate);
+ }
+
+ @Override
+ public long getMinDate() {
+ return mDayPickerView.getMinDate();
+ }
+
+ @Override
+ public void setMaxDate(long maxDate) {
+ mDayPickerView.setMaxDate(maxDate);
+ }
+
+ @Override
+ public long getMaxDate() {
+ return mDayPickerView.getMaxDate();
+ }
+
+ @Override
+ public void setShowWeekNumber(boolean showWeekNumber) {
+ // Deprecated.
+ }
+
+ @Override
+ public boolean getShowWeekNumber() {
+ // Deprecated.
+ return false;
+ }
+
+ @Override
+ public void setFirstDayOfWeek(int firstDayOfWeek) {
+ mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
+ }
+
+ @Override
+ public int getFirstDayOfWeek() {
+ return mDayPickerView.getFirstDayOfWeek();
+ }
+
+ @Override
+ public void setDate(long date) {
+ mDayPickerView.setDate(date, true, false);
+ }
+
+ @Override
+ public void setDate(long date, boolean animate, boolean center) {
+ mDayPickerView.setDate(date, animate, center);
+ }
+
+ @Override
+ public long getDate() {
+ return mDayPickerView.getDate();
+ }
+
+ @Override
+ public void setOnDateChangeListener(CalendarView.OnDateChangeListener listener) {
+ mOnDateChangeListener = listener;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Nothing to do here, configuration changes are already propagated
+ // by ViewGroup.
+ }
+
+ private final DayPickerView.OnDaySelectedListener mOnDaySelectedListener =
+ new DayPickerView.OnDaySelectedListener() {
+ @Override
+ public void onDaySelected(DayPickerView view, Calendar day) {
+ if (mOnDateChangeListener != null) {
+ final int year = day.get(Calendar.YEAR);
+ final int month = day.get(Calendar.MONTH);
+ final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
+ mOnDateChangeListener.onSelectedDayChange(mDelegator, year, month, dayOfMonth);
+ }
+ }
+ };
+}
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index cf3dbab..820bf78 100644
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -185,8 +185,9 @@
mDayPickerView = new DayPickerView(mContext);
mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek);
- mDayPickerView.setRange(mMinDate, mMaxDate);
- mDayPickerView.setDay(mCurrentDate);
+ mDayPickerView.setMinDate(mMinDate.getTimeInMillis());
+ mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis());
+ mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
mYearPickerView = new YearPickerView(mContext);
@@ -336,7 +337,7 @@
switch (viewIndex) {
case MONTH_AND_DAY_VIEW:
- mDayPickerView.setDay(getSelectedDay());
+ mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
if (mCurrentView != viewIndex) {
mMonthAndDayLayout.setSelected(true);
mHeaderYearTextView.setSelected(false);
@@ -414,7 +415,7 @@
updateDisplay(false);
}
mMinDate.setTimeInMillis(minDate);
- mDayPickerView.setRange(mMinDate, mMaxDate);
+ mDayPickerView.setMinDate(minDate);
mYearPickerView.setRange(mMinDate, mMaxDate);
}
@@ -436,7 +437,7 @@
updateDisplay(false);
}
mMaxDate.setTimeInMillis(maxDate);
- mDayPickerView.setRange(mMinDate, mMaxDate);
+ mDayPickerView.setMaxDate(maxDate);
mYearPickerView.setRange(mMinDate, mMaxDate);
}
@@ -616,7 +617,7 @@
listener.onDateChanged();
}
- mDayPickerView.setDay(getSelectedDay());
+ mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
}
@Override
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 6cb1c9d..7db3fb9 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -58,6 +58,8 @@
private Calendar mMinDate = Calendar.getInstance();
private Calendar mMaxDate = Calendar.getInstance();
+ private Calendar mTempCalendar;
+
private OnDaySelectedListener mOnDaySelectedListener;
// which month should be displayed/highlighted [0-11]
@@ -77,28 +79,65 @@
setDrawSelectorOnTop(false);
setUpListView();
- goTo(mSelectedDay, false, true, true);
+ goTo(mSelectedDay.getTimeInMillis(), false, false, true);
mAdapter.setOnDaySelectedListener(mProxyOnDaySelectedListener);
}
- public void setDay(Calendar day) {
- goTo(day, false, true, true);
+ /**
+ * Sets the currently selected date to the specified timestamp. Jumps
+ * immediately to the new date. To animate to the new date, use
+ * {@link #setDate(long, boolean, boolean)}.
+ *
+ * @param timeInMillis
+ */
+ public void setDate(long timeInMillis) {
+ setDate(timeInMillis, false, true);
+ }
+
+ public void setDate(long timeInMillis, boolean animate, boolean forceScroll) {
+ goTo(timeInMillis, animate, true, forceScroll);
+ }
+
+ public long getDate() {
+ return mSelectedDay.getTimeInMillis();
}
public void setFirstDayOfWeek(int firstDayOfWeek) {
mAdapter.setFirstDayOfWeek(firstDayOfWeek);
}
- public void setRange(Calendar minDate, Calendar maxDate) {
- mMinDate.setTimeInMillis(minDate.getTimeInMillis());
- mMaxDate.setTimeInMillis(maxDate.getTimeInMillis());
+ public int getFirstDayOfWeek() {
+ return mAdapter.getFirstDayOfWeek();
+ }
+ public void setMinDate(long timeInMillis) {
+ mMinDate.setTimeInMillis(timeInMillis);
+ onRangeChanged();
+ }
+
+ public long getMinDate() {
+ return mMinDate.getTimeInMillis();
+ }
+
+ public void setMaxDate(long timeInMillis) {
+ mMaxDate.setTimeInMillis(timeInMillis);
+ onRangeChanged();
+ }
+
+ public long getMaxDate() {
+ return mMaxDate.getTimeInMillis();
+ }
+
+ /**
+ * Handles changes to date range.
+ */
+ public void onRangeChanged() {
mAdapter.setRange(mMinDate, mMaxDate);
// Changing the min/max date changes the selection position since we
- // don't really have stable IDs.
- goTo(mSelectedDay, false, true, true);
+ // don't really have stable IDs. Jumps immediately to the new position.
+ goTo(mSelectedDay.getTimeInMillis(), false, false, true);
}
/**
@@ -136,12 +175,20 @@
return diffMonths;
}
- private int getPositionFromDay(Calendar day) {
+ private int getPositionFromDay(long timeInMillis) {
final int diffMonthMax = getDiffMonths(mMinDate, mMaxDate);
- final int diffMonth = getDiffMonths(mMinDate, day);
+ final int diffMonth = getDiffMonths(mMinDate, getTempCalendarForTime(timeInMillis));
return MathUtils.constrain(diffMonth, 0, diffMonthMax);
}
+ private Calendar getTempCalendarForTime(long timeInMillis) {
+ if (mTempCalendar == null) {
+ mTempCalendar = Calendar.getInstance();
+ }
+ mTempCalendar.setTimeInMillis(timeInMillis);
+ return mTempCalendar;
+ }
+
/**
* This moves to the specified time in the view. If the time is not already
* in range it will move the list so that the first of the month containing
@@ -157,14 +204,14 @@
* visible
* @return Whether or not the view animated to the new location
*/
- private boolean goTo(Calendar day, boolean animate, boolean setSelected, boolean forceScroll) {
+ private boolean goTo(long day, boolean animate, boolean setSelected, boolean forceScroll) {
// Set the selected day
if (setSelected) {
- mSelectedDay.setTimeInMillis(day.getTimeInMillis());
+ mSelectedDay.setTimeInMillis(day);
}
- mTempDay.setTimeInMillis(day.getTimeInMillis());
+ mTempDay.setTimeInMillis(day);
final int position = getPositionFromDay(day);
View child;
@@ -258,6 +305,10 @@
mAdapter.setCalendarTextColor(colors);
}
+ void setCalendarTextAppearance(int resId) {
+ mAdapter.setCalendarTextAppearance(resId);
+ }
+
protected class ScrollStateRunnable implements Runnable {
private int mNewState;
private View mParent;
@@ -415,7 +466,7 @@
}
private String getMonthAndYearString(Calendar day) {
- StringBuffer sbuf = new StringBuffer();
+ final StringBuilder sbuf = new StringBuilder();
sbuf.append(day.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()));
sbuf.append(" ");
sbuf.append(mYearFormat.format(day.getTime()));
@@ -429,8 +480,8 @@
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
}
/**
@@ -474,7 +525,7 @@
// Go to that month.
announceForAccessibility(getMonthAndYearString(day));
- goTo(day, true, false, true);
+ goTo(day.getTimeInMillis(), true, false, true);
mPerformingScroll = true;
return true;
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index dfdf606..4ee6418 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -45,7 +45,6 @@
import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.TypedValue;
import android.view.CollapsibleActionView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -99,17 +98,21 @@
*/
private static final String IME_OPTION_NO_MICROPHONE = "nm";
- private final SearchAutoComplete mQueryTextView;
+ private final SearchAutoComplete mSearchSrcTextView;
private final View mSearchEditFrame;
private final View mSearchPlate;
private final View mSubmitArea;
private final ImageView mSearchButton;
- private final ImageView mSubmitButton;
+ private final ImageView mGoButton;
private final ImageView mCloseButton;
private final ImageView mVoiceButton;
- private final ImageView mSearchHintIcon;
private final View mDropDownAnchor;
- private final int mSearchIconResId;
+
+ /** Icon optionally displayed when the SearchView is collapsed. */
+ private final ImageView mCollapsedIcon;
+
+ /** Drawable used as an EditText hint. */
+ private final Drawable mSearchHintIcon;
// Resources used by SuggestionsAdapter to display suggestions.
private final int mSuggestionRowLayout;
@@ -262,30 +265,38 @@
attrs, R.styleable.SearchView, defStyleAttr, defStyleRes);
final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
- final int layoutResId = a.getResourceId(R.styleable.SearchView_layout, R.layout.search_view);
+ final int layoutResId = a.getResourceId(
+ R.styleable.SearchView_layout, R.layout.search_view);
inflater.inflate(layoutResId, this, true);
- mQueryTextView = (SearchAutoComplete) findViewById(R.id.search_src_text);
- mQueryTextView.setSearchView(this);
+ mSearchSrcTextView = (SearchAutoComplete) findViewById(R.id.search_src_text);
+ mSearchSrcTextView.setSearchView(this);
mSearchEditFrame = findViewById(R.id.search_edit_frame);
mSearchPlate = findViewById(R.id.search_plate);
mSubmitArea = findViewById(R.id.submit_area);
mSearchButton = (ImageView) findViewById(R.id.search_button);
- mSubmitButton = (ImageView) findViewById(R.id.search_go_btn);
+ mGoButton = (ImageView) findViewById(R.id.search_go_btn);
mCloseButton = (ImageView) findViewById(R.id.search_close_btn);
mVoiceButton = (ImageView) findViewById(R.id.search_voice_btn);
- mSearchHintIcon = (ImageView) findViewById(R.id.search_mag_icon);
+ mCollapsedIcon = (ImageView) findViewById(R.id.search_mag_icon);
// Set up icons and backgrounds.
mSearchPlate.setBackground(a.getDrawable(R.styleable.SearchView_queryBackground));
mSubmitArea.setBackground(a.getDrawable(R.styleable.SearchView_submitBackground));
- mSearchIconResId = a.getResourceId(R.styleable.SearchView_searchIcon, 0);
- mSearchButton.setImageResource(mSearchIconResId);
- mSubmitButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_goIcon));
+ mSearchButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+ mGoButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_goIcon));
mCloseButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_closeIcon));
mVoiceButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_voiceIcon));
- mSearchHintIcon.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+ mCollapsedIcon.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
+
+ // Prior to L MR1, the search hint icon defaulted to searchIcon. If the
+ // style does not have an explicit value set, fall back to that.
+ if (a.hasValueOrEmpty(R.styleable.SearchView_searchHintIcon)) {
+ mSearchHintIcon = a.getDrawable(R.styleable.SearchView_searchHintIcon);
+ } else {
+ mSearchHintIcon = a.getDrawable(R.styleable.SearchView_searchIcon);
+ }
// Extract dropdown layout resource IDs for later use.
mSuggestionRowLayout = a.getResourceId(R.styleable.SearchView_suggestionRowLayout,
@@ -294,18 +305,18 @@
mSearchButton.setOnClickListener(mOnClickListener);
mCloseButton.setOnClickListener(mOnClickListener);
- mSubmitButton.setOnClickListener(mOnClickListener);
+ mGoButton.setOnClickListener(mOnClickListener);
mVoiceButton.setOnClickListener(mOnClickListener);
- mQueryTextView.setOnClickListener(mOnClickListener);
+ mSearchSrcTextView.setOnClickListener(mOnClickListener);
- mQueryTextView.addTextChangedListener(mTextWatcher);
- mQueryTextView.setOnEditorActionListener(mOnEditorActionListener);
- mQueryTextView.setOnItemClickListener(mOnItemClickListener);
- mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
- mQueryTextView.setOnKeyListener(mTextKeyListener);
+ mSearchSrcTextView.addTextChangedListener(mTextWatcher);
+ mSearchSrcTextView.setOnEditorActionListener(mOnEditorActionListener);
+ mSearchSrcTextView.setOnItemClickListener(mOnItemClickListener);
+ mSearchSrcTextView.setOnItemSelectedListener(mOnItemSelectedListener);
+ mSearchSrcTextView.setOnKeyListener(mTextKeyListener);
// Inform any listener of focus changes
- mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
+ mSearchSrcTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (mOnQueryTextFocusChangeListener != null) {
@@ -350,7 +361,7 @@
mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor());
+ mDropDownAnchor = findViewById(mSearchSrcTextView.getDropDownAnchor());
if (mDropDownAnchor != null) {
mDropDownAnchor.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
@@ -393,7 +404,7 @@
if (mVoiceButtonEnabled) {
// Disable the microphone on the keyboard, as a mic is displayed near the text box
// TODO: use imeOptions to disable voice input when the new API will be available
- mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
+ mSearchSrcTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
}
updateViewsVisibility(isIconified());
}
@@ -416,7 +427,7 @@
* @attr ref android.R.styleable#SearchView_imeOptions
*/
public void setImeOptions(int imeOptions) {
- mQueryTextView.setImeOptions(imeOptions);
+ mSearchSrcTextView.setImeOptions(imeOptions);
}
/**
@@ -427,7 +438,7 @@
* @attr ref android.R.styleable#SearchView_imeOptions
*/
public int getImeOptions() {
- return mQueryTextView.getImeOptions();
+ return mSearchSrcTextView.getImeOptions();
}
/**
@@ -439,7 +450,7 @@
* @attr ref android.R.styleable#SearchView_inputType
*/
public void setInputType(int inputType) {
- mQueryTextView.setInputType(inputType);
+ mSearchSrcTextView.setInputType(inputType);
}
/**
@@ -449,7 +460,7 @@
* @attr ref android.R.styleable#SearchView_inputType
*/
public int getInputType() {
- return mQueryTextView.getInputType();
+ return mSearchSrcTextView.getInputType();
}
/** @hide */
@@ -461,7 +472,7 @@
if (!isFocusable()) return false;
// If it is not iconified, then give the focus to the text field
if (!isIconified()) {
- boolean result = mQueryTextView.requestFocus(direction, previouslyFocusedRect);
+ boolean result = mSearchSrcTextView.requestFocus(direction, previouslyFocusedRect);
if (result) {
updateViewsVisibility(false);
}
@@ -477,7 +488,7 @@
mClearingFocus = true;
setImeVisibility(false);
super.clearFocus();
- mQueryTextView.clearFocus();
+ mSearchSrcTextView.clearFocus();
mClearingFocus = false;
}
@@ -536,7 +547,7 @@
* @return the query string
*/
public CharSequence getQuery() {
- return mQueryTextView.getText();
+ return mSearchSrcTextView.getText();
}
/**
@@ -548,9 +559,9 @@
* text field.
*/
public void setQuery(CharSequence query, boolean submit) {
- mQueryTextView.setText(query);
+ mSearchSrcTextView.setText(query);
if (query != null) {
- mQueryTextView.setSelection(mQueryTextView.length());
+ mSearchSrcTextView.setSelection(mSearchSrcTextView.length());
mUserQuery = query;
}
@@ -711,7 +722,7 @@
public void setSuggestionsAdapter(CursorAdapter adapter) {
mSuggestionsAdapter = adapter;
- mQueryTextView.setAdapter(mSuggestionsAdapter);
+ mSearchSrcTextView.setAdapter(mSuggestionsAdapter);
}
/**
@@ -789,12 +800,12 @@
// Visibility of views that are visible when collapsed
final int visCollapsed = collapsed ? VISIBLE : GONE;
// Is there text in the query
- final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+ final boolean hasText = !TextUtils.isEmpty(mSearchSrcTextView.getText());
mSearchButton.setVisibility(visCollapsed);
updateSubmitButton(hasText);
mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE);
- mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
+ mCollapsedIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
updateCloseButton();
updateVoiceButton(!hasText);
updateSubmitArea();
@@ -827,13 +838,13 @@
&& (hasText || !mVoiceButtonEnabled)) {
visibility = VISIBLE;
}
- mSubmitButton.setVisibility(visibility);
+ mGoButton.setVisibility(visibility);
}
private void updateSubmitArea() {
int visibility = GONE;
if (isSubmitAreaEnabled()
- && (mSubmitButton.getVisibility() == VISIBLE
+ && (mGoButton.getVisibility() == VISIBLE
|| mVoiceButton.getVisibility() == VISIBLE)) {
visibility = VISIBLE;
}
@@ -841,12 +852,15 @@
}
private void updateCloseButton() {
- final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+ final boolean hasText = !TextUtils.isEmpty(mSearchSrcTextView.getText());
// Should we show the close button? It is not shown if there's no focus,
// field is not iconified by default and there is no text in it.
final boolean showClose = hasText || (mIconifiedByDefault && !mExpandedInActionView);
mCloseButton.setVisibility(showClose ? VISIBLE : GONE);
- mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
+ final Drawable closeButtonImg = mCloseButton.getDrawable();
+ if (closeButtonImg != null){
+ closeButtonImg.setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
+ }
}
private void postUpdateFocusedState() {
@@ -854,9 +868,16 @@
}
private void updateFocusedState() {
- boolean focused = mQueryTextView.hasFocus();
- mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
- mSubmitArea.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
+ final boolean focused = mSearchSrcTextView.hasFocus();
+ final int[] stateSet = focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET;
+ final Drawable searchPlateBg = mSearchPlate.getBackground();
+ if (searchPlateBg != null) {
+ searchPlateBg.setState(stateSet);
+ }
+ final Drawable submitAreaBg = mSubmitArea.getBackground();
+ if (submitAreaBg != null) {
+ submitAreaBg.setState(stateSet);
+ }
invalidate();
}
@@ -896,11 +917,11 @@
onSearchClicked();
} else if (v == mCloseButton) {
onCloseClicked();
- } else if (v == mSubmitButton) {
+ } else if (v == mGoButton) {
onSubmitQuery();
} else if (v == mVoiceButton) {
onVoiceClicked();
- } else if (v == mQueryTextView) {
+ } else if (v == mSearchSrcTextView) {
forceSuggestionQuery();
}
}
@@ -925,7 +946,7 @@
// entered query with the action key
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
- launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText()
+ launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mSearchSrcTextView.getText()
.toString());
return true;
}
@@ -947,25 +968,25 @@
if (DBG) {
Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event + "), selection: "
- + mQueryTextView.getListSelection());
+ + mSearchSrcTextView.getListSelection());
}
// If a suggestion is selected, handle enter, search key, and action keys
// as presses on the selected suggestion
- if (mQueryTextView.isPopupShowing()
- && mQueryTextView.getListSelection() != ListView.INVALID_POSITION) {
+ if (mSearchSrcTextView.isPopupShowing()
+ && mSearchSrcTextView.getListSelection() != ListView.INVALID_POSITION) {
return onSuggestionsKey(v, keyCode, event);
}
// If there is text in the query box, handle enter, and action keys
// The search key is handled by the dialog's onKeyDown().
- if (!mQueryTextView.isEmpty() && event.hasNoModifiers()) {
+ if (!mSearchSrcTextView.isEmpty() && event.hasNoModifiers()) {
if (event.getAction() == KeyEvent.ACTION_UP) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
v.cancelLongPress();
// Launch as a regular search.
- launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mQueryTextView.getText()
+ launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mSearchSrcTextView.getText()
.toString());
return true;
}
@@ -973,7 +994,7 @@
if (event.getAction() == KeyEvent.ACTION_DOWN) {
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
- launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView
+ launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mSearchSrcTextView
.getText().toString());
return true;
}
@@ -1001,7 +1022,7 @@
// "click")
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH
|| keyCode == KeyEvent.KEYCODE_TAB) {
- int position = mQueryTextView.getListSelection();
+ int position = mSearchSrcTextView.getListSelection();
return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
}
@@ -1012,18 +1033,18 @@
// left key, at end if right key
// TODO: Reverse left/right for right-to-left languages, e.g.
// Arabic
- int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView
+ int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mSearchSrcTextView
.length();
- mQueryTextView.setSelection(selPoint);
- mQueryTextView.setListSelection(0);
- mQueryTextView.clearListSelection();
- mQueryTextView.ensureImeVisible(true);
+ mSearchSrcTextView.setSelection(selPoint);
+ mSearchSrcTextView.setListSelection(0);
+ mSearchSrcTextView.clearListSelection();
+ mSearchSrcTextView.ensureImeVisible(true);
return true;
}
// Next, check for an "up and out" move
- if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mQueryTextView.getListSelection()) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mSearchSrcTextView.getListSelection()) {
// TODO: restoreUserQuery();
// let ACTV complete the move
return false;
@@ -1035,7 +1056,7 @@
&& ((actionKey.getSuggestActionMsg() != null) || (actionKey
.getSuggestActionMsgColumn() != null))) {
// launch suggestion using action key column
- int position = mQueryTextView.getListSelection();
+ int position = mSearchSrcTextView.getListSelection();
if (position != ListView.INVALID_POSITION) {
Cursor c = mSuggestionsAdapter.getCursor();
if (c.moveToPosition(position)) {
@@ -1078,24 +1099,24 @@
}
private CharSequence getDecoratedHint(CharSequence hintText) {
- // If the field is always expanded, then don't add the search icon to the hint
- if (!mIconifiedByDefault) {
+ // If the field is always expanded or we don't have a search hint icon,
+ // then don't add the search icon to the hint.
+ if (!mIconifiedByDefault || mSearchHintIcon == null) {
return hintText;
}
- final Drawable searchIcon = getContext().getDrawable(mSearchIconResId);
- final int textSize = (int) (mQueryTextView.getTextSize() * 1.25);
- searchIcon.setBounds(0, 0, textSize, textSize);
+ final int textSize = (int) (mSearchSrcTextView.getTextSize() * 1.25);
+ mSearchHintIcon.setBounds(0, 0, textSize, textSize);
- final SpannableStringBuilder ssb = new SpannableStringBuilder(" "); // for the icon
+ final SpannableStringBuilder ssb = new SpannableStringBuilder(" ");
+ ssb.setSpan(new ImageSpan(mSearchHintIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(hintText);
- ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return ssb;
}
private void updateQueryHint() {
if (mQueryHint != null) {
- mQueryTextView.setHint(getDecoratedHint(mQueryHint));
+ mSearchSrcTextView.setHint(getDecoratedHint(mQueryHint));
} else if (mSearchable != null) {
CharSequence hint = null;
int hintId = mSearchable.getHintId();
@@ -1103,10 +1124,10 @@
hint = getContext().getString(hintId);
}
if (hint != null) {
- mQueryTextView.setHint(getDecoratedHint(hint));
+ mSearchSrcTextView.setHint(getDecoratedHint(hint));
}
} else {
- mQueryTextView.setHint(getDecoratedHint(""));
+ mSearchSrcTextView.setHint(getDecoratedHint(""));
}
}
@@ -1114,9 +1135,9 @@
* Updates the auto-complete text view.
*/
private void updateSearchAutoComplete() {
- mQueryTextView.setDropDownAnimationStyle(0); // no animation
- mQueryTextView.setThreshold(mSearchable.getSuggestThreshold());
- mQueryTextView.setImeOptions(mSearchable.getImeOptions());
+ mSearchSrcTextView.setDropDownAnimationStyle(0); // no animation
+ mSearchSrcTextView.setThreshold(mSearchable.getSuggestThreshold());
+ mSearchSrcTextView.setImeOptions(mSearchable.getImeOptions());
int inputType = mSearchable.getInputType();
// We only touch this if the input type is set up for text (which it almost certainly
// should be, in the case of search!)
@@ -1135,7 +1156,7 @@
inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
}
}
- mQueryTextView.setInputType(inputType);
+ mSearchSrcTextView.setInputType(inputType);
if (mSuggestionsAdapter != null) {
mSuggestionsAdapter.changeCursor(null);
}
@@ -1144,7 +1165,7 @@
if (mSearchable.getSuggestAuthority() != null) {
mSuggestionsAdapter = new SuggestionsAdapter(getContext(),
this, mSearchable, mOutsideDrawablesCache);
- mQueryTextView.setAdapter(mSuggestionsAdapter);
+ mSearchSrcTextView.setAdapter(mSuggestionsAdapter);
((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
mQueryRefinement ? SuggestionsAdapter.REFINE_ALL
: SuggestionsAdapter.REFINE_BY_ENTRY);
@@ -1161,7 +1182,7 @@
int visibility = GONE;
if (mVoiceButtonEnabled && !isIconified() && empty) {
visibility = VISIBLE;
- mSubmitButton.setVisibility(GONE);
+ mGoButton.setVisibility(GONE);
}
mVoiceButton.setVisibility(visibility);
}
@@ -1178,7 +1199,7 @@
};
private void onTextChanged(CharSequence newText) {
- CharSequence text = mQueryTextView.getText();
+ CharSequence text = mSearchSrcTextView.getText();
mUserQuery = text;
boolean hasText = !TextUtils.isEmpty(text);
updateSubmitButton(hasText);
@@ -1192,7 +1213,7 @@
}
private void onSubmitQuery() {
- CharSequence query = mQueryTextView.getText();
+ CharSequence query = mSearchSrcTextView.getText();
if (query != null && TextUtils.getTrimmedLength(query) > 0) {
if (mOnQueryChangeListener == null
|| !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
@@ -1206,11 +1227,11 @@
}
private void dismissSuggestions() {
- mQueryTextView.dismissDropDown();
+ mSearchSrcTextView.dismissDropDown();
}
private void onCloseClicked() {
- CharSequence text = mQueryTextView.getText();
+ CharSequence text = mSearchSrcTextView.getText();
if (TextUtils.isEmpty(text)) {
if (mIconifiedByDefault) {
// If the app doesn't override the close behavior
@@ -1222,8 +1243,8 @@
}
}
} else {
- mQueryTextView.setText("");
- mQueryTextView.requestFocus();
+ mSearchSrcTextView.setText("");
+ mSearchSrcTextView.requestFocus();
setImeVisibility(true);
}
@@ -1231,7 +1252,7 @@
private void onSearchClicked() {
updateViewsVisibility(false);
- mQueryTextView.requestFocus();
+ mSearchSrcTextView.requestFocus();
setImeVisibility(true);
if (mOnSearchClickListener != null) {
mOnSearchClickListener.onClick(this);
@@ -1266,7 +1287,7 @@
// Delayed update to make sure that the focus has settled down and window focus changes
// don't affect it. A synchronous update was not working.
postUpdateFocusedState();
- if (mQueryTextView.hasFocus()) {
+ if (mSearchSrcTextView.hasFocus()) {
forceSuggestionQuery();
}
}
@@ -1286,7 +1307,7 @@
setQuery("", false);
clearFocus();
updateViewsVisibility(true);
- mQueryTextView.setImeOptions(mCollapsedImeOptions);
+ mSearchSrcTextView.setImeOptions(mCollapsedImeOptions);
mExpandedInActionView = false;
}
@@ -1298,9 +1319,9 @@
if (mExpandedInActionView) return;
mExpandedInActionView = true;
- mCollapsedImeOptions = mQueryTextView.getImeOptions();
- mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
- mQueryTextView.setText("");
+ mCollapsedImeOptions = mSearchSrcTextView.getImeOptions();
+ mSearchSrcTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
+ mSearchSrcTextView.setText("");
setIconified(false);
}
@@ -1326,17 +1347,17 @@
? res.getDimensionPixelSize(R.dimen.dropdownitem_icon_width)
+ res.getDimensionPixelSize(R.dimen.dropdownitem_text_padding_left)
: 0;
- mQueryTextView.getDropDownBackground().getPadding(dropDownPadding);
+ mSearchSrcTextView.getDropDownBackground().getPadding(dropDownPadding);
int offset;
if (isLayoutRtl) {
offset = - dropDownPadding.left;
} else {
offset = anchorPadding - (dropDownPadding.left + iconOffset);
}
- mQueryTextView.setDropDownHorizontalOffset(offset);
+ mSearchSrcTextView.setDropDownHorizontalOffset(offset);
final int width = mDropDownAnchor.getWidth() + dropDownPadding.left
+ dropDownPadding.right + iconOffset - anchorPadding;
- mQueryTextView.setDropDownWidth(width);
+ mSearchSrcTextView.setDropDownWidth(width);
}
}
@@ -1394,7 +1415,7 @@
* Query rewriting.
*/
private void rewriteQueryFromSuggestion(int position) {
- CharSequence oldQuery = mQueryTextView.getText();
+ CharSequence oldQuery = mSearchSrcTextView.getText();
Cursor c = mSuggestionsAdapter.getCursor();
if (c == null) {
return;
@@ -1460,9 +1481,9 @@
* Sets the text in the query box, without updating the suggestions.
*/
private void setQuery(CharSequence query) {
- mQueryTextView.setText(query, true);
+ mSearchSrcTextView.setText(query, true);
// Move the cursor to the end
- mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
+ mSearchSrcTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
}
private void launchQuerySearch(int actionKey, String actionMsg, String query) {
@@ -1648,8 +1669,8 @@
}
private void forceSuggestionQuery() {
- mQueryTextView.doBeforeTextChanged();
- mQueryTextView.doAfterTextChanged();
+ mSearchSrcTextView.doBeforeTextChanged();
+ mSearchSrcTextView.doAfterTextChanged();
}
static boolean isLandscapeMode(Context context) {
diff --git a/core/java/android/widget/SimpleMonthAdapter.java b/core/java/android/widget/SimpleMonthAdapter.java
index ecd2912..24ebb2c 100644
--- a/core/java/android/widget/SimpleMonthAdapter.java
+++ b/core/java/android/widget/SimpleMonthAdapter.java
@@ -16,8 +16,12 @@
package android.widget;
+import com.android.internal.R;
+
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SimpleMonthView.OnDayClickListener;
@@ -33,15 +37,14 @@
private final Context mContext;
- private Calendar mSelectedDay;
- private ColorStateList mCalendarTextColors;
+ private Calendar mSelectedDay = Calendar.getInstance();
+ private ColorStateList mCalendarTextColors = ColorStateList.valueOf(Color.BLACK);
private OnDaySelectedListener mOnDaySelectedListener;
private int mFirstDayOfWeek;
public SimpleMonthAdapter(Context context) {
mContext = context;
- mSelectedDay = Calendar.getInstance();
}
public void setRange(Calendar min, Calendar max) {
@@ -57,6 +60,10 @@
notifyDataSetInvalidated();
}
+ public int getFirstDayOfWeek() {
+ return mFirstDayOfWeek;
+ }
+
/**
* Updates the selected day and related parameters.
*
@@ -81,6 +88,24 @@
mCalendarTextColors = colors;
}
+ /**
+ * Sets the text color, size, style, hint color, and highlight color from
+ * the specified TextAppearance resource. This is mostly copied from
+ * {@link TextView#setTextAppearance(Context, int)}.
+ */
+ void setCalendarTextAppearance(int resId) {
+ final TypedArray a = mContext.obtainStyledAttributes(resId, R.styleable.TextAppearance);
+
+ final ColorStateList textColor = a.getColorStateList(R.styleable.TextAppearance_textColor);
+ if (textColor != null) {
+ mCalendarTextColors = textColor;
+ }
+
+ // TODO: Support font size, etc.
+
+ a.recycle();
+ }
+
@Override
public int getCount() {
final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 178bab6..e572d22 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -275,6 +275,7 @@
subHeap = HEAP_DALVIK_LINEARALLOC;
} else if ((strstr(name, "/dev/ashmem/dalvik-alloc space") == name) ||
(strstr(name, "/dev/ashmem/dalvik-main space") == name) ||
+ (strstr(name, "/dev/ashmem/dalvik-zygote space") == name) ||
(strstr(name, "/dev/ashmem/dalvik-non moving space") == name)) {
// This is the regular Dalvik heap.
whichHeap = HEAP_DALVIK;
diff --git a/core/res/res/drawable-hdpi/switch_track_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/switch_track_mtrl_alpha.9.png
index 0ebe65e..9415bc0 100644
--- a/core/res/res/drawable-hdpi/switch_track_mtrl_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/switch_track_mtrl_alpha.9.png b/core/res/res/drawable-ldpi/switch_track_mtrl_alpha.9.png
deleted file mode 100644
index a58128f..0000000
--- a/core/res/res/drawable-ldpi/switch_track_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/edit_text_material.xml b/core/res/res/drawable/edit_text_material.xml
index bbc7301..38ac567 100644
--- a/core/res/res/drawable/edit_text_material.xml
+++ b/core/res/res/drawable/edit_text_material.xml
@@ -15,21 +15,23 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:inset="@dimen/control_inset_material">
- <ripple android:color="?attr/colorControlActivated">
- <item>
- <selector>
- <item android:state_enabled="false">
- <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
- </item>
- <item>
- <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- </selector>
+ android:insetLeft="@dimen/edit_text_inset_horizontal_material"
+ android:insetRight="@dimen/edit_text_inset_horizontal_material"
+ android:insetTop="@dimen/edit_text_inset_top_material"
+ android:insetBottom="@dimen/edit_text_inset_bottom_material">
+ <selector>
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
+ android:tint="?attr/colorControlNormal"
+ android:alpha="?attr/disabledAlpha" />
</item>
- <item android:id="@+id/mask" android:drawable="@drawable/textfield_activated_mtrl_alpha" />
- </ripple>
+ <item android:state_pressed="false" android:state_focused="false">
+ <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/textfield_activated_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ </selector>
</inset>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b1af4aa..403fd99 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4662,6 +4662,11 @@
<attr name="weekDayTextAppearance" format="reference" />
<!-- The text appearance for the calendar dates. -->
<attr name="dateTextAppearance" format="reference" />
+ <!-- The number of weeks to be shown. -->
+ <attr name="calendarViewMode">
+ <enum name="holo" value="0" />
+ <enum name="material" value="1" />
+ </attr>
</declare-styleable>
<declare-styleable name="NumberPicker">
@@ -7215,6 +7220,8 @@
<attr name="goIcon" format="reference" />
<!-- Search icon -->
<attr name="searchIcon" format="reference" />
+ <!-- Search icon displayed as a text field hint -->
+ <attr name="searchHintIcon" format="reference" />
<!-- Voice button icon -->
<attr name="voiceIcon" format="reference" />
<!-- Commit icon shown in the query suggestion row -->
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 46ec838..a8fd8d4 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -22,7 +22,7 @@
<color name="background_floating_material_light">#ffeeeeee</color>
<color name="primary_material_dark">#ff212121</color>
- <color name="primary_material_light">#ffe0e0e0</color>
+ <color name="primary_material_light">#ffefefef</color>
<color name="primary_dark_material_dark">#ff000000</color>
<color name="primary_dark_material_light">#ff757575</color>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 4a2119b..e3672c8 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -91,6 +91,10 @@
<!-- Default rounded corner for controls -->
<dimen name="control_corner_material">2dp</dimen>
+ <dimen name="edit_text_inset_horizontal_material">4dp</dimen>
+ <dimen name="edit_text_inset_top_material">4dp</dimen>
+ <dimen name="edit_text_inset_bottom_material">8dp</dimen>
+
<dimen name="dialog_padding_material">24dp</dimen>
<dimen name="dialog_padding_top_material">18dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c661c33..c01d406 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2600,5 +2600,6 @@
<public type="attr" name="accessibilityTraversalBefore" />
<public type="attr" name="accessibilityTraversalAfter" />
<public type="attr" name="dialogPreferredPadding" />
+ <public type="attr" name="searchHintIcon" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 522c85c..4500e3d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3306,6 +3306,8 @@
<!-- This is the default button label in the system-wide search UI.
It is also used by the home screen's search "widget". It should be short -->
<string name="search_go">Search</string>
+ <!-- Default hint text for the system-wide search UI's text field. [CHAR LIMIT=30] -->
+ <string name="search_hint">Search…</string>
<!-- SearchView accessibility description for search button [CHAR LIMIT=NONE] -->
<string name="searchview_description_search">Search</string>
<!-- SearchView accessibility description for search text field [CHAR LIMIT=NONE] -->
@@ -5077,7 +5079,7 @@
<string name="lock_to_app_unlock_password">Ask for password before unpinning</string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
- <string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
+ <string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
<!-- [CHAR_LIMIT=NONE] Zen mode: Condition summary for built-in downtime condition, if active -->
<string name="downtime_condition_summary">Until your downtime ends at <xliff:g id="formattedTime" example="10:00 PM">%1$s</xliff:g></string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 5a59afe..c520a46 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -565,6 +565,7 @@
<item name="selectedDateVerticalBar">@drawable/day_picker_week_view_dayline_holo</item>
<item name="weekDayTextAppearance">@style/TextAppearance.Small.CalendarViewWeekDayView</item>
<item name="dateTextAppearance">?attr/textAppearanceSmall</item>
+ <item name="calendarViewMode">holo</item>
</style>
<style name="Widget.NumberPicker">
diff --git a/core/res/res/values/styles_holo.xml b/core/res/res/values/styles_holo.xml
index c7d2db1..41b8b25 100644
--- a/core/res/res/values/styles_holo.xml
+++ b/core/res/res/values/styles_holo.xml
@@ -358,6 +358,7 @@
<item name="submitBackground">@drawable/textfield_searchview_right_holo_dark</item>
<item name="closeIcon">@drawable/ic_clear_holo_dark</item>
<item name="searchIcon">@drawable/ic_search_api_holo_dark</item>
+ <item name="searchHintIcon">@drawable/ic_search_api_holo_dark</item>
<item name="goIcon">@drawable/ic_go_search_api_holo_dark</item>
<item name="voiceIcon">@drawable/ic_voice_search_api_holo_dark</item>
<item name="commitIcon">@drawable/ic_commit_search_api_holo_dark</item>
@@ -444,6 +445,7 @@
<item name="weekSeparatorLineColor">#19FFFFFF</item>
<item name="selectedDateVerticalBar">@drawable/day_picker_week_view_dayline_holo</item>
<item name="weekDayTextAppearance">@style/TextAppearance.Holo.CalendarViewWeekDayView</item>
+ <item name="calendarViewMode">holo</item>
</style>
<style name="Widget.Holo.ImageButton" parent="Widget.ImageButton">
@@ -881,6 +883,7 @@
<item name="weekNumberColor">#7F080021</item>
<item name="weekSeparatorLineColor">#7F08002A</item>
<item name="weekDayTextAppearance">@style/TextAppearance.Holo.Light.CalendarViewWeekDayView</item>
+ <item name="calendarViewMode">holo</item>
</style>
<style name="Widget.Holo.Light.NumberPicker" parent="Widget.Holo.NumberPicker" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index e04d901..6749441 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -517,12 +517,20 @@
<item name="submitBackground">@drawable/textfield_search_material</item>
<item name="closeIcon">@drawable/ic_clear_material</item>
<item name="searchIcon">@drawable/ic_search_api_material</item>
+ <item name="searchHintIcon">@drawable/ic_search_api_material</item>
<item name="goIcon">@drawable/ic_go_search_api_material</item>
<item name="voiceIcon">@drawable/ic_voice_search_api_material</item>
<item name="commitIcon">@drawable/ic_commit_search_api_material</item>
<item name="suggestionRowLayout">@layout/search_dropdown_item_icons_2line</item>
</style>
+ <style name="Widget.Material.SearchView.ActionBar">
+ <item name="queryBackground">@empty</item>
+ <item name="submitBackground">@empty</item>
+ <item name="searchHintIcon">@empty</item>
+ <item name="queryHint">@string/search_hint</item>
+ </style>
+
<style name="Widget.Material.SegmentedButton" parent="SegmentedButton">
<item name="background">@drawable/btn_group_holo_dark</item>
</style>
@@ -612,6 +620,7 @@
<item name="weekSeparatorLineColor">#19FFFFFF</item>
<item name="selectedDateVerticalBar">@drawable/day_picker_week_view_dayline_holo</item>
<item name="weekDayTextAppearance">@style/TextAppearance.Material.CalendarViewWeekDayView</item>
+ <item name="calendarViewMode">material</item>
</style>
<style name="Widget.Material.ImageButton" parent="Widget.ImageButton">
@@ -961,6 +970,7 @@
<style name="Widget.Material.Light.ButtonBar" parent="Widget.Material.ButtonBar"/>
<style name="Widget.Material.Light.ButtonBar.AlertDialog" parent="Widget.Material.ButtonBar.AlertDialog"/>
<style name="Widget.Material.Light.SearchView" parent="Widget.Material.SearchView"/>
+ <style name="Widget.Material.Light.SearchView.ActionBar" parent="Widget.Material.SearchView.ActionBar"/>
<style name="Widget.Material.Light.SegmentedButton" parent="Widget.Material.SegmentedButton">
<item name="background">@drawable/btn_group_holo_light</item>
@@ -999,6 +1009,7 @@
<item name="weekNumberColor">#7F080021</item>
<item name="weekSeparatorLineColor">#7F08002A</item>
<item name="weekDayTextAppearance">@style/TextAppearance.Material.CalendarViewWeekDayView</item>
+ <item name="calendarViewMode">material</item>
</style>
<style name="Widget.Material.Light.NumberPicker" parent="Widget.Material.NumberPicker"/>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index b320ae5..3f2062dc 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -828,6 +828,7 @@
secondary text color, with the primary text color. -->
<style name="ThemeOverlay.Material.ActionBar">
<item name="colorControlNormal">?attr/textColorPrimary</item>
+ <item name="searchViewStyle">@style/Widget.Material.Light.SearchView.ActionBar</item>
</style>
<!-- Theme overlay that replaces colors with their dark versions and replaces the normal
@@ -835,6 +836,7 @@
text color. -->
<style name="ThemeOverlay.Material.Dark.ActionBar">
<item name="colorControlNormal">?attr/textColorPrimary</item>
+ <item name="searchViewStyle">@style/Widget.Material.SearchView.ActionBar</item>
</style>
<!-- Variant of the material (dark) theme with no action bar. -->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 178590b..80ddd4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -29,7 +29,8 @@
import com.android.systemui.qs.QSTileView;
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.DataUsageInfo;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataController;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataController.DataUsageInfo;
import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
/** Quick settings tile: Cellular **/
@@ -38,11 +39,13 @@
"com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity"));
private final NetworkController mController;
+ private final MobileDataController mDataController;
private final CellularDetailAdapter mDetailAdapter;
public CellularTile(Host host) {
super(host);
mController = host.getNetworkController();
+ mDataController = mController.getMobileDataController();
mDetailAdapter = new CellularDetailAdapter();
}
@@ -72,7 +75,7 @@
@Override
protected void handleClick() {
- if (mController.isMobileDataSupported()) {
+ if (mDataController.isMobileDataSupported()) {
showDetail(true);
} else {
mHost.startSettingsActivity(CELLULAR_SETTINGS);
@@ -199,7 +202,8 @@
@Override
public Boolean getToggleState() {
- return mController.isMobileDataSupported() ? mController.isMobileDataEnabled() : null;
+ return mDataController.isMobileDataSupported()
+ ? mDataController.isMobileDataEnabled() : null;
}
@Override
@@ -209,7 +213,7 @@
@Override
public void setToggleState(boolean state) {
- mController.setMobileDataEnabled(state);
+ mDataController.setMobileDataEnabled(state);
}
@Override
@@ -217,7 +221,7 @@
final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
? convertView
: LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false));
- final DataUsageInfo info = mController.getDataUsageInfo();
+ final DataUsageInfo info = mDataController.getDataUsageInfo();
if (info == null) return v;
v.bind(info);
return v;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
index 7bdb58f..eb816b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataUsageDetailView.java
@@ -20,7 +20,6 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.AttributeSet;
-import android.util.TypedValue;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -61,7 +60,7 @@
R.dimen.qs_data_usage_text_size);
}
- public void bind(NetworkController.DataUsageInfo info) {
+ public void bind(NetworkController.MobileDataController.DataUsageInfo info) {
final Resources res = mContext.getResources();
final int titleId;
final long bytes;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 7aa884e..4fb1189 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -31,7 +31,8 @@
import com.android.systemui.qs.QSTileView;
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPoint;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController.AccessPoint;
import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
/** Quick settings tile: Wifi **/
@@ -39,12 +40,14 @@
private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
private final NetworkController mController;
+ private final AccessPointController mWifiController;
private final WifiDetailAdapter mDetailAdapter;
private final QSTile.SignalState mStateBeforeClick = newTileState();
public WifiTile(Host host) {
super(host);
mController = host.getNetworkController();
+ mWifiController = mController.getAccessPointController();
mDetailAdapter = new WifiDetailAdapter();
}
@@ -62,10 +65,10 @@
public void setListening(boolean listening) {
if (listening) {
mController.addNetworkSignalChangedCallback(mCallback);
- mController.addAccessPointCallback(mDetailAdapter);
+ mWifiController.addAccessPointCallback(mDetailAdapter);
} else {
mController.removeNetworkSignalChangedCallback(mCallback);
- mController.removeAccessPointCallback(mDetailAdapter);
+ mWifiController.removeAccessPointCallback(mDetailAdapter);
}
}
@@ -87,7 +90,7 @@
@Override
protected void handleSecondaryClick() {
- if (!mController.canConfigWifi()) {
+ if (!mWifiController.canConfigWifi()) {
mHost.startSettingsActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
return;
}
@@ -231,7 +234,7 @@
};
private final class WifiDetailAdapter implements DetailAdapter,
- NetworkController.AccessPointCallback, QSDetailItems.Callback {
+ NetworkController.AccessPointController.AccessPointCallback, QSDetailItems.Callback {
private QSDetailItems mItems;
private AccessPoint[] mAccessPoints;
@@ -261,7 +264,7 @@
public View createDetailView(Context context, View convertView, ViewGroup parent) {
if (DEBUG) Log.d(TAG, "createDetailView convertView=" + (convertView != null));
mAccessPoints = null;
- mController.scanForAccessPoints();
+ mWifiController.scanForAccessPoints();
fireScanStateChanged(true);
mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
mItems.setTagSuffix("Wifi");
@@ -287,7 +290,7 @@
if (item == null || item.tag == null) return;
final AccessPoint ap = (AccessPoint) item.tag;
if (!ap.isConnected) {
- if (mController.connect(ap)) {
+ if (mWifiController.connect(ap)) {
mHost.collapsePanels();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 9154a48..418c57f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -48,6 +48,7 @@
private int mMobileStrengthId = 0, mMobileTypeId = 0;
private boolean mIsAirplaneMode = false;
private int mAirplaneIconId = 0;
+ private int mAirplaneContentDescription;
private String mWifiDescription, mMobileDescription, mMobileTypeDescription;
private boolean mIsMobileTypeIconWide;
@@ -160,9 +161,10 @@
}
@Override
- public void setIsAirplaneMode(boolean is, int airplaneIconId) {
+ public void setIsAirplaneMode(boolean is, int airplaneIconId, int contentDescription) {
mIsAirplaneMode = is;
mAirplaneIconId = airplaneIconId;
+ mAirplaneContentDescription = contentDescription;
apply();
}
@@ -236,6 +238,8 @@
if (mIsAirplaneMode) {
mAirplane.setImageResource(mAirplaneIconId);
+ mAirplane.setContentDescription(mAirplaneContentDescription != 0 ?
+ mContext.getString(mAirplaneContentDescription) : "");
mAirplane.setVisibility(View.VISIBLE);
} else {
mAirplane.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 3b2d3cb..9a1ac49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -79,6 +79,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -821,7 +822,12 @@
signalClusterQs.setNetworkController(mNetworkController);
final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
if (isAPhone) {
- mNetworkController.addEmergencyLabelView(mHeader);
+ mNetworkController.addEmergencyListener(new NetworkControllerImpl.EmergencyListener() {
+ @Override
+ public void setEmergencyCallsOnly(boolean emergencyOnly) {
+ mHeader.setShowEmergencyCallsOnly(emergencyOnly);
+ }
+ });
}
mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
@@ -830,13 +836,19 @@
if (mShowCarrierInPanel) {
mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
- // for mobile devices, we always show mobile connection info here (SPN/PLMN)
- // for other devices, we show whatever network is connected
- if (mNetworkController.hasMobileDataFeature()) {
- mNetworkController.addMobileLabelView(mCarrierLabel);
- } else {
- mNetworkController.addCombinedLabelView(mCarrierLabel);
- }
+ mNetworkController.addCarrierLabel(new NetworkControllerImpl.CarrierLabelListener() {
+ @Override
+ public void setCarrierLabel(String label) {
+ mCarrierLabel.setText(label);
+ if (mNetworkController.hasMobileDataFeature()) {
+ if (TextUtils.isEmpty(label)) {
+ mCarrierLabel.setVisibility(View.GONE);
+ } else {
+ mCarrierLabel.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+ });
// set up the dynamic hide/show of the label
// TODO: uncomment, handle this for the Stack scroller aswell
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 45a1386..8ce608c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -122,7 +122,7 @@
tile.userSwitch(newUserId);
}
mSecurity.onUserSwitched(newUserId);
- mNetwork.onUserSwitched(newUserId);
+ mNetwork.getAccessPointController().onUserSwitched(newUserId);
mObserver.register();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index 0a385d7..6fec97e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -36,15 +36,13 @@
import android.util.Log;
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPoint;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPointCallback;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-public class AccessPointController {
+public class AccessPointControllerImpl implements NetworkController.AccessPointController {
private static final String TAG = "AccessPointController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -69,7 +67,7 @@
private boolean mScanning;
private int mCurrentUser;
- public AccessPointController(Context context) {
+ public AccessPointControllerImpl(Context context) {
mContext = context;
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -81,25 +79,28 @@
new UserHandle(mCurrentUser));
}
- void onUserSwitched(int newUserId) {
+ public void onUserSwitched(int newUserId) {
mCurrentUser = newUserId;
}
- public void addCallback(AccessPointCallback callback) {
+ @Override
+ public void addAccessPointCallback(AccessPointCallback callback) {
if (callback == null || mCallbacks.contains(callback)) return;
if (DEBUG) Log.d(TAG, "addCallback " + callback);
mCallbacks.add(callback);
mReceiver.setListening(!mCallbacks.isEmpty());
}
- public void removeCallback(AccessPointCallback callback) {
+ @Override
+ public void removeAccessPointCallback(AccessPointCallback callback) {
if (callback == null) return;
if (DEBUG) Log.d(TAG, "removeCallback " + callback);
mCallbacks.remove(callback);
mReceiver.setListening(!mCallbacks.isEmpty());
}
- public void scan() {
+ @Override
+ public void scanForAccessPoints() {
if (mScanning) return;
if (DEBUG) Log.d(TAG, "scan!");
mScanning = mWifiManager.startScan();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java
index 7ac2a98..b7c74e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java
@@ -33,11 +33,5 @@
R.string.accessibility_wifi_three_bars,
R.string.accessibility_wifi_signal_full
};
- static final int[] WIMAX_CONNECTION_STRENGTH = {
- R.string.accessibility_no_wimax,
- R.string.accessibility_wimax_one_bar,
- R.string.accessibility_wimax_two_bars,
- R.string.accessibility_wimax_three_bars,
- R.string.accessibility_wimax_signal_full
- };
+ static final int WIFI_NO_CONNECTION = R.string.accessibility_no_wifi;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java
index 33d68bf..20f0a83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java
@@ -38,12 +38,10 @@
import android.text.format.Time;
import android.util.Log;
-import com.android.systemui.statusbar.policy.NetworkController.DataUsageInfo;
-
import java.util.Date;
import java.util.Locale;
-public class MobileDataController {
+public class MobileDataControllerImpl implements NetworkController.MobileDataController {
private static final String TAG = "MobileDataController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -61,8 +59,9 @@
private INetworkStatsSession mSession;
private Callback mCallback;
+ private NetworkControllerImpl mNetworkController;
- public MobileDataController(Context context) {
+ public MobileDataControllerImpl(Context context) {
mContext = context;
mTelephonyManager = TelephonyManager.from(context);
mConnectivityManager = ConnectivityManager.from(context);
@@ -71,6 +70,10 @@
mPolicyManager = NetworkPolicyManager.from(mContext);
}
+ public void setNetworkController(NetworkControllerImpl networkController) {
+ mNetworkController = networkController;
+ }
+
private INetworkStatsSession getSession() {
if (mSession == null) {
try {
@@ -155,6 +158,9 @@
} else {
usage.warningLevel = DEFAULT_WARNING_LEVEL;
}
+ if (usage != null) {
+ usage.carrier = mNetworkController.getMobileNetworkName();
+ }
return usage;
} catch (RemoteException e) {
return warn("remote call failed");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index bb29d01..b024f58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -22,6 +22,8 @@
void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb);
void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb);
void setWifiEnabled(boolean enabled);
+ AccessPointController getAccessPointController();
+ MobileDataController getMobileDataController();
public interface NetworkSignalChangedCallback {
void onWifiSignalChanged(boolean enabled, boolean connected, int wifiSignalIconId,
@@ -36,38 +38,50 @@
void onMobileDataEnabled(boolean enabled);
}
- void addAccessPointCallback(AccessPointCallback callback);
- void removeAccessPointCallback(AccessPointCallback callback);
- void scanForAccessPoints();
- boolean connect(AccessPoint ap);
- boolean isMobileDataSupported();
- boolean isMobileDataEnabled();
- void setMobileDataEnabled(boolean enabled);
- DataUsageInfo getDataUsageInfo();
- boolean canConfigWifi();
- void onUserSwitched(int newUserId);
+ /**
+ * Tracks changes in access points. Allows listening for changes, scanning for new APs,
+ * and connecting to new ones.
+ */
+ public interface AccessPointController {
+ void addAccessPointCallback(AccessPointCallback callback);
+ void removeAccessPointCallback(AccessPointCallback callback);
+ void scanForAccessPoints();
+ boolean connect(AccessPoint ap);
+ boolean canConfigWifi();
+ void onUserSwitched(int newUserId);
- public interface AccessPointCallback {
- void onAccessPointsChanged(AccessPoint[] accessPoints);
+ public interface AccessPointCallback {
+ void onAccessPointsChanged(AccessPoint[] accessPoints);
+ }
+
+ public static class AccessPoint {
+ public static final int NO_NETWORK = -1; // see WifiManager
+
+ public int networkId;
+ public int iconId;
+ public String ssid;
+ public boolean isConnected;
+ public boolean isConfigured;
+ public boolean hasSecurity;
+ public int level; // 0 - 5
+ }
}
- public static class AccessPoint {
- public static final int NO_NETWORK = -1; // see WifiManager
+ /**
+ * Tracks mobile data support and usage.
+ */
+ public interface MobileDataController {
+ boolean isMobileDataSupported();
+ boolean isMobileDataEnabled();
+ void setMobileDataEnabled(boolean enabled);
+ DataUsageInfo getDataUsageInfo();
- public int networkId;
- public int iconId;
- public String ssid;
- public boolean isConnected;
- public boolean isConfigured;
- public boolean hasSecurity;
- public int level; // 0 - 5
- }
-
- public static class DataUsageInfo {
- public String carrier;
- public String period;
- public long limitLevel;
- public long warningLevel;
- public long usageLevel;
+ public static class DataUsageInfo {
+ public String carrier;
+ public String period;
+ public long limitLevel;
+ public long warningLevel;
+ public long usageLevel;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 07762a4..5a97c75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -26,7 +26,6 @@
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wimax.WimaxManagerConstants;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
@@ -37,9 +36,8 @@
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
+import android.text.format.DateFormat;
import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IccCardConstants;
@@ -48,130 +46,66 @@
import com.android.internal.util.AsyncChannel;
import com.android.systemui.DemoMode;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.StatusBarHeaderView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
/** Platform implementation of the network controller. **/
public class NetworkControllerImpl extends BroadcastReceiver
implements NetworkController, DemoMode {
// debug
- static final String TAG = "StatusBar.NetworkController";
- static final boolean DEBUG = false;
- static final boolean CHATTY = false; // additional diagnostics, but not logspew
+ static final String TAG = "NetworkController";
+ static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ // additional diagnostics, but not logspew
+ static final boolean CHATTY = Log.isLoggable(TAG + ".Chat", Log.DEBUG);
+ // Save the previous states of all SignalController state info.
+ static final boolean RECORD_HISTORY = true;
+ // How many to save, must be a power of 2.
+ static final int HISTORY_SIZE = 16;
- // telephony
- boolean mHspaDataDistinguishable;
- final TelephonyManager mPhone;
- boolean mDataConnected;
- IccCardConstants.State mSimState = IccCardConstants.State.READY;
- int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
- int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
- int mDataState = TelephonyManager.DATA_DISCONNECTED;
- int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
- ServiceState mServiceState;
- SignalStrength mSignalStrength;
- int[] mDataIconList = TelephonyIcons.DATA_G[0];
- String mNetworkName;
- String mNetworkNameDefault;
- String mNetworkNameSeparator;
- int mPhoneSignalIconId;
- int mQSPhoneSignalIconId;
- int mDataDirectionIconId; // data + data direction on phones
- int mDataSignalIconId;
- int mDataTypeIconId;
- int mQSDataTypeIconId;
- int mAirplaneIconId;
- boolean mDataActive;
- boolean mNoSim;
- int mLastSignalLevel;
- boolean mShowPhoneRSSIForData = false;
- boolean mShowAtLeastThreeGees = false;
- boolean mAlwaysShowCdmaRssi = false;
+ private static final int INET_CONDITION_THRESHOLD = 50;
- String mContentDescriptionPhoneSignal;
- String mContentDescriptionWifi;
- String mContentDescriptionWimax;
- String mContentDescriptionCombinedSignal;
- String mContentDescriptionDataType;
+ private final Context mContext;
+ private final TelephonyManager mPhone;
+ private final WifiManager mWifiManager;
+ private final ConnectivityManager mConnectivityManager;
+ private final boolean mHasMobileDataFeature;
- // wifi
- final WifiManager mWifiManager;
- AsyncChannel mWifiChannel;
- boolean mWifiEnabled, mWifiConnected;
- int mWifiRssi, mWifiLevel;
- String mWifiSsid;
- int mWifiIconId = 0;
- int mQSWifiIconId = 0;
- int mWifiActivity = WifiManager.DATA_ACTIVITY_NONE;
+ // Subcontrollers.
+ @VisibleForTesting
+ final WifiSignalController mWifiSignalController;
+ @VisibleForTesting
+ final MobileSignalController mMobileSignalController;
+ private final AccessPointController mAccessPoints;
+ private final MobileDataControllerImpl mMobileDataController;
// bluetooth
private boolean mBluetoothTethered = false;
- private int mBluetoothTetherIconId =
- com.android.internal.R.drawable.stat_sys_tether_bluetooth;
-
- //wimax
- private boolean mWimaxSupported = false;
- private boolean mIsWimaxEnabled = false;
- private boolean mWimaxConnected = false;
- private boolean mWimaxIdle = false;
- private int mWimaxIconId = 0;
- private int mWimaxSignal = 0;
- private int mWimaxState = 0;
- private int mWimaxExtraState = 0;
// data connectivity (regardless of state, can we access the internet?)
// state of inet connection - 0 not connected, 100 connected
private boolean mConnected = false;
private int mConnectedNetworkType = ConnectivityManager.TYPE_NONE;
private String mConnectedNetworkTypeName;
- private int mLastConnectedNetworkType = ConnectivityManager.TYPE_NONE;
+ private boolean mInetCondition; // Used for Logging and demo.
- private int mInetCondition = 0;
- private int mLastInetCondition = 0;
- private static final int INET_CONDITION_THRESHOLD = 50;
-
+ // States that don't belong to a subcontroller.
private boolean mAirplaneMode = false;
- private boolean mLastAirplaneMode = true;
-
private Locale mLocale = null;
- private Locale mLastLocale = null;
- // our ui
- Context mContext;
- ArrayList<TextView> mCombinedLabelViews = new ArrayList<TextView>();
- ArrayList<TextView> mMobileLabelViews = new ArrayList<TextView>();
- ArrayList<TextView> mWifiLabelViews = new ArrayList<TextView>();
- ArrayList<StatusBarHeaderView> mEmergencyViews = new ArrayList<>();
- ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>();
- ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks =
+ // All the callbacks.
+ private ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<EmergencyListener>();
+ private ArrayList<CarrierLabelListener> mCarrierListeners =
+ new ArrayList<CarrierLabelListener>();
+ private ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>();
+ private ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks =
new ArrayList<NetworkSignalChangedCallback>();
- int mLastPhoneSignalIconId = -1;
- int mLastDataDirectionIconId = -1;
- int mLastWifiIconId = -1;
- int mLastWimaxIconId = -1;
- int mLastCombinedSignalIconId = -1;
- int mLastDataTypeIconId = -1;
- String mLastCombinedLabel = "";
-
- private boolean mHasMobileDataFeature;
-
- boolean mDataAndWifiStacked = false;
-
- public interface SignalCluster {
- void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription);
- void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
- String contentDescription, String typeContentDescription, boolean isTypeIconWide);
- void setIsAirplaneMode(boolean is, int airplaneIcon);
- }
-
- private final AccessPointController mAccessPoints;
- private final MobileDataController mMobileDataController;
- private final ConnectivityManager mConnectivityManager;
/**
* Construct this controller object and register for updates.
@@ -180,70 +114,50 @@
this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
(WifiManager) context.getSystemService(Context.WIFI_SERVICE),
- new AccessPointController(context), new MobileDataController(context));
+ Config.readConfig(context), new AccessPointControllerImpl(context),
+ new MobileDataControllerImpl(context));
registerListeners();
}
@VisibleForTesting
NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
- TelephonyManager telephonyManager, WifiManager wifiManager,
- AccessPointController accessPointController,
- MobileDataController mobileDataController) {
+ TelephonyManager telephonyManager, WifiManager wifiManager, Config config,
+ AccessPointControllerImpl accessPointController,
+ MobileDataControllerImpl mobileDataController) {
mContext = context;
- final Resources res = context.getResources();
mConnectivityManager = connectivityManager;
mHasMobileDataFeature =
mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
- mShowPhoneRSSIForData = res.getBoolean(R.bool.config_showPhoneRSSIForData);
- mShowAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G);
- mAlwaysShowCdmaRssi = res.getBoolean(
- com.android.internal.R.bool.config_alwaysUseCdmaRssi);
-
- // set up the default wifi icon, used when no radios have ever appeared
- updateWifiIcons();
- updateWimaxIcons();
-
// telephony
mPhone = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- mHspaDataDistinguishable = mContext.getResources().getBoolean(
- R.bool.config_hspa_data_distinguishable);
- mNetworkNameSeparator = mContext.getString(R.string.status_bar_network_name_separator);
- mNetworkNameDefault = mContext.getString(
- com.android.internal.R.string.lockscreen_carrier_default);
- mNetworkName = mNetworkNameDefault;
// wifi
mWifiManager = wifiManager;
- Handler handler = new WifiHandler();
- mWifiChannel = new AsyncChannel();
- Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
- if (wifiMessenger != null) {
- mWifiChannel.connect(mContext, handler, wifiMessenger);
- }
- // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
- updateAirplaneMode();
-
- mLastLocale = mContext.getResources().getConfiguration().locale;
+ mLocale = mContext.getResources().getConfiguration().locale;
mAccessPoints = accessPointController;
mMobileDataController = mobileDataController;
- mMobileDataController.setCallback(new MobileDataController.Callback() {
+ mMobileDataController.setNetworkController(this);
+ // TODO: Find a way to move this into MobileDataController.
+ mMobileDataController.setCallback(new MobileDataControllerImpl.Callback() {
@Override
public void onMobileDataEnabled(boolean enabled) {
notifyMobileDataEnabled(enabled);
}
});
+ mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
+ mSignalsChangedCallbacks, mSignalClusters, this);
+ mMobileSignalController = new MobileSignalController(mContext, config,
+ mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks, mSignalClusters, this);
+
+ // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
+ updateAirplaneMode(true);
}
private void registerListeners() {
- mPhone.listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_SERVICE_STATE
- | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
- | PhoneStateListener.LISTEN_CALL_STATE
- | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
- | PhoneStateListener.LISTEN_DATA_ACTIVITY);
+ mMobileSignalController.registerListener();
// broadcasts
IntentFilter filter = new IntentFilter();
@@ -256,29 +170,38 @@
filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- mWimaxSupported = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wimaxEnabled);
- if(mWimaxSupported) {
- filter.addAction(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION);
- filter.addAction(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION);
- }
mContext.registerReceiver(this, filter);
}
- @Override
- public boolean canConfigWifi() {
- return mAccessPoints.canConfigWifi();
+ private void unregisterListeners() {
+ mMobileSignalController.unregisterListener();
+ mContext.unregisterReceiver(this);
}
@Override
- public void onUserSwitched(int newUserId) {
- mAccessPoints.onUserSwitched(newUserId);
+ public AccessPointController getAccessPointController() {
+ return mAccessPoints;
+ }
+
+ @Override
+ public MobileDataController getMobileDataController() {
+ return mMobileDataController;
+ }
+
+ public void addEmergencyListener(EmergencyListener listener) {
+ mEmergencyListeners.add(listener);
+ refreshCarrierLabel();
+ }
+
+ public void addCarrierLabel(CarrierLabelListener listener) {
+ mCarrierListeners.add(listener);
+ refreshCarrierLabel();
}
private void notifyMobileDataEnabled(boolean enabled) {
- for (NetworkSignalChangedCallback cb : mSignalsChangedCallbacks) {
- cb.onMobileDataEnabled(enabled);
+ int length = mSignalsChangedCallbacks.size();
+ for (int i = 0; i < length; i++) {
+ mSignalsChangedCallbacks.get(i).onMobileDataEnabled(enabled);
}
}
@@ -290,34 +213,40 @@
return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
}
+ public String getMobileNetworkName() {
+ return mMobileSignalController.mCurrentState.networkName;
+ }
+
public boolean isEmergencyOnly() {
- return (mServiceState != null && mServiceState.isEmergencyOnly());
+ return mMobileSignalController.isEmergencyOnly();
}
- public void addCombinedLabelView(TextView v) {
- mCombinedLabelViews.add(v);
- }
+ /**
+ * Emergency status may have changed (triggered by MobileSignalController),
+ * so we should recheck and send out the state to listeners.
+ */
+ void recalculateEmergency() {
+ final boolean emergencyOnly = isEmergencyOnly();
- public void addMobileLabelView(TextView v) {
- mMobileLabelViews.add(v);
- }
-
- public void addWifiLabelView(TextView v) {
- mWifiLabelViews.add(v);
- }
-
- public void addEmergencyLabelView(StatusBarHeaderView v) {
- mEmergencyViews.add(v);
+ int length = mEmergencyListeners.size();
+ for (int i = 0; i < length; i++) {
+ mEmergencyListeners.get(i).setEmergencyCallsOnly(emergencyOnly);
+ }
}
public void addSignalCluster(SignalCluster cluster) {
mSignalClusters.add(cluster);
- refreshSignalCluster(cluster);
+ cluster.setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON,
+ R.string.accessibility_airplane_mode);
+ mWifiSignalController.notifyListeners();
+ mMobileSignalController.notifyListeners();
}
public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) {
mSignalsChangedCallbacks.add(cb);
- notifySignalsChangedCallbacks(cb);
+ cb.onAirplaneModeChanged(mAirplaneMode);
+ mWifiSignalController.notifyListeners();
+ mMobileSignalController.notifyListeners();
}
public void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) {
@@ -325,26 +254,6 @@
}
@Override
- public void addAccessPointCallback(AccessPointCallback callback) {
- mAccessPoints.addCallback(callback);
- }
-
- @Override
- public void removeAccessPointCallback(AccessPointCallback callback) {
- mAccessPoints.removeCallback(callback);
- }
-
- @Override
- public void scanForAccessPoints() {
- mAccessPoints.scan();
- }
-
- @Override
- public boolean connect(AccessPoint ap) {
- return mAccessPoints.connect(ap);
- }
-
- @Override
public void setWifiEnabled(final boolean enabled) {
new AsyncTask<Void, Void, Void>() {
@Override
@@ -352,7 +261,7 @@
// Disable tethering if enabling Wifi
final int wifiApState = mWifiManager.getWifiApState();
if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
- (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
+ (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
mWifiManager.setWifiApEnabled(null, false);
}
@@ -363,732 +272,79 @@
}
@Override
- public DataUsageInfo getDataUsageInfo() {
- final DataUsageInfo info = mMobileDataController.getDataUsageInfo();
- if (info != null) {
- info.carrier = mNetworkName;
- }
- return info;
- }
-
- @Override
- public boolean isMobileDataSupported() {
- return mMobileDataController.isMobileDataSupported();
- }
-
- @Override
- public boolean isMobileDataEnabled() {
- return mMobileDataController.isMobileDataEnabled();
- }
-
- @Override
- public void setMobileDataEnabled(boolean enabled) {
- mMobileDataController.setMobileDataEnabled(enabled);
- }
-
- private boolean isTypeIconWide(int iconId) {
- return TelephonyIcons.ICON_LTE == iconId || TelephonyIcons.ICON_1X == iconId
- || TelephonyIcons.ICON_3G == iconId || TelephonyIcons.ICON_4G == iconId;
- }
-
- private boolean isQsTypeIconWide(int iconId) {
- return TelephonyIcons.QS_ICON_LTE == iconId || TelephonyIcons.QS_ICON_1X == iconId
- || TelephonyIcons.QS_ICON_3G == iconId || TelephonyIcons.QS_ICON_4G == iconId;
- }
-
- public void refreshSignalCluster(SignalCluster cluster) {
- if (mDemoMode) return;
- cluster.setWifiIndicators(
- // only show wifi in the cluster if connected or if wifi-only
- mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature),
- mWifiIconId,
- mContentDescriptionWifi);
-
- if (mIsWimaxEnabled && mWimaxConnected) {
- // wimax is special
- cluster.setMobileDataIndicators(
- true,
- mAlwaysShowCdmaRssi ? mPhoneSignalIconId : mWimaxIconId,
- mDataTypeIconId,
- mContentDescriptionWimax,
- mContentDescriptionDataType,
- false /* isTypeIconWide */ );
- } else {
- // normal mobile data
- cluster.setMobileDataIndicators(
- mHasMobileDataFeature,
- mShowPhoneRSSIForData ? mPhoneSignalIconId : mDataSignalIconId,
- mDataTypeIconId,
- mContentDescriptionPhoneSignal,
- mContentDescriptionDataType,
- isTypeIconWide(mDataTypeIconId));
- }
- cluster.setIsAirplaneMode(mAirplaneMode, mAirplaneIconId);
- }
-
- void notifySignalsChangedCallbacks(NetworkSignalChangedCallback cb) {
- // only show wifi in the cluster if connected or if wifi-only
- boolean wifiEnabled = mWifiEnabled && (mWifiConnected || !mHasMobileDataFeature);
- String wifiDesc = wifiEnabled ?
- mWifiSsid : null;
- boolean wifiIn = wifiEnabled && mWifiSsid != null
- && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT
- || mWifiActivity == WifiManager.DATA_ACTIVITY_IN);
- boolean wifiOut = wifiEnabled && mWifiSsid != null
- && (mWifiActivity == WifiManager.DATA_ACTIVITY_INOUT
- || mWifiActivity == WifiManager.DATA_ACTIVITY_OUT);
- cb.onWifiSignalChanged(mWifiEnabled, mWifiConnected, mQSWifiIconId, wifiIn, wifiOut,
- mContentDescriptionWifi, wifiDesc);
-
- boolean mobileIn = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT
- || mDataActivity == TelephonyManager.DATA_ACTIVITY_IN);
- boolean mobileOut = mDataConnected && (mDataActivity == TelephonyManager.DATA_ACTIVITY_INOUT
- || mDataActivity == TelephonyManager.DATA_ACTIVITY_OUT);
- if (isEmergencyOnly()) {
- cb.onMobileDataSignalChanged(false, mQSPhoneSignalIconId,
- mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
- mContentDescriptionDataType, null, mNoSim, isQsTypeIconWide(mQSDataTypeIconId));
- } else {
- if (mIsWimaxEnabled && mWimaxConnected) {
- // Wimax is special
- cb.onMobileDataSignalChanged(true, mQSPhoneSignalIconId,
- mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
- mContentDescriptionDataType, mNetworkName, mNoSim,
- isQsTypeIconWide(mQSDataTypeIconId));
- } else {
- // Normal mobile data
- cb.onMobileDataSignalChanged(mHasMobileDataFeature, mQSPhoneSignalIconId,
- mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
- mContentDescriptionDataType, mNetworkName, mNoSim,
- isQsTypeIconWide(mQSDataTypeIconId));
- }
- }
- cb.onAirplaneModeChanged(mAirplaneMode);
- }
-
- public void setStackedMode(boolean stacked) {
- mDataAndWifiStacked = true;
- }
-
- @Override
public void onReceive(Context context, Intent intent) {
+ if (CHATTY) {
+ Log.d(TAG, "onReceive: intent=" + intent);
+ }
final String action = intent.getAction();
- if (action.equals(WifiManager.RSSI_CHANGED_ACTION)
- || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)
- || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- updateWifiState(intent);
- refreshViews();
- } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
- updateSimState(intent);
- updateDataIcon();
- refreshViews();
- } else if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
- updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
- intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
- intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
- intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
- refreshViews();
- } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE) ||
- action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
+ if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE) ||
+ action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
updateConnectivity(intent);
- refreshViews();
+ refreshCarrierLabel();
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
refreshLocale();
- refreshViews();
+ refreshCarrierLabel();
} else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
refreshLocale();
- updateAirplaneMode();
- refreshViews();
- } else if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION) ||
- action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION) ||
- action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) {
- updateWimaxState(intent);
- refreshViews();
+ updateAirplaneMode(false);
+ refreshCarrierLabel();
}
+ mWifiSignalController.handleBroadcast(intent);
+ mMobileSignalController.handleBroadcast(intent);
}
-
- // ===== Telephony ==============================================================
-
- PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- if (DEBUG) {
- Log.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength +
- ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
- }
- mSignalStrength = signalStrength;
- updateTelephonySignalStrength();
- refreshViews();
+ private void updateAirplaneMode(boolean force) {
+ boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
+ if (airplaneMode != mAirplaneMode || force) {
+ mAirplaneMode = airplaneMode;
+ mMobileSignalController.setAirplaneMode(mAirplaneMode);
+ notifyAirplaneCallbacks();
+ refreshCarrierLabel();
}
-
- @Override
- public void onServiceStateChanged(ServiceState state) {
- if (DEBUG) {
- Log.d(TAG, "onServiceStateChanged voiceState=" + state.getVoiceRegState()
- + " dataState=" + state.getDataRegState());
- }
- mServiceState = state;
- updateTelephonySignalStrength();
- updateDataNetType();
- updateDataIcon();
- refreshViews();
- }
-
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- if (DEBUG) {
- Log.d(TAG, "onCallStateChanged state=" + state);
- }
- // In cdma, if a voice call is made, RSSI should switch to 1x.
- if (isCdma()) {
- updateTelephonySignalStrength();
- refreshViews();
- }
- }
-
- @Override
- public void onDataConnectionStateChanged(int state, int networkType) {
- if (DEBUG) {
- Log.d(TAG, "onDataConnectionStateChanged: state=" + state
- + " type=" + networkType);
- }
- mDataState = state;
- mDataNetType = networkType;
- updateDataNetType();
- updateDataIcon();
- refreshViews();
- }
-
- @Override
- public void onDataActivity(int direction) {
- if (DEBUG) {
- Log.d(TAG, "onDataActivity: direction=" + direction);
- }
- mDataActivity = direction;
- updateDataIcon();
- refreshViews();
- }
- };
-
- private final void updateSimState(Intent intent) {
- String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
- if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
- mSimState = IccCardConstants.State.ABSENT;
- }
- else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
- mSimState = IccCardConstants.State.READY;
- }
- else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
- final String lockedReason =
- intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
- if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
- mSimState = IccCardConstants.State.PIN_REQUIRED;
- }
- else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
- mSimState = IccCardConstants.State.PUK_REQUIRED;
- }
- else {
- mSimState = IccCardConstants.State.NETWORK_LOCKED;
- }
- } else {
- mSimState = IccCardConstants.State.UNKNOWN;
- }
- if (DEBUG) Log.d(TAG, "updateSimState: mSimState=" + mSimState);
- }
-
- private boolean isCdma() {
- return (mSignalStrength != null) && !mSignalStrength.isGsm();
- }
-
- private boolean hasService() {
- boolean retVal;
- if (mServiceState != null) {
- // Consider the device to be in service if either voice or data service is available.
- // Some SIM cards are marketed as data-only and do not support voice service, and on
- // these SIM cards, we want to show signal bars for data service as well as the "no
- // service" or "emergency calls only" text that indicates that voice is not available.
- switch(mServiceState.getVoiceRegState()) {
- case ServiceState.STATE_POWER_OFF:
- retVal = false;
- break;
- case ServiceState.STATE_OUT_OF_SERVICE:
- case ServiceState.STATE_EMERGENCY_ONLY:
- retVal = mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE;
- break;
- default:
- retVal = true;
- }
- } else {
- retVal = false;
- }
- if (DEBUG) Log.d(TAG, "hasService: mServiceState=" + mServiceState + " retVal=" + retVal);
- return retVal;
- }
-
- private void updateAirplaneMode() {
- mAirplaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
}
private void refreshLocale() {
- mLocale = mContext.getResources().getConfiguration().locale;
- }
-
- private final void updateTelephonySignalStrength() {
- if (DEBUG) {
- Log.d(TAG, "updateTelephonySignalStrength: hasService=" + hasService()
- + " ss=" + mSignalStrength);
- }
- if (!hasService()) {
- if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: !hasService()");
- mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
- mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal;
- mDataSignalIconId = R.drawable.stat_sys_signal_null;
- mContentDescriptionPhoneSignal = mContext.getString(
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]);
- } else {
- if (mSignalStrength == null) {
- if (CHATTY) Log.d(TAG, "updateTelephonySignalStrength: mSignalStrength == null");
- mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
- mQSPhoneSignalIconId = R.drawable.ic_qs_signal_no_signal;
- mDataSignalIconId = R.drawable.stat_sys_signal_null;
- mContentDescriptionPhoneSignal = mContext.getString(
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]);
- } else {
- int iconLevel;
- int[] iconList;
- if (isCdma() && mAlwaysShowCdmaRssi) {
- mLastSignalLevel = iconLevel = mSignalStrength.getCdmaLevel();
- if (DEBUG) {
- Log.d(TAG, "updateTelephonySignalStrength:"
- + " mAlwaysShowCdmaRssi=" + mAlwaysShowCdmaRssi
- + " set to cdmaLevel=" + mSignalStrength.getCdmaLevel()
- + " instead of level=" + mSignalStrength.getLevel());
- }
- } else {
- mLastSignalLevel = iconLevel = mSignalStrength.getLevel();
- }
-
- if (isRoaming()) {
- iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[mInetCondition];
- } else {
- iconList = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[mInetCondition];
- }
- mPhoneSignalIconId = iconList[iconLevel];
- mQSPhoneSignalIconId =
- TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[mInetCondition][iconLevel];
- mContentDescriptionPhoneSignal = mContext.getString(
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[iconLevel]);
- mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel];
- if (DEBUG) Log.d(TAG, "updateTelephonySignalStrength: iconLevel=" + iconLevel);
- }
+ Locale current = mContext.getResources().getConfiguration().locale;
+ if (current.equals(mLocale)) {
+ mLocale = current;
+ notifyAllListeners();
}
}
- private int inetConditionForNetwork(int networkType) {
- return (mInetCondition == 1 && mConnectedNetworkType == networkType) ? 1 : 0;
+ /**
+ * Turns inet condition into a boolean indexing for a specific network.
+ * returns 0 for bad connectivity on this network.
+ * returns 1 for good connectivity on this network.
+ */
+ private int inetConditionForNetwork(int networkType, boolean inetCondition) {
+ return (inetCondition && mConnectedNetworkType == networkType) ? 1 : 0;
}
- private final void updateDataNetType() {
- int inetCondition;
- mDataTypeIconId = mQSDataTypeIconId = 0;
- if (mIsWimaxEnabled && mWimaxConnected) {
- // wimax is a special 4g network not handled by telephony
- inetCondition = inetConditionForNetwork(ConnectivityManager.TYPE_WIMAX);
- mDataIconList = TelephonyIcons.DATA_4G[inetCondition];
- mDataTypeIconId = R.drawable.stat_sys_data_fully_connected_4g;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_4g);
- } else {
- inetCondition = inetConditionForNetwork(ConnectivityManager.TYPE_MOBILE);
- final boolean showDataTypeIcon = (inetCondition > 0);
- switch (mDataNetType) {
- case TelephonyManager.NETWORK_TYPE_UNKNOWN:
- if (!mShowAtLeastThreeGees) {
- mDataIconList = TelephonyIcons.DATA_G[inetCondition];
- mContentDescriptionDataType = "";
- break;
- } else {
- // fall through
- }
- case TelephonyManager.NETWORK_TYPE_EDGE:
- if (!mShowAtLeastThreeGees) {
- mDataIconList = TelephonyIcons.DATA_E[inetCondition];
- mDataTypeIconId = showDataTypeIcon ?
- R.drawable.stat_sys_data_fully_connected_e : 0;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_E[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_edge);
- break;
- } else {
- // fall through
- }
- case TelephonyManager.NETWORK_TYPE_UMTS:
- mDataIconList = TelephonyIcons.DATA_3G[inetCondition];
- mDataTypeIconId = showDataTypeIcon ?
- R.drawable.stat_sys_data_fully_connected_3g : 0;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_3g);
- break;
- case TelephonyManager.NETWORK_TYPE_HSDPA:
- case TelephonyManager.NETWORK_TYPE_HSUPA:
- case TelephonyManager.NETWORK_TYPE_HSPA:
- case TelephonyManager.NETWORK_TYPE_HSPAP:
- if (mHspaDataDistinguishable) {
- mDataIconList = TelephonyIcons.DATA_H[inetCondition];
- mDataTypeIconId = showDataTypeIcon ?
- R.drawable.stat_sys_data_fully_connected_h : 0;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_H[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_3_5g);
- } else {
- mDataIconList = TelephonyIcons.DATA_3G[inetCondition];
- mDataTypeIconId = showDataTypeIcon ?
- R.drawable.stat_sys_data_fully_connected_3g : 0;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_3g);
- }
- break;
- case TelephonyManager.NETWORK_TYPE_CDMA:
- if (!mShowAtLeastThreeGees) {
- // display 1xRTT for IS95A/B
- mDataIconList = TelephonyIcons.DATA_1X[inetCondition];
- mDataTypeIconId = showDataTypeIcon ?
- R.drawable.stat_sys_data_fully_connected_1x : 0;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_cdma);
- break;
- } else {
- // fall through
- }
- case TelephonyManager.NETWORK_TYPE_1xRTT:
- if (!mShowAtLeastThreeGees) {
- mDataIconList = TelephonyIcons.DATA_1X[inetCondition];
- mDataTypeIconId = showDataTypeIcon ?
- R.drawable.stat_sys_data_fully_connected_1x : 0;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_1X[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_cdma);
- break;
- } else {
- // fall through
- }
- case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
- case TelephonyManager.NETWORK_TYPE_EVDO_A:
- case TelephonyManager.NETWORK_TYPE_EVDO_B:
- case TelephonyManager.NETWORK_TYPE_EHRPD:
- mDataIconList = TelephonyIcons.DATA_3G[inetCondition];
- mDataTypeIconId = showDataTypeIcon ?
- R.drawable.stat_sys_data_fully_connected_3g : 0;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_3g);
- break;
- case TelephonyManager.NETWORK_TYPE_LTE:
- boolean show4GforLTE = mContext.getResources().getBoolean(R.bool.config_show4GForLTE);
- if (show4GforLTE) {
- mDataIconList = TelephonyIcons.DATA_4G[inetCondition];
- mDataTypeIconId = showDataTypeIcon ?
- R.drawable.stat_sys_data_fully_connected_4g : 0;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_4G[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_4g);
- } else {
- mDataIconList = TelephonyIcons.DATA_LTE[inetCondition];
- mDataTypeIconId = showDataTypeIcon ? TelephonyIcons.ICON_LTE : 0;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_LTE[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_lte);
- }
- break;
- default:
- if (!mShowAtLeastThreeGees) {
- mDataIconList = TelephonyIcons.DATA_G[inetCondition];
- mDataTypeIconId = showDataTypeIcon ?
- R.drawable.stat_sys_data_fully_connected_g : 0;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_G[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_gprs);
- } else {
- mDataIconList = TelephonyIcons.DATA_3G[inetCondition];
- mDataTypeIconId = showDataTypeIcon ?
- R.drawable.stat_sys_data_fully_connected_3g : 0;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_3G[inetCondition];
- mContentDescriptionDataType = mContext.getString(
- R.string.accessibility_data_connection_3g);
- }
- break;
- }
- }
+ private void notifyAllListeners() {
+ // Something changed, trigger everything!
+ notifyAirplaneCallbacks();
+ mMobileSignalController.notifyListeners();
+ mWifiSignalController.notifyListeners();
+ }
- if (isRoaming()) {
- mDataTypeIconId = TelephonyIcons.ROAMING_ICON;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
+ private void notifyAirplaneCallbacks() {
+ int length = mSignalClusters.size();
+ for (int i = 0; i < length; i++) {
+ mSignalClusters.get(i).setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON,
+ R.string.accessibility_airplane_mode);
+ }
+ // update QS
+ int signalsChangedLength = mSignalsChangedCallbacks.size();
+ for (int i = 0; i < signalsChangedLength; i++) {
+ mSignalsChangedCallbacks.get(i).onAirplaneModeChanged(mAirplaneMode);
}
}
- boolean isCdmaEri() {
- if (mServiceState != null) {
- final int iconIndex = mServiceState.getCdmaEriIconIndex();
- if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) {
- final int iconMode = mServiceState.getCdmaEriIconMode();
- if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
- || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean isRoaming() {
- if (isCdma()) {
- return isCdmaEri();
- } else {
- return mServiceState != null && mServiceState.getRoaming();
- }
- }
-
- private final void updateDataIcon() {
- int iconId;
- boolean visible = true;
-
- if (!isCdma()) {
- // GSM case, we have to check also the sim state
- if (mSimState == IccCardConstants.State.READY ||
- mSimState == IccCardConstants.State.UNKNOWN) {
- mNoSim = false;
- if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
- switch (mDataActivity) {
- case TelephonyManager.DATA_ACTIVITY_IN:
- iconId = mDataIconList[1];
- break;
- case TelephonyManager.DATA_ACTIVITY_OUT:
- iconId = mDataIconList[2];
- break;
- case TelephonyManager.DATA_ACTIVITY_INOUT:
- iconId = mDataIconList[3];
- break;
- default:
- iconId = mDataIconList[0];
- break;
- }
- mDataDirectionIconId = iconId;
- } else {
- iconId = 0;
- visible = false;
- }
- } else {
- iconId = 0;
- mNoSim = true;
- visible = false; // no SIM? no data
- }
- } else {
- // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT
- if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
- switch (mDataActivity) {
- case TelephonyManager.DATA_ACTIVITY_IN:
- iconId = mDataIconList[1];
- break;
- case TelephonyManager.DATA_ACTIVITY_OUT:
- iconId = mDataIconList[2];
- break;
- case TelephonyManager.DATA_ACTIVITY_INOUT:
- iconId = mDataIconList[3];
- break;
- case TelephonyManager.DATA_ACTIVITY_DORMANT:
- default:
- iconId = mDataIconList[0];
- break;
- }
- } else {
- iconId = 0;
- visible = false;
- }
- }
-
- mDataDirectionIconId = iconId;
- mDataConnected = visible;
- }
-
- void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
- if (false) {
- Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
- + " showPlmn=" + showPlmn + " plmn=" + plmn);
- }
- StringBuilder str = new StringBuilder();
- boolean something = false;
- if (showPlmn && plmn != null) {
- str.append(plmn);
- something = true;
- }
- if (showSpn && spn != null) {
- if (something) {
- str.append(mNetworkNameSeparator);
- }
- str.append(spn);
- something = true;
- }
- if (something) {
- mNetworkName = str.toString();
- } else {
- mNetworkName = mNetworkNameDefault;
- }
- }
-
- // ===== Wifi ===================================================================
-
- class WifiHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- mWifiChannel.sendMessage(Message.obtain(this,
- AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
- } else {
- Log.e(TAG, "Failed to connect to wifi");
- }
- break;
- case WifiManager.DATA_ACTIVITY_NOTIFICATION:
- if (msg.arg1 != mWifiActivity) {
- mWifiActivity = msg.arg1;
- refreshViews();
- }
- break;
- default:
- //Ignore
- break;
- }
- }
- }
-
- private void updateWifiState(Intent intent) {
- final String action = intent.getAction();
- if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
- mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
-
- } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- final NetworkInfo networkInfo = (NetworkInfo)
- intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
- boolean wasConnected = mWifiConnected;
- mWifiConnected = networkInfo != null && networkInfo.isConnected();
- // If Connected grab the signal strength and ssid
- if (mWifiConnected) {
- // try getting it out of the intent first
- WifiInfo info = (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
- if (info == null) {
- info = mWifiManager.getConnectionInfo();
- }
- if (info != null) {
- mWifiSsid = huntForSsid(info);
- } else {
- mWifiSsid = null;
- }
- } else if (!mWifiConnected) {
- mWifiSsid = null;
- }
- } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
- mWifiLevel = WifiManager.calculateSignalLevel(
- mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT);
- }
-
- updateWifiIcons();
- }
-
- private void updateWifiIcons() {
- int inetCondition = inetConditionForNetwork(ConnectivityManager.TYPE_WIFI);
- if (mWifiConnected) {
- mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[inetCondition][mWifiLevel];
- mQSWifiIconId = WifiIcons.QS_WIFI_SIGNAL_STRENGTH[inetCondition][mWifiLevel];
- mContentDescriptionWifi = mContext.getString(
- AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[mWifiLevel]);
- } else {
- if (mDataAndWifiStacked) {
- mWifiIconId = 0;
- mQSWifiIconId = 0;
- } else {
- mWifiIconId = mWifiEnabled ? R.drawable.stat_sys_wifi_signal_null : 0;
- mQSWifiIconId = mWifiEnabled ? R.drawable.ic_qs_wifi_no_network : 0;
- }
- mContentDescriptionWifi = mContext.getString(R.string.accessibility_no_wifi);
- }
- }
-
- private String huntForSsid(WifiInfo info) {
- String ssid = info.getSSID();
- if (ssid != null) {
- return ssid;
- }
- // OK, it's not in the connectionInfo; we have to go hunting for it
- List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks();
- for (WifiConfiguration net : networks) {
- if (net.networkId == info.getNetworkId()) {
- return net.SSID;
- }
- }
- return null;
- }
-
-
- // ===== Wimax ===================================================================
- private final void updateWimaxState(Intent intent) {
- final String action = intent.getAction();
- boolean wasConnected = mWimaxConnected;
- if (action.equals(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION)) {
- int wimaxStatus = intent.getIntExtra(WimaxManagerConstants.EXTRA_4G_STATE,
- WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
- mIsWimaxEnabled = (wimaxStatus ==
- WimaxManagerConstants.NET_4G_STATE_ENABLED);
- } else if (action.equals(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION)) {
- mWimaxSignal = intent.getIntExtra(WimaxManagerConstants.EXTRA_NEW_SIGNAL_LEVEL, 0);
- } else if (action.equals(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION)) {
- mWimaxState = intent.getIntExtra(WimaxManagerConstants.EXTRA_WIMAX_STATE,
- WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
- mWimaxExtraState = intent.getIntExtra(
- WimaxManagerConstants.EXTRA_WIMAX_STATE_DETAIL,
- WimaxManagerConstants.NET_4G_STATE_UNKNOWN);
- mWimaxConnected = (mWimaxState ==
- WimaxManagerConstants.WIMAX_STATE_CONNECTED);
- mWimaxIdle = (mWimaxExtraState == WimaxManagerConstants.WIMAX_IDLE);
- }
- updateDataNetType();
- updateWimaxIcons();
- }
-
- private void updateWimaxIcons() {
- if (mIsWimaxEnabled) {
- if (mWimaxConnected) {
- int inetCondition = inetConditionForNetwork(ConnectivityManager.TYPE_WIMAX);
- if (mWimaxIdle)
- mWimaxIconId = WimaxIcons.WIMAX_IDLE;
- else
- mWimaxIconId = WimaxIcons.WIMAX_SIGNAL_STRENGTH[inetCondition][mWimaxSignal];
- mContentDescriptionWimax = mContext.getString(
- AccessibilityContentDescriptions.WIMAX_CONNECTION_STRENGTH[mWimaxSignal]);
- } else {
- mWimaxIconId = WimaxIcons.WIMAX_DISCONNECTED;
- mContentDescriptionWimax = mContext.getString(R.string.accessibility_no_wimax);
- }
- } else {
- mWimaxIconId = 0;
- }
- }
-
- // ===== Full or limited Internet connectivity ==================================
-
+ /**
+ * Update the Inet conditions and what network we are connected to.
+ */
private void updateConnectivity(Intent intent) {
- if (CHATTY) {
- Log.d(TAG, "updateConnectivity: intent=" + intent);
- }
-
final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
// Are we connected at all, by any interface?
@@ -1108,7 +364,7 @@
Log.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus);
}
- mInetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
+ mInetCondition = connectionStatus > INET_CONDITION_THRESHOLD;
if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) {
mBluetoothTethered = info.isConnected();
@@ -1117,376 +373,68 @@
}
// We want to update all the icons, all at once, for any condition change
- updateDataNetType();
- updateWimaxIcons();
- updateDataIcon();
- updateTelephonySignalStrength();
- updateWifiIcons();
+ mMobileSignalController.setInetCondition(mInetCondition ? 1 : 0,
+ inetConditionForNetwork(mMobileSignalController.getNetworkType(), mInetCondition));
+ mWifiSignalController.setInetCondition(
+ inetConditionForNetwork(mWifiSignalController.getNetworkType(), mInetCondition));
}
-
- // ===== Update the views =======================================================
-
- void refreshViews() {
+ /**
+ * Recalculate and update the carrier label.
+ */
+ void refreshCarrierLabel() {
Context context = mContext;
- int combinedSignalIconId = 0;
- String combinedLabel = "";
- String wifiLabel = "";
- String mobileLabel = "";
- int N;
- final boolean emergencyOnly = isEmergencyOnly();
+ WifiSignalController.WifiState wifiState = mWifiSignalController.getState();
+ MobileSignalController.MobileState mobileState = mMobileSignalController.getState();
+ String label = mMobileSignalController.getLabel("", mConnected, mHasMobileDataFeature);
- if (!mHasMobileDataFeature) {
- mDataSignalIconId = mPhoneSignalIconId = 0;
- mQSPhoneSignalIconId = 0;
- mobileLabel = "";
- } else {
- // We want to show the carrier name if in service and either:
- // - We are connected to mobile data, or
- // - We are not connected to mobile data, as long as the *reason* packets are not
- // being routed over that link is that we have better connectivity via wifi.
- // If data is disconnected for some other reason but wifi (or ethernet/bluetooth)
- // is connected, we show nothing.
- // Otherwise (nothing connected) we show "No internet connection".
-
- if (mDataConnected) {
- mobileLabel = mNetworkName;
- } else if (mConnected || emergencyOnly) {
- if (hasService() || emergencyOnly) {
- // The isEmergencyOnly test covers the case of a phone with no SIM
- mobileLabel = mNetworkName;
- } else {
- // Tablets, basically
- mobileLabel = "";
- }
- } else {
- mobileLabel
- = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
- }
-
- // Now for things that should only be shown when actually using mobile data.
- if (mDataConnected) {
- combinedSignalIconId = mDataSignalIconId;
-
- combinedLabel = mobileLabel;
- combinedSignalIconId = mDataSignalIconId; // set by updateDataIcon()
- mContentDescriptionCombinedSignal = mContentDescriptionDataType;
- }
+ // TODO Simplify this ugliness, some of the flows below shouldn't be possible anymore
+ // but stay for the sake of history.
+ if (mBluetoothTethered && !mHasMobileDataFeature) {
+ label = mContext.getString(R.string.bluetooth_tethered);
}
- if (mWifiConnected) {
- if (mWifiSsid == null) {
- wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_wifi_nossid);
- } else {
- wifiLabel = mWifiSsid;
- if (DEBUG) {
- wifiLabel += "xxxxXXXXxxxxXXXX";
- }
- }
-
- combinedLabel = wifiLabel;
- combinedSignalIconId = mWifiIconId; // set by updateWifiIcons()
- mContentDescriptionCombinedSignal = mContentDescriptionWifi;
- } else {
- if (mHasMobileDataFeature) {
- wifiLabel = "";
- } else {
- wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
- }
+ final boolean ethernetConnected =
+ (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET);
+ if (ethernetConnected && !mHasMobileDataFeature) {
+ label = context.getString(R.string.ethernet_label);
}
- if (mBluetoothTethered) {
- combinedLabel = mContext.getString(R.string.bluetooth_tethered);
- combinedSignalIconId = mBluetoothTetherIconId;
- mContentDescriptionCombinedSignal = mContext.getString(
- R.string.accessibility_bluetooth_tether);
- }
-
- final boolean ethernetConnected = (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET);
- if (ethernetConnected) {
- combinedLabel = context.getString(R.string.ethernet_label);
- }
-
- if (mAirplaneMode &&
- (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) {
- // Only display the flight-mode icon if not in "emergency calls only" mode.
-
- // look again; your radios are now airplanes
- mContentDescriptionPhoneSignal = mContext.getString(
- R.string.accessibility_airplane_mode);
- mAirplaneIconId = TelephonyIcons.FLIGHT_MODE_ICON;
- mPhoneSignalIconId = mDataSignalIconId = mDataTypeIconId = mQSDataTypeIconId = 0;
- mQSPhoneSignalIconId = 0;
-
+ if (mAirplaneMode && (!mobileState.connected && !mobileState.isEmergency)) {
// combined values from connected wifi take precedence over airplane mode
- if (mWifiConnected) {
+ if (wifiState.connected && mHasMobileDataFeature) {
// Suppress "No internet connection." from mobile if wifi connected.
- mobileLabel = "";
+ label = "";
} else {
- if (mHasMobileDataFeature) {
- // let the mobile icon show "No internet connection."
- wifiLabel = "";
- } else {
- wifiLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
- combinedLabel = wifiLabel;
- }
- mContentDescriptionCombinedSignal = mContentDescriptionPhoneSignal;
- combinedSignalIconId = mDataSignalIconId;
+ if (!mHasMobileDataFeature) {
+ label = context.getString(
+ R.string.status_bar_settings_signal_meter_disconnected);
+ }
}
- }
- else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected && !ethernetConnected) {
- // pretty much totally disconnected
-
- combinedLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
- // On devices without mobile radios, we want to show the wifi icon
- combinedSignalIconId =
- mHasMobileDataFeature ? mDataSignalIconId : mWifiIconId;
- mContentDescriptionCombinedSignal = mHasMobileDataFeature
- ? mContentDescriptionDataType : mContentDescriptionWifi;
-
- int inetCondition = inetConditionForNetwork(ConnectivityManager.TYPE_MOBILE);
-
- mDataTypeIconId = 0;
- mQSDataTypeIconId = 0;
- if (isRoaming()) {
- mDataTypeIconId = TelephonyIcons.ROAMING_ICON;
- mQSDataTypeIconId = TelephonyIcons.QS_DATA_R[mInetCondition];
- }
+ } else if (!mobileState.dataConnected && !wifiState.connected && !mBluetoothTethered &&
+ !ethernetConnected && !mHasMobileDataFeature) {
+ // Pretty much no connection.
+ label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
}
- if (mDemoMode) {
- mQSWifiIconId = mDemoWifiLevel < 0 ? R.drawable.ic_qs_wifi_no_network
- : WifiIcons.QS_WIFI_SIGNAL_STRENGTH[mDemoInetCondition][mDemoWifiLevel];
- mQSPhoneSignalIconId = mDemoMobileLevel < 0 ? R.drawable.ic_qs_signal_no_signal :
- TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[mDemoInetCondition][mDemoMobileLevel];
- mQSDataTypeIconId = mDemoQSDataTypeIconId;
- }
-
- if (DEBUG) {
- Log.d(TAG, "refreshViews connected={"
- + (mWifiConnected?" wifi":"")
- + (mDataConnected?" data":"")
- + " } level="
- + ((mSignalStrength == null)?"??":Integer.toString(mSignalStrength.getLevel()))
- + " combinedSignalIconId=0x"
- + Integer.toHexString(combinedSignalIconId)
- + "/" + getResourceName(combinedSignalIconId)
- + " mobileLabel=" + mobileLabel
- + " wifiLabel=" + wifiLabel
- + " emergencyOnly=" + emergencyOnly
- + " combinedLabel=" + combinedLabel
- + " mAirplaneMode=" + mAirplaneMode
- + " mDataActivity=" + mDataActivity
- + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId)
- + " mQSPhoneSignalIconId=0x" + Integer.toHexString(mQSPhoneSignalIconId)
- + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId)
- + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId)
- + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId)
- + " mQSDataTypeIconId=0x" + Integer.toHexString(mQSDataTypeIconId)
- + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId)
- + " mQSWifiIconId=0x" + Integer.toHexString(mQSWifiIconId)
- + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId));
- }
-
- // update QS
- for (NetworkSignalChangedCallback cb : mSignalsChangedCallbacks) {
- notifySignalsChangedCallbacks(cb);
- }
-
- if (mLastPhoneSignalIconId != mPhoneSignalIconId
- || mLastWifiIconId != mWifiIconId
- || mLastInetCondition != mInetCondition
- || mLastWimaxIconId != mWimaxIconId
- || mLastDataTypeIconId != mDataTypeIconId
- || mLastAirplaneMode != mAirplaneMode
- || mLastLocale != mLocale
- || mLastConnectedNetworkType != mConnectedNetworkType)
- {
- // NB: the mLast*s will be updated later
- for (SignalCluster cluster : mSignalClusters) {
- refreshSignalCluster(cluster);
- }
- }
-
- if (mLastAirplaneMode != mAirplaneMode) {
- mLastAirplaneMode = mAirplaneMode;
- }
-
- if (mLastLocale != mLocale) {
- mLastLocale = mLocale;
- }
-
- // the phone icon on phones
- if (mLastPhoneSignalIconId != mPhoneSignalIconId) {
- mLastPhoneSignalIconId = mPhoneSignalIconId;
- }
-
- // the data icon on phones
- if (mLastDataDirectionIconId != mDataDirectionIconId) {
- mLastDataDirectionIconId = mDataDirectionIconId;
- }
-
- // the wifi icon on phones
- if (mLastWifiIconId != mWifiIconId) {
- mLastWifiIconId = mWifiIconId;
- }
-
- if (mLastInetCondition != mInetCondition) {
- mLastInetCondition = mInetCondition;
- }
-
- if (mLastConnectedNetworkType != mConnectedNetworkType) {
- mLastConnectedNetworkType = mConnectedNetworkType;
- }
-
- // the wimax icon on phones
- if (mLastWimaxIconId != mWimaxIconId) {
- mLastWimaxIconId = mWimaxIconId;
- }
- // the combined data signal icon
- if (mLastCombinedSignalIconId != combinedSignalIconId) {
- mLastCombinedSignalIconId = combinedSignalIconId;
- }
-
- // the data network type overlay
- if (mLastDataTypeIconId != mDataTypeIconId) {
- mLastDataTypeIconId = mDataTypeIconId;
- }
-
- // the combinedLabel in the notification panel
- if (!mLastCombinedLabel.equals(combinedLabel)) {
- mLastCombinedLabel = combinedLabel;
- N = mCombinedLabelViews.size();
- for (int i=0; i<N; i++) {
- TextView v = mCombinedLabelViews.get(i);
- v.setText(combinedLabel);
- }
- }
-
- // wifi label
- N = mWifiLabelViews.size();
- for (int i=0; i<N; i++) {
- TextView v = mWifiLabelViews.get(i);
- v.setText(wifiLabel);
- if ("".equals(wifiLabel)) {
- v.setVisibility(View.GONE);
- } else {
- v.setVisibility(View.VISIBLE);
- }
- }
-
- // mobile label
- N = mMobileLabelViews.size();
- for (int i=0; i<N; i++) {
- TextView v = mMobileLabelViews.get(i);
- v.setText(mobileLabel);
- if ("".equals(mobileLabel)) {
- v.setVisibility(View.GONE);
- } else {
- v.setVisibility(View.VISIBLE);
- }
- }
-
- // e-call label
- N = mEmergencyViews.size();
- for (int i=0; i<N; i++) {
- StatusBarHeaderView v = mEmergencyViews.get(i);
- v.setShowEmergencyCallsOnly(emergencyOnly);
+ // for mobile devices, we always show mobile connection info here (SPN/PLMN)
+ // for other devices, we show whatever network is connected
+ // This is determined above by references to mHasMobileDataFeature.
+ int length = mCarrierListeners.size();
+ for (int i = 0; i < length; i++) {
+ mCarrierListeners.get(i).setCarrierLabel(label);
}
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NetworkController state:");
pw.println(String.format(" %s network type %d (%s)",
- mConnected?"CONNECTED":"DISCONNECTED",
+ mConnected ? "CONNECTED" : "DISCONNECTED",
mConnectedNetworkType, mConnectedNetworkTypeName));
pw.println(" - telephony ------");
pw.print(" hasVoiceCallingFeature()=");
pw.println(hasVoiceCallingFeature());
- pw.print(" hasService()=");
- pw.println(hasService());
- pw.print(" mHspaDataDistinguishable=");
- pw.println(mHspaDataDistinguishable);
- pw.print(" mDataConnected=");
- pw.println(mDataConnected);
- pw.print(" mSimState=");
- pw.println(mSimState);
- pw.print(" mPhoneState=");
- pw.println(mPhoneState);
- pw.print(" mDataState=");
- pw.println(mDataState);
- pw.print(" mDataActivity=");
- pw.println(mDataActivity);
- pw.print(" mDataNetType=");
- pw.print(mDataNetType);
- pw.print("/");
- pw.println(TelephonyManager.getNetworkTypeName(mDataNetType));
- pw.print(" mServiceState=");
- pw.println(mServiceState);
- pw.print(" mSignalStrength=");
- pw.println(mSignalStrength);
- pw.print(" mLastSignalLevel=");
- pw.println(mLastSignalLevel);
- pw.print(" mNetworkName=");
- pw.println(mNetworkName);
- pw.print(" mNetworkNameDefault=");
- pw.println(mNetworkNameDefault);
- pw.print(" mNetworkNameSeparator=");
- pw.println(mNetworkNameSeparator.replace("\n","\\n"));
- pw.print(" mPhoneSignalIconId=0x");
- pw.print(Integer.toHexString(mPhoneSignalIconId));
- pw.print("/");
- pw.print(" mQSPhoneSignalIconId=0x");
- pw.print(Integer.toHexString(mQSPhoneSignalIconId));
- pw.print("/");
- pw.println(getResourceName(mPhoneSignalIconId));
- pw.print(" mDataDirectionIconId=");
- pw.print(Integer.toHexString(mDataDirectionIconId));
- pw.print("/");
- pw.println(getResourceName(mDataDirectionIconId));
- pw.print(" mDataSignalIconId=");
- pw.print(Integer.toHexString(mDataSignalIconId));
- pw.print("/");
- pw.println(getResourceName(mDataSignalIconId));
- pw.print(" mDataTypeIconId=");
- pw.print(Integer.toHexString(mDataTypeIconId));
- pw.print("/");
- pw.println(getResourceName(mDataTypeIconId));
- pw.print(" mQSDataTypeIconId=");
- pw.print(Integer.toHexString(mQSDataTypeIconId));
- pw.print("/");
- pw.println(getResourceName(mQSDataTypeIconId));
-
- pw.println(" - wifi ------");
- pw.print(" mWifiEnabled=");
- pw.println(mWifiEnabled);
- pw.print(" mWifiConnected=");
- pw.println(mWifiConnected);
- pw.print(" mWifiRssi=");
- pw.println(mWifiRssi);
- pw.print(" mWifiLevel=");
- pw.println(mWifiLevel);
- pw.print(" mWifiSsid=");
- pw.println(mWifiSsid);
- pw.println(String.format(" mWifiIconId=0x%08x/%s",
- mWifiIconId, getResourceName(mWifiIconId)));
- pw.println(String.format(" mQSWifiIconId=0x%08x/%s",
- mQSWifiIconId, getResourceName(mQSWifiIconId)));
- pw.print(" mWifiActivity=");
- pw.println(mWifiActivity);
-
- if (mWimaxSupported) {
- pw.println(" - wimax ------");
- pw.print(" mIsWimaxEnabled="); pw.println(mIsWimaxEnabled);
- pw.print(" mWimaxConnected="); pw.println(mWimaxConnected);
- pw.print(" mWimaxIdle="); pw.println(mWimaxIdle);
- pw.println(String.format(" mWimaxIconId=0x%08x/%s",
- mWimaxIconId, getResourceName(mWimaxIconId)));
- pw.println(String.format(" mWimaxSignal=%d", mWimaxSignal));
- pw.println(String.format(" mWimaxState=%d", mWimaxState));
- pw.println(String.format(" mWimaxExtraState=%d", mWimaxExtraState));
- }
pw.println(" - Bluetooth ----");
pw.print(" mBtReverseTethered=");
@@ -1495,143 +443,1079 @@
pw.println(" - connectivity ------");
pw.print(" mInetCondition=");
pw.println(mInetCondition);
+ pw.print(" mAirplaneMode=");
+ pw.println(mAirplaneMode);
+ pw.print(" mLocale=");
+ pw.println(mLocale);
- pw.println(" - icons ------");
- pw.print(" mLastPhoneSignalIconId=0x");
- pw.print(Integer.toHexString(mLastPhoneSignalIconId));
- pw.print("/");
- pw.println(getResourceName(mLastPhoneSignalIconId));
- pw.print(" mLastDataDirectionIconId=0x");
- pw.print(Integer.toHexString(mLastDataDirectionIconId));
- pw.print("/");
- pw.println(getResourceName(mLastDataDirectionIconId));
- pw.print(" mLastWifiIconId=0x");
- pw.print(Integer.toHexString(mLastWifiIconId));
- pw.print("/");
- pw.println(getResourceName(mLastWifiIconId));
- pw.print(" mLastCombinedSignalIconId=0x");
- pw.print(Integer.toHexString(mLastCombinedSignalIconId));
- pw.print("/");
- pw.println(getResourceName(mLastCombinedSignalIconId));
- pw.print(" mLastDataTypeIconId=0x");
- pw.print(Integer.toHexString(mLastDataTypeIconId));
- pw.print("/");
- pw.println(getResourceName(mLastDataTypeIconId));
- pw.print(" mLastCombinedLabel=");
- pw.print(mLastCombinedLabel);
- pw.println("");
- }
-
- private String getResourceName(int resId) {
- if (resId != 0) {
- final Resources res = mContext.getResources();
- try {
- return res.getResourceName(resId);
- } catch (android.content.res.Resources.NotFoundException ex) {
- return "(unknown)";
- }
- } else {
- return "(null)";
- }
+ mMobileSignalController.dump(pw);
+ mWifiSignalController.dump(pw);
}
private boolean mDemoMode;
private int mDemoInetCondition;
- private int mDemoWifiLevel;
- private int mDemoDataTypeIconId;
- private int mDemoQSDataTypeIconId;
- private int mDemoMobileLevel;
+ private WifiSignalController.WifiState mDemoWifiState;
+ private MobileSignalController.MobileState mDemoMobileState;
@Override
public void dispatchDemoCommand(String command, Bundle args) {
if (!mDemoMode && command.equals(COMMAND_ENTER)) {
+ if (DEBUG) Log.d(TAG, "Entering demo mode");
+ unregisterListeners();
mDemoMode = true;
- mDemoWifiLevel = mWifiLevel;
- mDemoInetCondition = mInetCondition;
- mDemoDataTypeIconId = mDataTypeIconId;
- mDemoQSDataTypeIconId = mQSDataTypeIconId;
- mDemoMobileLevel = mLastSignalLevel;
+ mDemoInetCondition = mInetCondition ? 1 : 0;
+ mDemoWifiState = mWifiSignalController.getState();
+ mDemoMobileState = mMobileSignalController.getState();
} else if (mDemoMode && command.equals(COMMAND_EXIT)) {
+ if (DEBUG) Log.d(TAG, "Exiting demo mode");
mDemoMode = false;
- for (SignalCluster cluster : mSignalClusters) {
- refreshSignalCluster(cluster);
- }
- refreshViews();
+ mWifiSignalController.resetLastState();
+ mMobileSignalController.resetLastState();
+ registerListeners();
+ notifyAllListeners();
+ refreshCarrierLabel();
} else if (mDemoMode && command.equals(COMMAND_NETWORK)) {
String airplane = args.getString("airplane");
if (airplane != null) {
boolean show = airplane.equals("show");
- for (SignalCluster cluster : mSignalClusters) {
- cluster.setIsAirplaneMode(show, TelephonyIcons.FLIGHT_MODE_ICON);
+ int length = mSignalClusters.size();
+ for (int i = 0; i < length; i++) {
+ mSignalClusters.get(i).setIsAirplaneMode(show, TelephonyIcons.FLIGHT_MODE_ICON,
+ R.string.accessibility_airplane_mode);
}
}
String fully = args.getString("fully");
if (fully != null) {
mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0;
+ mWifiSignalController.setInetCondition(mDemoInetCondition);
+ mMobileSignalController.setInetCondition(mDemoInetCondition, mDemoInetCondition);
}
String wifi = args.getString("wifi");
if (wifi != null) {
boolean show = wifi.equals("show");
String level = args.getString("level");
if (level != null) {
- mDemoWifiLevel = level.equals("null") ? -1
+ mDemoWifiState.level = level.equals("null") ? -1
: Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1);
+ mDemoWifiState.connected = mDemoWifiState.level >= 0;
}
- int iconId = mDemoWifiLevel < 0 ? R.drawable.stat_sys_wifi_signal_null
- : WifiIcons.WIFI_SIGNAL_STRENGTH[mDemoInetCondition][mDemoWifiLevel];
- for (SignalCluster cluster : mSignalClusters) {
- cluster.setWifiIndicators(
- show,
- iconId,
- "Demo");
- }
- refreshViews();
+ mDemoWifiState.enabled = show;
+ mWifiSignalController.notifyListeners();
}
String mobile = args.getString("mobile");
if (mobile != null) {
boolean show = mobile.equals("show");
String datatype = args.getString("datatype");
if (datatype != null) {
- mDemoDataTypeIconId =
- datatype.equals("1x") ? TelephonyIcons.ICON_1X :
- datatype.equals("3g") ? TelephonyIcons.ICON_3G :
- datatype.equals("4g") ? TelephonyIcons.ICON_4G :
- datatype.equals("e") ? R.drawable.stat_sys_data_fully_connected_e :
- datatype.equals("g") ? R.drawable.stat_sys_data_fully_connected_g :
- datatype.equals("h") ? R.drawable.stat_sys_data_fully_connected_h :
- datatype.equals("lte") ? TelephonyIcons.ICON_LTE :
- datatype.equals("roam") ? TelephonyIcons.ROAMING_ICON :
- 0;
- mDemoQSDataTypeIconId =
- datatype.equals("1x") ? TelephonyIcons.QS_ICON_1X :
- datatype.equals("3g") ? TelephonyIcons.QS_ICON_3G :
- datatype.equals("4g") ? TelephonyIcons.QS_ICON_4G :
- datatype.equals("e") ? R.drawable.ic_qs_signal_e :
- datatype.equals("g") ? R.drawable.ic_qs_signal_g :
- datatype.equals("h") ? R.drawable.ic_qs_signal_h :
- datatype.equals("lte") ? TelephonyIcons.QS_ICON_LTE :
- datatype.equals("roam") ? R.drawable.ic_qs_signal_r :
- 0;
+ mDemoMobileState.iconGroup =
+ datatype.equals("1x") ? TelephonyIcons.ONE_X :
+ datatype.equals("3g") ? TelephonyIcons.THREE_G :
+ datatype.equals("4g") ? TelephonyIcons.FOUR_G :
+ datatype.equals("e") ? TelephonyIcons.E :
+ datatype.equals("g") ? TelephonyIcons.G :
+ datatype.equals("h") ? TelephonyIcons.H :
+ datatype.equals("lte") ? TelephonyIcons.LTE :
+ datatype.equals("roam") ? TelephonyIcons.ROAMING :
+ TelephonyIcons.UNKNOWN;
}
int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH;
String level = args.getString("level");
if (level != null) {
- mDemoMobileLevel = level.equals("null") ? -1
+ mDemoMobileState.level = level.equals("null") ? -1
: Math.min(Integer.parseInt(level), icons[0].length - 1);
+ mDemoMobileState.connected = mDemoMobileState.level >= 0;
}
- int iconId = mDemoMobileLevel < 0 ? R.drawable.stat_sys_signal_null :
- icons[mDemoInetCondition][mDemoMobileLevel];
- for (SignalCluster cluster : mSignalClusters) {
- cluster.setMobileDataIndicators(
- show,
- iconId,
- mDemoDataTypeIconId,
- "Demo",
- "Demo",
- isTypeIconWide(mDemoDataTypeIconId));
- }
- refreshViews();
+ mDemoMobileState.enabled = show;
+ mMobileSignalController.notifyListeners();
}
+ refreshCarrierLabel();
+ }
+ }
+
+ static class WifiSignalController extends
+ SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
+ private final WifiManager mWifiManager;
+ private final AsyncChannel mWifiChannel;
+ private final boolean mHasMobileData;
+
+ public WifiSignalController(Context context, boolean hasMobileData,
+ List<NetworkSignalChangedCallback> signalCallbacks,
+ List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
+ super("WifiSignalController", context, ConnectivityManager.TYPE_WIFI, signalCallbacks,
+ signalClusters, networkController);
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ mHasMobileData = hasMobileData;
+ Handler handler = new WifiHandler();
+ mWifiChannel = new AsyncChannel();
+ Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
+ if (wifiMessenger != null) {
+ mWifiChannel.connect(context, handler, wifiMessenger);
+ }
+ // WiFi only has one state.
+ mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
+ "Wi-Fi Icons",
+ WifiIcons.WIFI_SIGNAL_STRENGTH,
+ WifiIcons.QS_WIFI_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH,
+ WifiIcons.WIFI_NO_NETWORK,
+ WifiIcons.QS_WIFI_NO_NETWORK,
+ WifiIcons.WIFI_NO_NETWORK,
+ WifiIcons.QS_WIFI_NO_NETWORK,
+ AccessibilityContentDescriptions.WIFI_NO_CONNECTION
+ );
+ }
+
+ @Override
+ public WifiState cleanState() {
+ return new WifiState();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void notifyListeners() {
+ // only show wifi in the cluster if connected or if wifi-only
+ boolean wifiEnabled = mCurrentState.enabled
+ && (mCurrentState.connected || !mHasMobileData);
+ String wifiDesc = wifiEnabled ? mCurrentState.ssid : null;
+ boolean ssidPresent = wifiEnabled && mCurrentState.ssid != null;
+ String contentDescription = getStringIfExists(getContentDescription());
+ int length = mSignalsChangedCallbacks.size();
+ for (int i = 0; i < length; i++) {
+ mSignalsChangedCallbacks.get(i).onWifiSignalChanged(mCurrentState.enabled,
+ mCurrentState.connected, getQsCurrentIconId(),
+ ssidPresent && mCurrentState.activityIn,
+ ssidPresent && mCurrentState.activityOut, contentDescription, wifiDesc);
+ }
+
+ int signalClustersLength = mSignalClusters.size();
+ for (int i = 0; i < signalClustersLength; i++) {
+ mSignalClusters.get(i).setWifiIndicators(
+ // only show wifi in the cluster if connected or if wifi-only
+ mCurrentState.enabled && (mCurrentState.connected || !mHasMobileData),
+ getCurrentIconId(), contentDescription);
+ }
+ }
+
+ /**
+ * Extract wifi state directly from broadcasts about changes in wifi state.
+ */
+ public void handleBroadcast(Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ mCurrentState.enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
+ } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ final NetworkInfo networkInfo = (NetworkInfo)
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+ mCurrentState.connected = networkInfo != null && networkInfo.isConnected();
+ // If Connected grab the signal strength and ssid.
+ if (mCurrentState.connected) {
+ // try getting it out of the intent first
+ WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null
+ ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
+ : mWifiManager.getConnectionInfo();
+ if (info != null) {
+ mCurrentState.ssid = huntForSsid(info);
+ } else {
+ mCurrentState.ssid = null;
+ }
+ } else if (!mCurrentState.connected) {
+ mCurrentState.ssid = null;
+ }
+ } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+ mCurrentState.rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
+ mCurrentState.level = WifiManager.calculateSignalLevel(
+ mCurrentState.rssi, WifiIcons.WIFI_LEVEL_COUNT);
+ }
+
+ notifyListenersIfNecessary();
+ }
+
+ private String huntForSsid(WifiInfo info) {
+ String ssid = info.getSSID();
+ if (ssid != null) {
+ return ssid;
+ }
+ // OK, it's not in the connectionInfo; we have to go hunting for it
+ List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks();
+ int length = networks.size();
+ for (int i = 0; i < length; i++) {
+ if (networks.get(i).networkId == info.getNetworkId()) {
+ return networks.get(i).SSID;
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ void setActivity(int wifiActivity) {
+ mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
+ || wifiActivity == WifiManager.DATA_ACTIVITY_IN;
+ mCurrentState.activityOut = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
+ || wifiActivity == WifiManager.DATA_ACTIVITY_OUT;
+ notifyListenersIfNecessary();
+ }
+
+ /**
+ * Handler to receive the data activity on wifi.
+ */
+ class WifiHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ mWifiChannel.sendMessage(Message.obtain(this,
+ AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
+ } else {
+ Log.e(mTag, "Failed to connect to wifi");
+ }
+ break;
+ case WifiManager.DATA_ACTIVITY_NOTIFICATION:
+ setActivity(msg.arg1);
+ break;
+ default:
+ // Ignore
+ break;
+ }
+ }
+ }
+
+ static class WifiState extends SignalController.State {
+ String ssid;
+
+ @Override
+ public void copyFrom(State s) {
+ WifiState state = (WifiState) s;
+ ssid = state.ssid;
+ super.copyFrom(s);
+ }
+
+ @Override
+ protected void toString(StringBuilder builder) {
+ builder.append("ssid=").append(ssid).append(',');
+ super.toString(builder);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return super.equals(o)
+ && Objects.equals(((WifiState) o).ssid, ssid);
+ }
+ }
+ }
+
+ static class MobileSignalController extends SignalController<MobileSignalController.MobileState,
+ MobileSignalController.MobileIconGroup> {
+ private final Config mConfig;
+ private final TelephonyManager mPhone;
+ private final String mNetworkNameDefault;
+ private final String mNetworkNameSeparator;
+
+ // @VisibleForDemoMode
+ Map<Integer, MobileIconGroup> mNetworkToIconLookup;
+
+ // Since some pieces of the phone state are interdependent we store it locally,
+ // this could potentially become part of MobileState for simplification/complication
+ // of code.
+ private IccCardConstants.State mSimState = IccCardConstants.State.READY;
+ private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ private int mDataState = TelephonyManager.DATA_DISCONNECTED;
+ private ServiceState mServiceState;
+ private SignalStrength mSignalStrength;
+ private MobileIconGroup mDefaultIcons;
+
+ // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
+ // need listener lists anymore.
+ public MobileSignalController(Context context, Config config, boolean hasMobileData,
+ TelephonyManager phone, List<NetworkSignalChangedCallback> signalCallbacks,
+ List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
+ super("MobileSignalController", context, ConnectivityManager.TYPE_MOBILE,
+ signalCallbacks, signalClusters, networkController);
+ mConfig = config;
+ mPhone = phone;
+ mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator);
+ mNetworkNameDefault = getStringIfExists(
+ com.android.internal.R.string.lockscreen_carrier_default);
+
+ mapIconSets();
+
+ mLastState.networkName = mCurrentState.networkName = mNetworkNameDefault;
+ mLastState.enabled = mCurrentState.enabled = hasMobileData;
+ mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
+ }
+
+ /**
+ * Get (the mobile parts of) the carrier string.
+ *
+ * @param currentLabel can be used for concatenation, currently just empty
+ * @param connected whether the device has connection to the internet at all
+ * @param isMobileLabel whether to always return the network or just when data is connected
+ */
+ public String getLabel(String currentLabel, boolean connected, boolean isMobileLabel) {
+ if (!mCurrentState.enabled) {
+ return "";
+ } else {
+ String mobileLabel = "";
+ // We want to show the carrier name if in service and either:
+ // - We are connected to mobile data, or
+ // - We are not connected to mobile data, as long as the *reason* packets are not
+ // being routed over that link is that we have better connectivity via wifi.
+ // If data is disconnected for some other reason but wifi (or ethernet/bluetooth)
+ // is connected, we show nothing.
+ // Otherwise (nothing connected) we show "No internet connection".
+ if (mCurrentState.dataConnected) {
+ mobileLabel = mCurrentState.networkName;
+ } else if (connected || mCurrentState.isEmergency) {
+ if (mCurrentState.connected || mCurrentState.isEmergency) {
+ // The isEmergencyOnly test covers the case of a phone with no SIM
+ mobileLabel = mCurrentState.networkName;
+ }
+ } else {
+ mobileLabel = mContext
+ .getString(R.string.status_bar_settings_signal_meter_disconnected);
+ }
+
+ // Now for things that should only be shown when actually using mobile data.
+ if (isMobileLabel) {
+ return mobileLabel;
+ } else {
+ return mCurrentState.dataConnected ? mobileLabel : currentLabel;
+ }
+ }
+ }
+
+ public int getDataContentDescription() {
+ return getIcons().mDataContentDescription;
+ }
+
+ public void setAirplaneMode(boolean airplaneMode) {
+ mCurrentState.airplaneMode = airplaneMode;
+ notifyListenersIfNecessary();
+ }
+
+ public void setInetCondition(int inetCondition, int inetConditionForNetwork) {
+ // For mobile data, use general inet condition for phone signal indexing,
+ // and network specific for data indexing (I think this might be a bug, but
+ // keeping for now).
+ // TODO: Update with explanation of why.
+ mCurrentState.inetForNetwork = inetConditionForNetwork;
+ setInetCondition(inetCondition);
+ }
+
+ /**
+ * Start listening for phone state changes.
+ */
+ public void registerListener() {
+ mPhone.listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_SERVICE_STATE
+ | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
+ | PhoneStateListener.LISTEN_CALL_STATE
+ | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+ | PhoneStateListener.LISTEN_DATA_ACTIVITY);
+ }
+
+ /**
+ * Stop listening for phone state changes.
+ */
+ public void unregisterListener() {
+ mPhone.listen(mPhoneStateListener, 0);
+ }
+
+ /**
+ * Produce a mapping of data network types to icon groups for simple and quick use in
+ * updateTelephony.
+ *
+ * TODO: See if config can change with locale, this may need to be regenerated on Locale
+ * change.
+ */
+ private void mapIconSets() {
+ mNetworkToIconLookup = new HashMap<Integer, MobileIconGroup>();
+
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G);
+
+ if (!mConfig.showAtLeastThreeGees) {
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TelephonyIcons.UNKNOWN);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X);
+
+ mDefaultIcons = TelephonyIcons.G;
+ } else {
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE,
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA,
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT,
+ TelephonyIcons.THREE_G);
+ mDefaultIcons = TelephonyIcons.THREE_G;
+ }
+
+ MobileIconGroup hGroup = TelephonyIcons.THREE_G;
+ if (mConfig.hspaDataDistinguishable) {
+ hGroup = TelephonyIcons.H;
+ }
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hGroup);
+
+ if (mConfig.show4gForLte) {
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G);
+ } else {
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void notifyListeners() {
+ MobileIconGroup icons = getIcons();
+
+ String contentDescription = getStringIfExists(getContentDescription());
+ String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
+ int qsTypeIcon = icons.mQsDataType[mCurrentState.inetForNetwork];
+ int length = mSignalsChangedCallbacks.size();
+ for (int i = 0; i < length; i++) {
+ mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled
+ && !mCurrentState.isEmergency && !mCurrentState.airplaneMode,
+ getQsCurrentIconId(), contentDescription,
+ qsTypeIcon,
+ mCurrentState.dataConnected && mCurrentState.activityIn,
+ mCurrentState.dataConnected && mCurrentState.activityOut,
+ dataContentDescription,
+ mCurrentState.isEmergency ? null : mCurrentState.networkName,
+ mCurrentState.noSim,
+ // Only wide if actually showing something.
+ icons.mIsWide && qsTypeIcon != 0);
+ }
+ boolean showDataIcon = mCurrentState.inetForNetwork != 0
+ || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
+ int typeIcon = showDataIcon ? icons.mDataType : 0;
+ int signalClustersLength = mSignalClusters.size();
+ for (int i = 0; i < signalClustersLength; i++) {
+ mSignalClusters.get(i).setMobileDataIndicators(
+ mCurrentState.enabled && !mCurrentState.airplaneMode,
+ getCurrentIconId(),
+ typeIcon,
+ contentDescription,
+ dataContentDescription,
+ // Only wide if actually showing something.
+ icons.mIsWide && typeIcon != 0);
+ }
+ }
+
+ @Override
+ public MobileState cleanState() {
+ return new MobileState();
+ }
+
+ private boolean hasService() {
+ if (mServiceState != null) {
+ // Consider the device to be in service if either voice or data
+ // service is available. Some SIM cards are marketed as data-only
+ // and do not support voice service, and on these SIM cards, we
+ // want to show signal bars for data service as well as the "no
+ // service" or "emergency calls only" text that indicates that voice
+ // is not available.
+ switch (mServiceState.getVoiceRegState()) {
+ case ServiceState.STATE_POWER_OFF:
+ return false;
+ case ServiceState.STATE_OUT_OF_SERVICE:
+ case ServiceState.STATE_EMERGENCY_ONLY:
+ return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE;
+ default:
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private boolean isCdma() {
+ return (mSignalStrength != null) && !mSignalStrength.isGsm();
+ }
+
+ public boolean isEmergencyOnly() {
+ return (mServiceState != null && mServiceState.isEmergencyOnly());
+ }
+
+ private boolean isRoaming() {
+ if (isCdma()) {
+ final int iconMode = mServiceState.getCdmaEriIconMode();
+ return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF
+ && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
+ || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH);
+ } else {
+ return mServiceState != null && mServiceState.getRoaming();
+ }
+ }
+
+ public void handleBroadcast(Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+ String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+ final String lockedReason =
+ intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
+ updateSimState(stateExtra, lockedReason);
+ updateTelephony();
+ } else if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
+ updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
+ intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
+ intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
+ intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
+ notifyListenersIfNecessary();
+ }
+ }
+
+ /**
+ * Determines the current sim state, based on a TelephonyIntents.ACTION_SIM_STATE_CHANGED
+ * broadcast.
+ */
+ private final void updateSimState(String stateExtra, String lockedReason) {
+ if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+ mSimState = IccCardConstants.State.ABSENT;
+ } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+ mSimState = IccCardConstants.State.READY;
+ } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+ if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+ mSimState = IccCardConstants.State.PIN_REQUIRED;
+ } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+ mSimState = IccCardConstants.State.PUK_REQUIRED;
+ } else {
+ mSimState = IccCardConstants.State.NETWORK_LOCKED;
+ }
+ } else {
+ mSimState = IccCardConstants.State.UNKNOWN;
+ }
+ if (DEBUG) Log.d(TAG, "updateSimState: mSimState=" + mSimState);
+ }
+
+ /**
+ * Updates the network's name based on incoming spn and plmn.
+ */
+ void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
+ if (CHATTY) {
+ Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
+ + " showPlmn=" + showPlmn + " plmn=" + plmn);
+ }
+ StringBuilder str = new StringBuilder();
+ if (showPlmn && plmn != null) {
+ str.append(plmn);
+ }
+ if (showSpn && spn != null) {
+ if (str.length() != 0) {
+ str.append(mNetworkNameSeparator);
+ }
+ str.append(spn);
+ }
+ if (str.length() != 0) {
+ mCurrentState.networkName = str.toString();
+ } else {
+ mCurrentState.networkName = mNetworkNameDefault;
+ }
+ }
+
+ /**
+ * Updates the current state based on mServiceState, mSignalStrength, mDataNetType,
+ * mDataState, and mSimState. It should be called any time one of these is updated.
+ * This will call listeners if necessary.
+ */
+ private final void updateTelephony() {
+ if (DEBUG) {
+ Log.d(TAG, "updateTelephonySignalStrength: hasService=" + hasService()
+ + " ss=" + mSignalStrength);
+ }
+ mCurrentState.connected = hasService() && mSignalStrength != null;
+ if (mCurrentState.connected) {
+ if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
+ mCurrentState.level = mSignalStrength.getCdmaLevel();
+ } else {
+ mCurrentState.level = mSignalStrength.getLevel();
+ }
+ }
+ if (mNetworkToIconLookup.containsKey(mDataNetType)) {
+ mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
+ } else {
+ mCurrentState.iconGroup = mDefaultIcons;
+ }
+ mCurrentState.dataConnected = mCurrentState.connected
+ && mDataState == TelephonyManager.DATA_CONNECTED;
+ if (!isCdma()) {
+ if (mSimState == IccCardConstants.State.READY ||
+ mSimState == IccCardConstants.State.UNKNOWN) {
+ mCurrentState.noSim = false;
+ } else {
+ mCurrentState.noSim = true;
+ // No sim, no data.
+ mCurrentState.dataConnected = false;
+ }
+ }
+
+ if (isRoaming()) {
+ mCurrentState.iconGroup = TelephonyIcons.ROAMING;
+ }
+ if (isEmergencyOnly() != mCurrentState.isEmergency) {
+ mCurrentState.isEmergency = isEmergencyOnly();
+ mNetworkController.recalculateEmergency();
+ }
+ notifyListenersIfNecessary();
+ }
+
+ @VisibleForTesting
+ void setActivity(int activity) {
+ mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT
+ || activity == TelephonyManager.DATA_ACTIVITY_IN;
+ mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT
+ || activity == TelephonyManager.DATA_ACTIVITY_OUT;
+ notifyListenersIfNecessary();
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ super.dump(pw);
+ pw.println(" mServiceState=" + mServiceState + ",");
+ pw.println(" mSignalStrength=" + mSignalStrength + ",");
+ pw.println(" mDataState=" + mDataState + ",");
+ pw.println(" mDataNetType=" + mDataNetType + ",");
+ }
+
+ PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ if (DEBUG) {
+ Log.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength +
+ ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
+ }
+ mSignalStrength = signalStrength;
+ updateTelephony();
+ }
+
+ @Override
+ public void onServiceStateChanged(ServiceState state) {
+ if (DEBUG) {
+ Log.d(TAG, "onServiceStateChanged voiceState=" + state.getVoiceRegState()
+ + " dataState=" + state.getDataRegState());
+ }
+ mServiceState = state;
+ updateTelephony();
+ }
+
+ @Override
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ if (DEBUG) {
+ Log.d(TAG, "onDataConnectionStateChanged: state=" + state
+ + " type=" + networkType);
+ }
+ mDataState = state;
+ mDataNetType = networkType;
+ updateTelephony();
+ }
+
+ @Override
+ public void onDataActivity(int direction) {
+ if (DEBUG) {
+ Log.d(TAG, "onDataActivity: direction=" + direction);
+ }
+ setActivity(direction);
+ }
+ };
+
+ static class MobileIconGroup extends SignalController.IconGroup {
+ final int mDataContentDescription; // mContentDescriptionDataType
+ final int mDataType;
+ final boolean mIsWide;
+ final int[] mQsDataType;
+
+ public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
+ int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
+ int discContentDesc, int dataContentDesc, int dataType, boolean isWide,
+ int[] qsDataType) {
+ super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState,
+ qsDiscState, discContentDesc);
+ mDataContentDescription = dataContentDesc;
+ mDataType = dataType;
+ mIsWide = isWide;
+ mQsDataType = qsDataType;
+ }
+ }
+
+ static class MobileState extends SignalController.State {
+ String networkName;
+ boolean noSim;
+ boolean dataConnected;
+ boolean isEmergency;
+ boolean airplaneMode;
+ int inetForNetwork;
+
+ @Override
+ public void copyFrom(State s) {
+ MobileState state = (MobileState) s;
+ noSim = state.noSim;
+ networkName = state.networkName;
+ dataConnected = state.dataConnected;
+ inetForNetwork = state.inetForNetwork;
+ isEmergency = state.isEmergency;
+ airplaneMode = state.airplaneMode;
+ super.copyFrom(s);
+ }
+
+ @Override
+ protected void toString(StringBuilder builder) {
+ builder.append("noSim=").append(noSim).append(',');
+ builder.append("networkName=").append(networkName).append(',');
+ builder.append("dataConnected=").append(dataConnected).append(',');
+ builder.append("inetForNetwork=").append(inetForNetwork).append(',');
+ builder.append("isEmergency=").append(isEmergency).append(',');
+ builder.append("airplaneMode=").append(airplaneMode).append(',');
+ super.toString(builder);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return super.equals(o)
+ && Objects.equals(((MobileState) o).networkName, networkName)
+ && ((MobileState) o).noSim == noSim
+ && ((MobileState) o).dataConnected == dataConnected
+ && ((MobileState) o).isEmergency == isEmergency
+ && ((MobileState) o).airplaneMode == airplaneMode
+ && ((MobileState) o).inetForNetwork == inetForNetwork;
+ }
+ }
+ }
+
+ /**
+ * Common base class for handling signal for both wifi and mobile data.
+ */
+ static abstract class SignalController<T extends SignalController.State,
+ I extends SignalController.IconGroup> {
+ protected final String mTag;
+ protected final T mCurrentState;
+ protected final T mLastState;
+ protected final int mNetworkType;
+ protected final Context mContext;
+ // The owner of the SignalController (i.e. NetworkController will maintain the following
+ // lists and call notifyListeners whenever the list has changed to ensure everyone
+ // is aware of current state.
+ protected final List<NetworkSignalChangedCallback> mSignalsChangedCallbacks;
+ protected final List<SignalCluster> mSignalClusters;
+ protected final NetworkControllerImpl mNetworkController;
+
+ // Save the previous HISTORY_SIZE states for logging.
+ private final State[] mHistory;
+ // Where to copy the next state into.
+ private int mHistoryIndex;
+
+ public SignalController(String tag, Context context, int type,
+ List<NetworkSignalChangedCallback> signalCallbacks,
+ List<SignalCluster> signalClusters, NetworkControllerImpl networkController) {
+ mTag = TAG + "::" + tag;
+ mNetworkController = networkController;
+ mNetworkType = type;
+ mContext = context;
+ mSignalsChangedCallbacks = signalCallbacks;
+ mSignalClusters = signalClusters;
+ mCurrentState = cleanState();
+ mLastState = cleanState();
+ if (RECORD_HISTORY) {
+ mHistory = new State[HISTORY_SIZE];
+ for (int i = 0; i < HISTORY_SIZE; i++) {
+ mHistory[i] = cleanState();
+ }
+ }
+ }
+
+ public T getState() {
+ return mCurrentState;
+ }
+
+ public int getNetworkType() {
+ return mNetworkType;
+ }
+
+ public void setInetCondition(int inetCondition) {
+ mCurrentState.inetCondition = inetCondition;
+ notifyListenersIfNecessary();
+ }
+
+ // @VisibleForDemoMode
+ /**
+ * Used at the end of demo mode to clear out any ugly state that it has created.
+ * Since we haven't had any callbacks, then isDirty will not have been triggered,
+ * so we can just take the last good state directly from there.
+ */
+ void resetLastState() {
+ mCurrentState.copyFrom(mLastState);
+ }
+
+ /**
+ * Determines if the state of this signal controller has changed and
+ * needs to trigger callbacks related to it.
+ */
+ public boolean isDirty() {
+ if (!mLastState.equals(mCurrentState)) {
+ if (DEBUG) {
+ Log.d(mTag, "Change in state from: " + mLastState + "\n"
+ + "\tto: " + mCurrentState);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public void saveLastState() {
+ if (RECORD_HISTORY) {
+ recordLast();
+ }
+ // Updates the current time.
+ mCurrentState.time = System.currentTimeMillis();
+ mLastState.copyFrom(mCurrentState);
+ }
+
+ /**
+ * Gets the signal icon for QS based on current state of connected, enabled, and level.
+ */
+ public int getQsCurrentIconId() {
+ if (mCurrentState.connected) {
+ return getIcons().mQsIcons[mCurrentState.inetCondition][mCurrentState.level];
+ } else if (mCurrentState.enabled) {
+ return getIcons().mQsDiscState;
+ } else {
+ return getIcons().mQsNullState;
+ }
+ }
+
+ /**
+ * Gets the signal icon for SB based on current state of connected, enabled, and level.
+ */
+ public int getCurrentIconId() {
+ if (mCurrentState.connected) {
+ return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level];
+ } else if (mCurrentState.enabled) {
+ return getIcons().mSbDiscState;
+ } else {
+ return getIcons().mSbNullState;
+ }
+ }
+
+ /**
+ * Gets the content description for the signal based on current state of connected and
+ * level.
+ */
+ public int getContentDescription() {
+ if (mCurrentState.connected) {
+ return getIcons().mContentDesc[mCurrentState.level];
+ } else {
+ return getIcons().mDiscContentDesc;
+ }
+ }
+
+ protected void notifyListenersIfNecessary() {
+ if (isDirty()) {
+ saveLastState();
+ notifyListeners();
+ mNetworkController.refreshCarrierLabel();
+ }
+ }
+
+ /**
+ * Returns the resource if resId is not 0, and an empty string otherwise.
+ */
+ protected String getStringIfExists(int resId) {
+ return resId != 0 ? mContext.getString(resId) : "";
+ }
+
+ protected I getIcons() {
+ return (I) mCurrentState.iconGroup;
+ }
+
+ /**
+ * Saves the last state of any changes, so we can log the current
+ * and last value of any state data.
+ */
+ protected void recordLast() {
+ mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)].copyFrom(mLastState);
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println(" - " + mTag + " -----");
+ pw.println(" Current State: " + mCurrentState);
+ if (RECORD_HISTORY) {
+ // Count up the states that actually contain time stamps, and only display those.
+ int size = 0;
+ for (int i = 0; i < HISTORY_SIZE; i++) {
+ if (mHistory[i].time != 0) size++;
+ }
+ // Print out the previous states in ordered number.
+ for (int i = mHistoryIndex + HISTORY_SIZE - 1;
+ i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
+ pw.println(" Previous State(" + (mHistoryIndex + HISTORY_SIZE - i) + ": "
+ + mHistory[i & (HISTORY_SIZE - 1)]);
+ }
+ }
+ }
+
+ /**
+ * Trigger callbacks based on current state. The callbacks should be completely
+ * based on current state, and only need to be called in the scenario where
+ * mCurrentState != mLastState.
+ */
+ public abstract void notifyListeners();
+
+ /**
+ * Generate a blank T.
+ */
+ public abstract T cleanState();
+
+ /*
+ * Holds icons for a given state. Arrays are generally indexed as inet
+ * state (full connectivity or not) first, and second dimension as
+ * signal strength.
+ */
+ static class IconGroup {
+ final int[][] mSbIcons;
+ final int[][] mQsIcons;
+ final int[] mContentDesc;
+ final int mSbNullState;
+ final int mQsNullState;
+ final int mSbDiscState;
+ final int mQsDiscState;
+ final int mDiscContentDesc;
+ // For logging.
+ final String mName;
+
+ public IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
+ int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
+ int discContentDesc) {
+ mName = name;
+ mSbIcons = sbIcons;
+ mQsIcons = qsIcons;
+ mContentDesc = contentDesc;
+ mSbNullState = sbNullState;
+ mQsNullState = qsNullState;
+ mSbDiscState = sbDiscState;
+ mQsDiscState = qsDiscState;
+ mDiscContentDesc = discContentDesc;
+ }
+
+ @Override
+ public String toString() {
+ return "IconGroup(" + mName + ")";
+ }
+ }
+
+ static class State {
+ boolean connected;
+ boolean enabled;
+ boolean activityIn;
+ boolean activityOut;
+ int level;
+ IconGroup iconGroup;
+ int inetCondition;
+ int rssi; // Only for logging.
+
+ // Not used for comparison, just used for logging.
+ long time;
+
+ public void copyFrom(State state) {
+ connected = state.connected;
+ enabled = state.enabled;
+ level = state.level;
+ iconGroup = state.iconGroup;
+ inetCondition = state.inetCondition;
+ activityIn = state.activityIn;
+ activityOut = state.activityOut;
+ rssi = state.rssi;
+ time = state.time;
+ }
+
+ @Override
+ public String toString() {
+ if (time != 0) {
+ StringBuilder builder = new StringBuilder();
+ toString(builder);
+ return builder.toString();
+ } else {
+ return "Empty " + getClass().getSimpleName();
+ }
+ }
+
+ protected void toString(StringBuilder builder) {
+ builder.append("connected=").append(connected).append(',')
+ .append("enabled=").append(enabled).append(',')
+ .append("level=").append(level).append(',')
+ .append("inetCondition=").append(inetCondition).append(',')
+ .append("iconGroup=").append(iconGroup).append(',')
+ .append("activityIn=").append(activityIn).append(',')
+ .append("activityOut=").append(activityOut).append(',')
+ .append("rssi=").append(rssi).append(',')
+ .append("lastModified=").append(DateFormat.format("MM-dd hh:mm:ss", time));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!o.getClass().equals(getClass())) {
+ return false;
+ }
+ State other = (State) o;
+ return other.connected == connected
+ && other.enabled == enabled
+ && other.level == level
+ && other.inetCondition == inetCondition
+ && other.iconGroup == iconGroup
+ && other.activityIn == activityIn
+ && other.activityOut == activityOut
+ && other.rssi == rssi;
+ }
+ }
+ }
+
+ public interface SignalCluster {
+ void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription);
+
+ void setMobileDataIndicators(boolean visible, int strengthIcon, int typeIcon,
+ String contentDescription, String typeContentDescription, boolean isTypeIconWide);
+
+ void setIsAirplaneMode(boolean is, int airplaneIcon, int contentDescription);
+ }
+
+ public interface EmergencyListener {
+ void setEmergencyCallsOnly(boolean emergencyOnly);
+ }
+
+ public interface CarrierLabelListener {
+ void setCarrierLabel(String label);
+ }
+
+ @VisibleForTesting
+ static class Config {
+ boolean showAtLeastThreeGees = false;
+ boolean alwaysShowCdmaRssi = false;
+ boolean show4gForLte = false;
+ boolean hspaDataDistinguishable;
+
+ static Config readConfig(Context context) {
+ Config config = new Config();
+ Resources res = context.getResources();
+
+ config.showAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G);
+ config.alwaysShowCdmaRssi =
+ res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
+ config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
+ config.hspaDataDistinguishable =
+ res.getBoolean(R.bool.config_hspa_data_distinguishable);
+ return config;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index 1f2b918..4091619 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -17,11 +17,16 @@
package com.android.systemui.statusbar.policy;
import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl.MobileSignalController.MobileIconGroup;
class TelephonyIcons {
//***** Signal strength icons
+ static final int TELEPHONY_NUM_LEVELS = 5;
+
//GSM/UMTS
+ static final int TELEPHONY_NO_NETWORK = R.drawable.stat_sys_signal_null;
+
static final int[][] TELEPHONY_SIGNAL_STRENGTH = {
{ R.drawable.stat_sys_signal_0,
R.drawable.stat_sys_signal_1,
@@ -35,6 +40,8 @@
R.drawable.stat_sys_signal_4_fully }
};
+ static final int QS_TELEPHONY_NO_NETWORK = R.drawable.ic_qs_signal_no_signal;
+
static final int[][] QS_TELEPHONY_SIGNAL_STRENGTH = {
{ R.drawable.ic_qs_signal_0,
R.drawable.ic_qs_signal_1,
@@ -66,8 +73,6 @@
R.drawable.ic_qs_signal_r
};
- static final int[][] DATA_SIGNAL_STRENGTH = TELEPHONY_SIGNAL_STRENGTH;
-
//***** Data connection icons
//GSM/UMTS
@@ -191,6 +196,9 @@
static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_airplane_mode;
static final int ROAMING_ICON = R.drawable.stat_sys_data_fully_connected_roam;
static final int ICON_LTE = R.drawable.stat_sys_data_fully_connected_lte;
+ static final int ICON_G = R.drawable.stat_sys_data_fully_connected_g;
+ static final int ICON_E = R.drawable.stat_sys_data_fully_connected_e;
+ static final int ICON_H = R.drawable.stat_sys_data_fully_connected_h;
static final int ICON_3G = R.drawable.stat_sys_data_fully_connected_3g;
static final int ICON_4G = R.drawable.stat_sys_data_fully_connected_4g;
static final int ICON_1X = R.drawable.stat_sys_data_fully_connected_1x;
@@ -199,5 +207,137 @@
static final int QS_ICON_3G = R.drawable.ic_qs_signal_3g;
static final int QS_ICON_4G = R.drawable.ic_qs_signal_4g;
static final int QS_ICON_1X = R.drawable.ic_qs_signal_1x;
+
+ static final MobileIconGroup THREE_G = new MobileIconGroup(
+ "3G",
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
+ TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0, 0,
+ TelephonyIcons.TELEPHONY_NO_NETWORK,
+ TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.accessibility_data_connection_3g,
+ TelephonyIcons.ICON_3G,
+ true,
+ TelephonyIcons.QS_DATA_3G
+ );
+
+ static final MobileIconGroup UNKNOWN = new MobileIconGroup(
+ "Unknown",
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
+ TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0, 0,
+ TelephonyIcons.TELEPHONY_NO_NETWORK,
+ TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ 0, 0, false, new int[2]
+ );
+
+ static final MobileIconGroup E = new MobileIconGroup(
+ "E",
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
+ TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0, 0,
+ TelephonyIcons.TELEPHONY_NO_NETWORK,
+ TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.accessibility_data_connection_edge,
+ TelephonyIcons.ICON_E,
+ false,
+ TelephonyIcons.QS_DATA_E
+ );
+
+ static final MobileIconGroup ONE_X = new MobileIconGroup(
+ "1X",
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
+ TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0, 0,
+ TelephonyIcons.TELEPHONY_NO_NETWORK,
+ TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.accessibility_data_connection_cdma,
+ TelephonyIcons.ICON_1X,
+ true,
+ TelephonyIcons.QS_DATA_1X
+ );
+
+ static final MobileIconGroup G = new MobileIconGroup(
+ "G",
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
+ TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0, 0,
+ TelephonyIcons.TELEPHONY_NO_NETWORK,
+ TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.accessibility_data_connection_gprs,
+ TelephonyIcons.ICON_G,
+ false,
+ TelephonyIcons.QS_DATA_G
+ );
+
+ static final MobileIconGroup H = new MobileIconGroup(
+ "H",
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
+ TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0, 0,
+ TelephonyIcons.TELEPHONY_NO_NETWORK,
+ TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.accessibility_data_connection_3_5g,
+ TelephonyIcons.ICON_H,
+ false,
+ TelephonyIcons.QS_DATA_H
+ );
+
+ static final MobileIconGroup FOUR_G = new MobileIconGroup(
+ "4G",
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
+ TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0, 0,
+ TelephonyIcons.TELEPHONY_NO_NETWORK,
+ TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.accessibility_data_connection_4g,
+ TelephonyIcons.ICON_4G,
+ true,
+ TelephonyIcons.QS_DATA_4G
+ );
+
+ static final MobileIconGroup LTE = new MobileIconGroup(
+ "LTE",
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
+ TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0, 0,
+ TelephonyIcons.TELEPHONY_NO_NETWORK,
+ TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.accessibility_data_connection_lte,
+ TelephonyIcons.ICON_LTE,
+ true,
+ TelephonyIcons.QS_DATA_LTE
+ );
+
+ static final MobileIconGroup ROAMING = new MobileIconGroup(
+ "Roaming",
+ TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING,
+ TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0, 0,
+ TelephonyIcons.TELEPHONY_NO_NETWORK,
+ TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.accessibility_data_connection_roaming,
+ TelephonyIcons.ROAMING_ICON,
+ false,
+ TelephonyIcons.QS_DATA_R
+ );
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
index 49af979..c56646f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
@@ -45,5 +45,8 @@
R.drawable.ic_qs_wifi_full_4 }
};
+ static final int QS_WIFI_NO_NETWORK = R.drawable.ic_qs_wifi_no_network;
+ static final int WIFI_NO_NETWORK = R.drawable.stat_sys_wifi_signal_null;
+
static final int WIFI_LEVEL_COUNT = WIFI_SIGNAL_STRENGTH[0].length;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java
deleted file mode 100644
index 4877828..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WimaxIcons.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2011 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.systemui.statusbar.policy;
-
-import com.android.systemui.statusbar.policy.TelephonyIcons;
-
-class WimaxIcons {
- static final int[][] WIMAX_SIGNAL_STRENGTH = TelephonyIcons.DATA_SIGNAL_STRENGTH;
-
- static final int WIMAX_DISCONNECTED = WIMAX_SIGNAL_STRENGTH[0][0];
-
- static final int WIMAX_IDLE = WIMAX_DISCONNECTED; // XXX: unclear if we need a different icon
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 10cffc4..49fe1e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -17,6 +17,7 @@
import com.android.internal.telephony.cdma.EriInfo;
import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster;
import org.mockito.ArgumentCaptor;
@@ -44,6 +45,7 @@
protected ConnectivityManager mMockCm;
protected WifiManager mMockWm;
protected TelephonyManager mMockTm;
+ protected Config mConfig;
@Override
protected void setUp() throws Exception {
@@ -59,16 +61,19 @@
mSignalStrength = mock(SignalStrength.class);
mServiceState = mock(ServiceState.class);
- mSignalCluster = mock(SignalCluster.class);
- mNetworkSignalChangedCallback = mock(NetworkSignalChangedCallback.class);
+ mConfig = new Config();
+ mConfig.hspaDataDistinguishable = true;
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm,
- mock(AccessPointController.class), mock(MobileDataController.class));
+ mConfig, mock(AccessPointControllerImpl.class),
+ mock(MobileDataControllerImpl.class));
setupNetworkController();
}
protected void setupNetworkController() {
- mPhoneStateListener = mNetworkController.mPhoneStateListener;
+ mPhoneStateListener = mNetworkController.mMobileSignalController.mPhoneStateListener;
+ mSignalCluster = mock(SignalCluster.class);
+ mNetworkSignalChangedCallback = mock(NetworkSignalChangedCallback.class);
mNetworkController.addSignalCluster(mSignalCluster);
mNetworkController.addNetworkSignalChangedCallback(mNetworkSignalChangedCallback);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index af05309..bb2ff7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -18,7 +18,8 @@
Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm,
- mock(AccessPointController.class), mock(MobileDataController.class));
+ mConfig, mock(AccessPointControllerImpl.class),
+ mock(MobileDataControllerImpl.class));
setupNetworkController();
verifyLastMobileDataIndicators(false, 0, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 4ffdff2..7f0a8f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -6,8 +6,6 @@
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import com.android.systemui.R;
-
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@@ -16,14 +14,10 @@
private static final int MIN_RSSI = -100;
private static final int MAX_RSSI = -55;
- // TODO: Move this into WifiIcons, remove all R.drawable from NetworkControllerImpl.
- private static final int NULL_SIGNAL = R.drawable.stat_sys_wifi_signal_null;
- private static final int QS_NO_NET = R.drawable.ic_qs_wifi_no_network;
-
public void testWifiIcon() {
String testSsid = "Test SSID";
setWifiEnabled(true);
- verifyLastWifiIcon(false, NULL_SIGNAL);
+ verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
setWifiState(true, testSsid);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
@@ -42,10 +36,10 @@
String testSsid = "Test SSID";
setWifiEnabled(false);
- verifyLastQsWifiIcon(false, false, 0, null);
+ verifyLastQsWifiIcon(false, false, WifiIcons.QS_WIFI_NO_NETWORK, null);
setWifiEnabled(true);
- verifyLastQsWifiIcon(true, false, QS_NO_NET, null);
+ verifyLastQsWifiIcon(true, false, WifiIcons.QS_WIFI_NO_NETWORK, null);
setWifiState(true, testSsid);
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
@@ -118,8 +112,7 @@
protected void setWifiActivity(int activity) {
// TODO: Not this, because this variable probably isn't sticking around.
- mNetworkController.mWifiActivity = activity;
- mNetworkController.refreshViews();
+ mNetworkController.mWifiSignalController.setActivity(activity);
}
protected void setWifiLevel(int level) {
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index de90aa2..17edb53 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -41,6 +41,9 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
/**
* Service for reading and writing blocks to a persistent partition.
@@ -63,22 +66,16 @@
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
private static final int HEADER_SIZE = 8;
// Magic number to mark block device as adhering to the format consumed by this service
- private static final int PARTITION_TYPE_MARKER = 0x1990;
+ private static final int PARTITION_TYPE_MARKER = 0x19901873;
// Limit to 100k as blocks larger than this might cause strain on Binder.
- // TODO(anmorales): Consider splitting up too-large blocks in PersistentDataBlockManager
private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
+ public static final int DIGEST_SIZE_BYTES = 32;
private final Context mContext;
private final String mDataBlockFile;
private final Object mLock = new Object();
private int mAllowedUid = -1;
- /*
- * Separate lock for OEM unlock related operations as they can happen in parallel with regular
- * block operations.
- */
- private final Object mOemLock = new Object();
-
private long mBlockDeviceSize;
public PersistentDataBlockService(Context context) {
@@ -89,7 +86,6 @@
mAllowedUid = getAllowedUid(UserHandle.USER_OWNER);
}
-
private int getAllowedUid(int userHandle) {
String allowedPackage = mContext.getResources()
.getString(R.string.config_persistentDataPackageName);
@@ -106,6 +102,7 @@
@Override
public void onStart() {
+ enforceChecksumValidity();
publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
}
@@ -128,6 +125,9 @@
}
private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
+ // skip over checksum
+ inputStream.skipBytes(DIGEST_SIZE_BYTES);
+
int totalDataSize;
int blockId = inputStream.readInt();
if (blockId == PARTITION_TYPE_MARKER) {
@@ -148,6 +148,143 @@
return mBlockDeviceSize;
}
+ private boolean enforceChecksumValidity() {
+ byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
+
+ synchronized (mLock) {
+ byte[] digest = computeDigestLocked(storedDigest);
+ if (digest == null || !Arrays.equals(storedDigest, digest)) {
+ Slog.i(TAG, "Formatting FRP partition...");
+ formatPartitionLocked();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean computeAndWriteDigestLocked() {
+ byte[] digest = computeDigestLocked(null);
+ if (digest != null) {
+ DataOutputStream outputStream;
+ try {
+ outputStream = new DataOutputStream(
+ new FileOutputStream(new File(mDataBlockFile)));
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "partition not available?", e);
+ return false;
+ }
+
+ try {
+ outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
+ outputStream.flush();
+ } catch (IOException e) {
+ Slog.e(TAG, "failed to write block checksum", e);
+ return false;
+ } finally {
+ IoUtils.closeQuietly(outputStream);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private byte[] computeDigestLocked(byte[] storedDigest) {
+ DataInputStream inputStream;
+ try {
+ inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "partition not available?", e);
+ return null;
+ }
+
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ // won't ever happen -- every implementation is required to support SHA-256
+ Slog.e(TAG, "SHA-256 not supported?", e);
+ IoUtils.closeQuietly(inputStream);
+ return null;
+ }
+
+ try {
+ if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
+ inputStream.read(storedDigest);
+ } else {
+ inputStream.skipBytes(DIGEST_SIZE_BYTES);
+ }
+
+ int read;
+ byte[] data = new byte[1024];
+ md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
+ while ((read = inputStream.read(data)) != -1) {
+ md.update(data, 0, read);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "failed to read partition", e);
+ return null;
+ } finally {
+ IoUtils.closeQuietly(inputStream);
+ }
+
+ return md.digest();
+ }
+
+ private void formatPartitionLocked() {
+ DataOutputStream outputStream;
+ try {
+ outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "partition not available?", e);
+ return;
+ }
+
+ byte[] data = new byte[DIGEST_SIZE_BYTES];
+ try {
+ outputStream.write(data, 0, DIGEST_SIZE_BYTES);
+ outputStream.writeInt(PARTITION_TYPE_MARKER);
+ outputStream.writeInt(0); // data size
+ outputStream.flush();
+ } catch (IOException e) {
+ Slog.e(TAG, "failed to format block", e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(outputStream);
+ }
+
+ doSetOemUnlockEnabledLocked(false);
+ computeAndWriteDigestLocked();
+ }
+
+ private void doSetOemUnlockEnabledLocked(boolean enabled) {
+ FileOutputStream outputStream;
+ try {
+ outputStream = new FileOutputStream(new File(mDataBlockFile));
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "partition not available", e);
+ return;
+ }
+
+ try {
+ FileChannel channel = outputStream.getChannel();
+
+ channel.position(getBlockDeviceSize() - 1);
+
+ ByteBuffer data = ByteBuffer.allocate(1);
+ data.put(enabled ? (byte) 1 : (byte) 0);
+ data.flip();
+ channel.write(data);
+ outputStream.flush();
+ } catch (IOException e) {
+ Slog.e(TAG, "unable to access persistent partition", e);
+ return;
+ } finally {
+ IoUtils.closeQuietly(outputStream);
+ }
+ }
+
private native long nativeGetBlockDeviceSize(String path);
private native int nativeWipe(String path);
@@ -176,19 +313,23 @@
headerAndData.putInt(data.length);
headerAndData.put(data);
- try {
- synchronized (mLock) {
- outputStream.write(headerAndData.array());
- return data.length;
- }
- } catch (IOException e) {
- Slog.e(TAG, "failed writing to the persistent data block", e);
- return -1;
- } finally {
+ synchronized (mLock) {
try {
- outputStream.close();
+ byte[] checksum = new byte[DIGEST_SIZE_BYTES];
+ outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
+ outputStream.write(headerAndData.array());
+ outputStream.flush();
} catch (IOException e) {
- Slog.e(TAG, "failed closing output stream", e);
+ Slog.e(TAG, "failed writing to the persistent data block", e);
+ return -1;
+ } finally {
+ IoUtils.closeQuietly(outputStream);
+ }
+
+ if (computeAndWriteDigestLocked()) {
+ return data.length;
+ } else {
+ return -1;
}
}
}
@@ -196,6 +337,9 @@
@Override
public byte[] read() {
enforceUid(Binder.getCallingUid());
+ if (!enforceChecksumValidity()) {
+ return new byte[0];
+ }
DataInputStream inputStream;
try {
@@ -256,30 +400,10 @@
}
enforceOemUnlockPermission();
enforceIsOwner();
- FileOutputStream outputStream;
- try {
- outputStream = new FileOutputStream(new File(mDataBlockFile));
- } catch (FileNotFoundException e) {
- Slog.e(TAG, "parition not available", e);
- return;
- }
- try {
- FileChannel channel = outputStream.getChannel();
-
- channel.position(getBlockDeviceSize() - 1);
-
- ByteBuffer data = ByteBuffer.allocate(1);
- data.put(enabled ? (byte) 1 : (byte) 0);
- data.flip();
-
- synchronized (mOemLock) {
- channel.write(data);
- }
- } catch (IOException e) {
- Slog.e(TAG, "unable to access persistent partition", e);
- } finally {
- IoUtils.closeQuietly(outputStream);
+ synchronized (mLock) {
+ doSetOemUnlockEnabledLocked(enabled);
+ computeAndWriteDigestLocked();
}
}
@@ -295,8 +419,8 @@
}
try {
- inputStream.skip(getBlockDeviceSize() - 1);
- synchronized (mOemLock) {
+ synchronized (mLock) {
+ inputStream.skip(getBlockDeviceSize() - 1);
return inputStream.readByte() != 0;
}
} catch (IOException e) {
@@ -336,6 +460,5 @@
long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
}
-
};
}
diff --git a/telecomm/java/android/telecom/PhoneCapabilities.java b/telecomm/java/android/telecom/PhoneCapabilities.java
index feb7a95..c21a6d1 100644
--- a/telecomm/java/android/telecom/PhoneCapabilities.java
+++ b/telecomm/java/android/telecom/PhoneCapabilities.java
@@ -101,6 +101,16 @@
*/
public static final int DISCONNECT_FROM_CONFERENCE = 0x00002000;
+ /**
+ * Whether the call is a generic conference, where we do not know the precise state of
+ * participants in the conference (eg. on CDMA).
+ *
+ * TODO: Move to CallProperties.
+ *
+ * @hide
+ */
+ public static final int GENERIC_CONFERENCE = 0x00004000;
+
public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CONFERENCE | SWAP_CONFERENCE
| RESPOND_VIA_TEXT | MUTE | MANAGE_CONFERENCE | SEPARATE_FROM_CONFERENCE
| DISCONNECT_FROM_CONFERENCE;
@@ -163,6 +173,10 @@
if (can(capabilities, VoWIFI)) {
builder.append(" VoWIFI");
}
+ if (can(capabilities, GENERIC_CONFERENCE)) {
+ builder.append(" GENERIC_CONFERENCE");
+ }
+
builder.append("]");
return builder.toString();
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 1393bce..6ddebde 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1968,7 +1968,7 @@
/**
* Start Wi-fi Protected Setup
*
- * @param config WPS configuration
+ * @param config WPS configuration (does not support {@link WpsInfo#LABEL})
* @param listener for callbacks on success or failure. Can be null.
* @throws IllegalStateException if the WifiManager instance needs to be
* initialized again