Merge "Fix doc for package checksum in DevicePolicyManager."
diff --git a/api/current.txt b/api/current.txt
index c004b15..904d9a99 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -464,8 +464,8 @@
field public static final int datePickerMode = 16843955; // 0x10104b3
field public static final int datePickerStyle = 16843612; // 0x101035c
field public static final int dateTextAppearance = 16843593; // 0x1010349
- field public static final int dayOfWeekBackground = 16843924; // 0x1010494
- field public static final int dayOfWeekTextAppearance = 16843925; // 0x1010495
+ field public static final deprecated int dayOfWeekBackground = 16843924; // 0x1010494
+ field public static final deprecated int dayOfWeekTextAppearance = 16843925; // 0x1010495
field public static final int debuggable = 16842767; // 0x101000f
field public static final int defaultValue = 16843245; // 0x10101ed
field public static final int delay = 16843212; // 0x10101cc
@@ -555,6 +555,7 @@
field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
field public static final int exported = 16842768; // 0x1010010
field public static final int extraTension = 16843371; // 0x101026b
+ field public static final int extractNativeLibs = 16844008; // 0x10104e8
field public static final int factor = 16843219; // 0x10101d3
field public static final int fadeDuration = 16843384; // 0x1010278
field public static final int fadeEnabled = 16843390; // 0x101027e
@@ -587,7 +588,7 @@
field public static final int flipInterval = 16843129; // 0x1010179
field public static final int focusable = 16842970; // 0x10100da
field public static final int focusableInTouchMode = 16842971; // 0x10100db
- field public static final int focusedMonthDateColor = 16843587; // 0x1010343
+ field public static final deprecated int focusedMonthDateColor = 16843587; // 0x1010343
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
@@ -651,9 +652,9 @@
field public static final int hasCode = 16842764; // 0x101000c
field public static final int headerAmPmTextAppearance = 16843936; // 0x10104a0
field public static final int headerBackground = 16843055; // 0x101012f
- field public static final int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
+ field public static final deprecated int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
field public static final int headerDividersEnabled = 16843310; // 0x101022e
- field public static final int headerMonthTextAppearance = 16843926; // 0x1010496
+ field public static final deprecated int headerMonthTextAppearance = 16843926; // 0x1010496
field public static final int headerTimeTextAppearance = 16843935; // 0x101049f
field public static final int headerYearTextAppearance = 16843928; // 0x1010498
field public static final int height = 16843093; // 0x1010155
@@ -1094,8 +1095,8 @@
field public static final int selectable = 16843238; // 0x10101e6
field public static final int selectableItemBackground = 16843534; // 0x101030e
field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c
- field public static final int selectedDateVerticalBar = 16843591; // 0x1010347
- field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
+ field public static final deprecated int selectedDateVerticalBar = 16843591; // 0x1010347
+ field public static final deprecated int selectedWeekBackgroundColor = 16843586; // 0x1010342
field public static final int sessionService = 16843837; // 0x101043d
field public static final int settingsActivity = 16843301; // 0x1010225
field public static final int setupActivity = 16843766; // 0x10103f6
@@ -1114,8 +1115,8 @@
field public static final int showOnLockScreen = 16843721; // 0x10103c9
field public static final int showSilent = 16843259; // 0x10101fb
field public static final int showText = 16843949; // 0x10104ad
- field public static final int showWeekNumber = 16843582; // 0x101033e
- field public static final int shownWeekCount = 16843585; // 0x1010341
+ field public static final deprecated int showWeekNumber = 16843582; // 0x101033e
+ field public static final deprecated int shownWeekCount = 16843585; // 0x1010341
field public static final int shrinkColumns = 16843082; // 0x101014a
field public static final deprecated int singleLine = 16843101; // 0x101015d
field public static final int singleUser = 16843711; // 0x10103bf
@@ -1352,7 +1353,7 @@
field public static final int typeface = 16842902; // 0x1010096
field public static final int uiOptions = 16843672; // 0x1010398
field public static final int uncertainGestureColor = 16843382; // 0x1010276
- field public static final int unfocusedMonthDateColor = 16843588; // 0x1010344
+ field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
field public static final int useDefaultMargins = 16843641; // 0x1010379
@@ -1394,8 +1395,8 @@
field public static final int webTextViewStyle = 16843449; // 0x10102b9
field public static final int webViewStyle = 16842885; // 0x1010085
field public static final int weekDayTextAppearance = 16843592; // 0x1010348
- field public static final int weekNumberColor = 16843589; // 0x1010345
- field public static final int weekSeparatorLineColor = 16843590; // 0x1010346
+ field public static final deprecated int weekNumberColor = 16843589; // 0x1010345
+ field public static final deprecated int weekSeparatorLineColor = 16843590; // 0x1010346
field public static final int weightSum = 16843048; // 0x1010128
field public static final int widgetCategory = 16843716; // 0x10103c4
field public static final int widgetLayout = 16843243; // 0x10101eb
@@ -1454,7 +1455,7 @@
field public static final int xlargeScreens = 16843455; // 0x10102bf
field public static final int y = 16842925; // 0x10100ad
field public static final int yearListItemTextAppearance = 16843929; // 0x1010499
- field public static final int yearListSelectorColor = 16843930; // 0x101049a
+ field public static final deprecated int yearListSelectorColor = 16843930; // 0x101049a
field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
field public static final int zAdjustment = 16843201; // 0x10101c1
}
@@ -3431,6 +3432,7 @@
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
method public void onProvideAssistContent(android.app.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
+ method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method protected void onRestart();
method protected void onRestoreInstanceState(android.os.Bundle);
method public void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
@@ -3461,6 +3463,7 @@
method public boolean releaseInstance();
method public final deprecated void removeDialog(int);
method public void reportFullyDrawn();
+ method public final void requestPermissions(java.lang.String[], int);
method public boolean requestVisibleBehind(boolean);
method public final boolean requestWindowFeature(int);
method public final void runOnUiThread(java.lang.Runnable);
@@ -3977,22 +3980,25 @@
method public int describeContents();
method public android.content.ComponentName getActivityComponent();
method public static android.app.AssistStructure getAssistStructure(android.os.Bundle);
- method public void getWindowAt(int, android.app.AssistStructure.ViewNode);
- method public int getWindowCount();
+ method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
+ method public int getWindowNodeCount();
method public void writeToParcel(android.os.Parcel, int);
field public static final java.lang.String ASSIST_KEY = "android:assist_structure";
field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
}
public static class AssistStructure.ViewNode {
- ctor public AssistStructure.ViewNode();
- method public void getChildAt(int, android.app.AssistStructure.ViewNode);
+ method public android.app.AssistStructure.ViewNode getChildAt(int);
method public int getChildCount();
method public java.lang.String getClassName();
- method public java.lang.String getContentDescription();
+ method public java.lang.CharSequence getContentDescription();
method public android.os.Bundle getExtras();
method public int getHeight();
method public java.lang.String getHint();
+ method public int getId();
+ method public java.lang.String getIdEntry();
+ method public java.lang.String getIdPackage();
+ method public java.lang.String getIdType();
method public int getLeft();
method public int getScrollX();
method public int getScrollY();
@@ -4023,6 +4029,15 @@
field public static final int TEXT_STYLE_UNDERLINE = 4; // 0x4
}
+ public static class AssistStructure.WindowNode {
+ method public int getHeight();
+ method public int getLeft();
+ method public android.app.AssistStructure.ViewNode getRootViewNode();
+ method public java.lang.CharSequence getTitle();
+ method public int getTop();
+ method public int getWidth();
+ }
+
public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
@@ -4303,6 +4318,7 @@
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPause();
method public void onPrepareOptionsMenu(android.view.Menu);
+ method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method public void onResume();
method public void onSaveInstanceState(android.os.Bundle);
method public void onStart();
@@ -4311,6 +4327,7 @@
method public void onViewCreated(android.view.View, android.os.Bundle);
method public void onViewStateRestored(android.os.Bundle);
method public void registerForContextMenu(android.view.View);
+ method public final void requestPermissions(java.lang.String[], int);
method public void setAllowEnterTransitionOverlap(boolean);
method public void setAllowReturnTransitionOverlap(boolean);
method public void setArguments(android.os.Bundle);
@@ -5663,12 +5680,18 @@
field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
field public static final java.lang.String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
field public static final java.lang.String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
+ field public static final java.lang.String EXTRA_PROVISIONING_BT_DEVICE_ID = "android.app.extra.PROVISIONING_BT_DEVICE_ID";
+ field public static final java.lang.String EXTRA_PROVISIONING_BT_MAC_ADDRESS = "android.app.extra.PROVISIONING_BT_MAC_ADDRESS";
+ field public static final java.lang.String EXTRA_PROVISIONING_BT_USE_PROXY = "android.app.extra.PROVISIONING_BT_USE_PROXY";
+ field public static final java.lang.String EXTRA_PROVISIONING_BT_UUID = "android.app.extra.PROVISIONING_BT_UUID";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
field public static final deprecated java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
@@ -7395,6 +7418,7 @@
method public abstract int checkCallingPermission(java.lang.String);
method public abstract int checkCallingUriPermission(android.net.Uri, int);
method public abstract int checkPermission(java.lang.String, int, int);
+ method public abstract int checkSelfPermission(java.lang.String);
method public abstract int checkUriPermission(android.net.Uri, int, int, int);
method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public abstract deprecated void clearWallpaper() throws java.io.IOException;
@@ -7572,6 +7596,7 @@
method public int checkCallingPermission(java.lang.String);
method public int checkCallingUriPermission(android.net.Uri, int);
method public int checkPermission(java.lang.String, int, int);
+ method public int checkSelfPermission(java.lang.String);
method public int checkUriPermission(android.net.Uri, int, int, int);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public deprecated void clearWallpaper() throws java.io.IOException;
@@ -8645,6 +8670,7 @@
field public static final int FLAG_ALLOW_TASK_REPARENTING = 32; // 0x20
field public static final int FLAG_DEBUGGABLE = 2; // 0x2
field public static final int FLAG_EXTERNAL_STORAGE = 262144; // 0x40000
+ field public static final int FLAG_EXTRACT_NATIVE_LIBS = 268435456; // 0x10000000
field public static final int FLAG_FACTORY_TEST = 16; // 0x10
field public static final int FLAG_FULL_BACKUP_ONLY = 67108864; // 0x4000000
field public static final int FLAG_HAS_CODE = 4; // 0x4
@@ -8824,7 +8850,6 @@
field public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1; // 0x1
field public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; // 0x2
field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
- field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
field public android.content.pm.ActivityInfo[] activities;
field public android.content.pm.ApplicationInfo applicationInfo;
field public int baseRevisionCode;
@@ -11871,9 +11896,12 @@
public class AnimatedVectorDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable {
ctor public AnimatedVectorDrawable();
+ method public void addListener(android.animation.Animator.AnimatorListener);
method public void draw(android.graphics.Canvas);
+ method public java.util.List<android.animation.Animator.AnimatorListener> getListeners();
method public int getOpacity();
method public boolean isRunning();
+ method public void removeListener(android.animation.Animator.AnimatorListener);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void start();
@@ -15223,6 +15251,9 @@
field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
field public static final java.lang.String PROPERTY_VENDOR = "vendor";
field public static final java.lang.String PROPERTY_VERSION = "version";
+ field public static final int REQUEST_TYPE_INITIAL = 0; // 0x0
+ field public static final int REQUEST_TYPE_RELEASE = 2; // 0x2
+ field public static final int REQUEST_TYPE_RENEWAL = 1; // 0x1
}
public final class MediaDrm.CryptoSession {
@@ -15235,6 +15266,7 @@
public static final class MediaDrm.KeyRequest {
method public byte[] getData();
method public java.lang.String getDefaultUrl();
+ method public int getRequestType();
}
public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException {
@@ -17266,6 +17298,10 @@
field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
@@ -22851,6 +22887,7 @@
}
public final class PowerManager {
+ method public boolean isDeviceIdleMode();
method public boolean isInteractive();
method public boolean isPowerSaveMode();
method public deprecated boolean isScreenOn();
@@ -22858,6 +22895,7 @@
method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
method public void reboot(java.lang.String);
field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
+ field public static final java.lang.String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
field public static final deprecated int FULL_WAKE_LOCK = 26; // 0x1a
field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
@@ -30174,6 +30212,7 @@
method public int checkCallingPermission(java.lang.String);
method public int checkCallingUriPermission(android.net.Uri, int);
method public int checkPermission(java.lang.String, int, int);
+ method public int checkSelfPermission(java.lang.String);
method public int checkUriPermission(android.net.Uri, int, int, int);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
@@ -34408,6 +34447,10 @@
method public boolean getFitsSystemWindows();
method public java.util.ArrayList<android.view.View> getFocusables(int);
method public void getFocusedRect(android.graphics.Rect);
+ method public android.graphics.drawable.Drawable getForeground();
+ method public int getForegroundGravity();
+ method public android.content.res.ColorStateList getForegroundTintList();
+ method public android.graphics.PorterDuff.Mode getForegroundTintMode();
method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point);
method public final boolean getGlobalVisibleRect(android.graphics.Rect);
method public android.os.Handler getHandler();
@@ -34579,6 +34622,7 @@
method protected void onDisplayHint(int);
method public boolean onDragEvent(android.view.DragEvent);
method protected void onDraw(android.graphics.Canvas);
+ method public void onDrawForeground(android.graphics.Canvas);
method protected final void onDrawScrollBars(android.graphics.Canvas);
method public boolean onFilterTouchEventForSecurity(android.view.MotionEvent);
method protected void onFinishInflate();
@@ -34684,6 +34728,10 @@
method public void setFitsSystemWindows(boolean);
method public void setFocusable(boolean);
method public void setFocusableInTouchMode(boolean);
+ method public void setForeground(android.graphics.drawable.Drawable);
+ method public void setForegroundGravity(int);
+ method public void setForegroundTintList(android.content.res.ColorStateList);
+ method public void setForegroundTintMode(android.graphics.PorterDuff.Mode);
method public void setHapticFeedbackEnabled(boolean);
method public void setHasTransientState(boolean);
method public void setHorizontalFadingEdgeEnabled(boolean);
@@ -38152,34 +38200,34 @@
method public long getDate();
method public int getDateTextAppearance();
method public int getFirstDayOfWeek();
- method public int getFocusedMonthDateColor();
+ method public deprecated int getFocusedMonthDateColor();
method public long getMaxDate();
method public long getMinDate();
- method public android.graphics.drawable.Drawable getSelectedDateVerticalBar();
- method public int getSelectedWeekBackgroundColor();
+ method public deprecated android.graphics.drawable.Drawable getSelectedDateVerticalBar();
+ method public deprecated int getSelectedWeekBackgroundColor();
method public boolean getShowWeekNumber();
- method public int getShownWeekCount();
- method public int getUnfocusedMonthDateColor();
+ method public deprecated int getShownWeekCount();
+ method public deprecated int getUnfocusedMonthDateColor();
method public int getWeekDayTextAppearance();
- method public int getWeekNumberColor();
- method public int getWeekSeparatorLineColor();
+ method public deprecated int getWeekNumberColor();
+ method public deprecated int getWeekSeparatorLineColor();
method public void setDate(long);
method public void setDate(long, boolean, boolean);
method public void setDateTextAppearance(int);
method public void setFirstDayOfWeek(int);
- method public void setFocusedMonthDateColor(int);
+ method public deprecated void setFocusedMonthDateColor(int);
method public void setMaxDate(long);
method public void setMinDate(long);
method public void setOnDateChangeListener(android.widget.CalendarView.OnDateChangeListener);
- method public void setSelectedDateVerticalBar(int);
- method public void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
- method public void setSelectedWeekBackgroundColor(int);
+ method public deprecated void setSelectedDateVerticalBar(int);
+ method public deprecated void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
+ method public deprecated void setSelectedWeekBackgroundColor(int);
method public void setShowWeekNumber(boolean);
- method public void setShownWeekCount(int);
- method public void setUnfocusedMonthDateColor(int);
+ method public deprecated void setShownWeekCount(int);
+ method public deprecated void setUnfocusedMonthDateColor(int);
method public void setWeekDayTextAppearance(int);
- method public void setWeekNumberColor(int);
- method public void setWeekSeparatorLineColor(int);
+ method public deprecated void setWeekNumberColor(int);
+ method public deprecated void setWeekSeparatorLineColor(int);
}
public static abstract interface CalendarView.OnDateChangeListener {
@@ -38512,16 +38560,8 @@
ctor public FrameLayout(android.content.Context, android.util.AttributeSet, int);
ctor public FrameLayout(android.content.Context, android.util.AttributeSet, int, int);
method public deprecated boolean getConsiderGoneChildrenWhenMeasuring();
- method public android.graphics.drawable.Drawable getForeground();
- method public int getForegroundGravity();
- method public android.content.res.ColorStateList getForegroundTintList();
- method public android.graphics.PorterDuff.Mode getForegroundTintMode();
method public boolean getMeasureAllChildren();
method protected void onLayout(boolean, int, int, int, int);
- method public void setForeground(android.graphics.drawable.Drawable);
- method public void setForegroundGravity(int);
- method public void setForegroundTintList(android.content.res.ColorStateList);
- method public void setForegroundTintMode(android.graphics.PorterDuff.Mode);
method public void setMeasureAllChildren(boolean);
}
@@ -39112,7 +39152,7 @@
method public void setTouchInterceptor(android.view.View.OnTouchListener);
method public void setTouchable(boolean);
method public void setWidth(int);
- method public void setWindowLayoutMode(int, int);
+ method public deprecated void setWindowLayoutMode(int, int);
method public void showAsDropDown(android.view.View);
method public void showAsDropDown(android.view.View, int, int);
method public void showAsDropDown(android.view.View, int, int, int);
diff --git a/api/removed.txt b/api/removed.txt
index 1b209a9..c2b9d3e 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,3 +1,11 @@
+package android.content.pm {
+
+ public class PackageInfo implements android.os.Parcelable {
+ field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
+ }
+
+}
+
package android.media {
public class AudioFormat {
diff --git a/api/system-current.txt b/api/system-current.txt
index aa20ff1..f3ecd45 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -536,8 +536,8 @@
field public static final int datePickerMode = 16843955; // 0x10104b3
field public static final int datePickerStyle = 16843612; // 0x101035c
field public static final int dateTextAppearance = 16843593; // 0x1010349
- field public static final int dayOfWeekBackground = 16843924; // 0x1010494
- field public static final int dayOfWeekTextAppearance = 16843925; // 0x1010495
+ field public static final deprecated int dayOfWeekBackground = 16843924; // 0x1010494
+ field public static final deprecated int dayOfWeekTextAppearance = 16843925; // 0x1010495
field public static final int debuggable = 16842767; // 0x101000f
field public static final int defaultValue = 16843245; // 0x10101ed
field public static final int delay = 16843212; // 0x10101cc
@@ -627,6 +627,7 @@
field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
field public static final int exported = 16842768; // 0x1010010
field public static final int extraTension = 16843371; // 0x101026b
+ field public static final int extractNativeLibs = 16844008; // 0x10104e8
field public static final int factor = 16843219; // 0x10101d3
field public static final int fadeDuration = 16843384; // 0x1010278
field public static final int fadeEnabled = 16843390; // 0x101027e
@@ -659,7 +660,7 @@
field public static final int flipInterval = 16843129; // 0x1010179
field public static final int focusable = 16842970; // 0x10100da
field public static final int focusableInTouchMode = 16842971; // 0x10100db
- field public static final int focusedMonthDateColor = 16843587; // 0x1010343
+ field public static final deprecated int focusedMonthDateColor = 16843587; // 0x1010343
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
@@ -723,9 +724,9 @@
field public static final int hasCode = 16842764; // 0x101000c
field public static final int headerAmPmTextAppearance = 16843936; // 0x10104a0
field public static final int headerBackground = 16843055; // 0x101012f
- field public static final int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
+ field public static final deprecated int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
field public static final int headerDividersEnabled = 16843310; // 0x101022e
- field public static final int headerMonthTextAppearance = 16843926; // 0x1010496
+ field public static final deprecated int headerMonthTextAppearance = 16843926; // 0x1010496
field public static final int headerTimeTextAppearance = 16843935; // 0x101049f
field public static final int headerYearTextAppearance = 16843928; // 0x1010498
field public static final int height = 16843093; // 0x1010155
@@ -1170,8 +1171,8 @@
field public static final int selectable = 16843238; // 0x10101e6
field public static final int selectableItemBackground = 16843534; // 0x101030e
field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c
- field public static final int selectedDateVerticalBar = 16843591; // 0x1010347
- field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
+ field public static final deprecated int selectedDateVerticalBar = 16843591; // 0x1010347
+ field public static final deprecated int selectedWeekBackgroundColor = 16843586; // 0x1010342
field public static final int sessionService = 16843837; // 0x101043d
field public static final int settingsActivity = 16843301; // 0x1010225
field public static final int setupActivity = 16843766; // 0x10103f6
@@ -1190,8 +1191,8 @@
field public static final int showOnLockScreen = 16843721; // 0x10103c9
field public static final int showSilent = 16843259; // 0x10101fb
field public static final int showText = 16843949; // 0x10104ad
- field public static final int showWeekNumber = 16843582; // 0x101033e
- field public static final int shownWeekCount = 16843585; // 0x1010341
+ field public static final deprecated int showWeekNumber = 16843582; // 0x101033e
+ field public static final deprecated int shownWeekCount = 16843585; // 0x1010341
field public static final int shrinkColumns = 16843082; // 0x101014a
field public static final deprecated int singleLine = 16843101; // 0x101015d
field public static final int singleUser = 16843711; // 0x10103bf
@@ -1428,7 +1429,7 @@
field public static final int typeface = 16842902; // 0x1010096
field public static final int uiOptions = 16843672; // 0x1010398
field public static final int uncertainGestureColor = 16843382; // 0x1010276
- field public static final int unfocusedMonthDateColor = 16843588; // 0x1010344
+ field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
field public static final int useDefaultMargins = 16843641; // 0x1010379
@@ -1470,8 +1471,8 @@
field public static final int webTextViewStyle = 16843449; // 0x10102b9
field public static final int webViewStyle = 16842885; // 0x1010085
field public static final int weekDayTextAppearance = 16843592; // 0x1010348
- field public static final int weekNumberColor = 16843589; // 0x1010345
- field public static final int weekSeparatorLineColor = 16843590; // 0x1010346
+ field public static final deprecated int weekNumberColor = 16843589; // 0x1010345
+ field public static final deprecated int weekSeparatorLineColor = 16843590; // 0x1010346
field public static final int weightSum = 16843048; // 0x1010128
field public static final int widgetCategory = 16843716; // 0x10103c4
field public static final int widgetLayout = 16843243; // 0x10101eb
@@ -1530,7 +1531,7 @@
field public static final int xlargeScreens = 16843455; // 0x10102bf
field public static final int y = 16842925; // 0x10100ad
field public static final int yearListItemTextAppearance = 16843929; // 0x1010499
- field public static final int yearListSelectorColor = 16843930; // 0x101049a
+ field public static final deprecated int yearListSelectorColor = 16843930; // 0x101049a
field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
field public static final int zAdjustment = 16843201; // 0x10101c1
}
@@ -3514,6 +3515,7 @@
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
method public void onProvideAssistContent(android.app.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
+ method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method protected void onRestart();
method protected void onRestoreInstanceState(android.os.Bundle);
method public void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
@@ -3544,6 +3546,7 @@
method public boolean releaseInstance();
method public final deprecated void removeDialog(int);
method public void reportFullyDrawn();
+ method public final void requestPermissions(java.lang.String[], int);
method public boolean requestVisibleBehind(boolean);
method public final boolean requestWindowFeature(int);
method public final void runOnUiThread(java.lang.Runnable);
@@ -4067,22 +4070,25 @@
method public int describeContents();
method public android.content.ComponentName getActivityComponent();
method public static android.app.AssistStructure getAssistStructure(android.os.Bundle);
- method public void getWindowAt(int, android.app.AssistStructure.ViewNode);
- method public int getWindowCount();
+ method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
+ method public int getWindowNodeCount();
method public void writeToParcel(android.os.Parcel, int);
field public static final java.lang.String ASSIST_KEY = "android:assist_structure";
field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
}
public static class AssistStructure.ViewNode {
- ctor public AssistStructure.ViewNode();
- method public void getChildAt(int, android.app.AssistStructure.ViewNode);
+ method public android.app.AssistStructure.ViewNode getChildAt(int);
method public int getChildCount();
method public java.lang.String getClassName();
- method public java.lang.String getContentDescription();
+ method public java.lang.CharSequence getContentDescription();
method public android.os.Bundle getExtras();
method public int getHeight();
method public java.lang.String getHint();
+ method public int getId();
+ method public java.lang.String getIdEntry();
+ method public java.lang.String getIdPackage();
+ method public java.lang.String getIdType();
method public int getLeft();
method public int getScrollX();
method public int getScrollY();
@@ -4113,6 +4119,15 @@
field public static final int TEXT_STYLE_UNDERLINE = 4; // 0x4
}
+ public static class AssistStructure.WindowNode {
+ method public int getHeight();
+ method public int getLeft();
+ method public android.app.AssistStructure.ViewNode getRootViewNode();
+ method public java.lang.CharSequence getTitle();
+ method public int getTop();
+ method public int getWidth();
+ }
+
public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
@@ -4393,6 +4408,7 @@
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPause();
method public void onPrepareOptionsMenu(android.view.Menu);
+ method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method public void onResume();
method public void onSaveInstanceState(android.os.Bundle);
method public void onStart();
@@ -4401,6 +4417,7 @@
method public void onViewCreated(android.view.View, android.os.Bundle);
method public void onViewStateRestored(android.os.Bundle);
method public void registerForContextMenu(android.view.View);
+ method public final void requestPermissions(java.lang.String[], int);
method public void setAllowEnterTransitionOverlap(boolean);
method public void setAllowReturnTransitionOverlap(boolean);
method public void setArguments(android.os.Bundle);
@@ -5767,12 +5784,18 @@
field public static final java.lang.String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
field public static final java.lang.String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
field public static final java.lang.String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
+ field public static final java.lang.String EXTRA_PROVISIONING_BT_DEVICE_ID = "android.app.extra.PROVISIONING_BT_DEVICE_ID";
+ field public static final java.lang.String EXTRA_PROVISIONING_BT_MAC_ADDRESS = "android.app.extra.PROVISIONING_BT_MAC_ADDRESS";
+ field public static final java.lang.String EXTRA_PROVISIONING_BT_USE_PROXY = "android.app.extra.PROVISIONING_BT_USE_PROXY";
+ field public static final java.lang.String EXTRA_PROVISIONING_BT_UUID = "android.app.extra.PROVISIONING_BT_UUID";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
field public static final deprecated java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
@@ -7601,6 +7624,7 @@
method public abstract int checkCallingPermission(java.lang.String);
method public abstract int checkCallingUriPermission(android.net.Uri, int);
method public abstract int checkPermission(java.lang.String, int, int);
+ method public abstract int checkSelfPermission(java.lang.String);
method public abstract int checkUriPermission(android.net.Uri, int, int, int);
method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public abstract deprecated void clearWallpaper() throws java.io.IOException;
@@ -7784,6 +7808,7 @@
method public int checkCallingPermission(java.lang.String);
method public int checkCallingUriPermission(android.net.Uri, int);
method public int checkPermission(java.lang.String, int, int);
+ method public int checkSelfPermission(java.lang.String);
method public int checkUriPermission(android.net.Uri, int, int, int);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public deprecated void clearWallpaper() throws java.io.IOException;
@@ -8860,6 +8885,7 @@
field public static final int FLAG_ALLOW_TASK_REPARENTING = 32; // 0x20
field public static final int FLAG_DEBUGGABLE = 2; // 0x2
field public static final int FLAG_EXTERNAL_STORAGE = 262144; // 0x40000
+ field public static final int FLAG_EXTRACT_NATIVE_LIBS = 268435456; // 0x10000000
field public static final int FLAG_FACTORY_TEST = 16; // 0x10
field public static final int FLAG_FULL_BACKUP_ONLY = 67108864; // 0x4000000
field public static final int FLAG_HAS_CODE = 4; // 0x4
@@ -9064,7 +9090,6 @@
field public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1; // 0x1
field public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; // 0x2
field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
- field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
field public android.content.pm.ActivityInfo[] activities;
field public android.content.pm.ApplicationInfo applicationInfo;
field public int baseRevisionCode;
@@ -9264,6 +9289,7 @@
method public abstract android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public abstract void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract boolean hasSystemFeature(java.lang.String);
method public abstract boolean isSafeMode();
method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9279,16 +9305,20 @@
method public abstract android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
method public abstract android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
+ method public abstract void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
method public abstract void verifyPendingInstall(int, int);
+ field public static final java.lang.String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2
field public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4; // 0x4
field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
field public static final int DONT_KILL_APP = 1; // 0x1
+ field public static final java.lang.String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
+ field public static final java.lang.String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
field public static final java.lang.String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
field public static final java.lang.String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
field public static final java.lang.String FEATURE_APP_WIDGETS = "android.software.app_widgets";
@@ -12146,9 +12176,12 @@
public class AnimatedVectorDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable {
ctor public AnimatedVectorDrawable();
+ method public void addListener(android.animation.Animator.AnimatorListener);
method public void draw(android.graphics.Canvas);
+ method public java.util.List<android.animation.Animator.AnimatorListener> getListeners();
method public int getOpacity();
method public boolean isRunning();
+ method public void removeListener(android.animation.Animator.AnimatorListener);
method public void setAlpha(int);
method public void setColorFilter(android.graphics.ColorFilter);
method public void start();
@@ -16414,6 +16447,9 @@
field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
field public static final java.lang.String PROPERTY_VENDOR = "vendor";
field public static final java.lang.String PROPERTY_VERSION = "version";
+ field public static final int REQUEST_TYPE_INITIAL = 0; // 0x0
+ field public static final int REQUEST_TYPE_RELEASE = 2; // 0x2
+ field public static final int REQUEST_TYPE_RENEWAL = 1; // 0x1
}
public final class MediaDrm.CryptoSession {
@@ -16426,6 +16462,7 @@
public static final class MediaDrm.KeyRequest {
method public byte[] getData();
method public java.lang.String getDefaultUrl();
+ method public int getRequestType();
}
public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException {
@@ -18537,6 +18574,10 @@
field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
@@ -24638,6 +24679,7 @@
}
public final class PowerManager {
+ method public boolean isDeviceIdleMode();
method public boolean isInteractive();
method public boolean isPowerSaveMode();
method public deprecated boolean isScreenOn();
@@ -24646,6 +24688,7 @@
method public void reboot(java.lang.String);
method public void userActivity(long, int, int);
field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
+ field public static final java.lang.String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
field public static final deprecated int FULL_WAKE_LOCK = 26; // 0x1a
field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
@@ -32533,6 +32576,7 @@
method public int checkCallingPermission(java.lang.String);
method public int checkCallingUriPermission(android.net.Uri, int);
method public int checkPermission(java.lang.String, int, int);
+ method public int checkSelfPermission(java.lang.String);
method public int checkUriPermission(android.net.Uri, int, int, int);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
@@ -32732,6 +32776,7 @@
method public android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public boolean hasSystemFeature(java.lang.String);
method public boolean isSafeMode();
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -32747,6 +32792,7 @@
method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
+ method public void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public void setApplicationEnabledSetting(java.lang.String, int, int);
method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
method public void setInstallerPackageName(java.lang.String, java.lang.String);
@@ -36767,6 +36813,10 @@
method public boolean getFitsSystemWindows();
method public java.util.ArrayList<android.view.View> getFocusables(int);
method public void getFocusedRect(android.graphics.Rect);
+ method public android.graphics.drawable.Drawable getForeground();
+ method public int getForegroundGravity();
+ method public android.content.res.ColorStateList getForegroundTintList();
+ method public android.graphics.PorterDuff.Mode getForegroundTintMode();
method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point);
method public final boolean getGlobalVisibleRect(android.graphics.Rect);
method public android.os.Handler getHandler();
@@ -36938,6 +36988,7 @@
method protected void onDisplayHint(int);
method public boolean onDragEvent(android.view.DragEvent);
method protected void onDraw(android.graphics.Canvas);
+ method public void onDrawForeground(android.graphics.Canvas);
method protected final void onDrawScrollBars(android.graphics.Canvas);
method public boolean onFilterTouchEventForSecurity(android.view.MotionEvent);
method protected void onFinishInflate();
@@ -37043,6 +37094,10 @@
method public void setFitsSystemWindows(boolean);
method public void setFocusable(boolean);
method public void setFocusableInTouchMode(boolean);
+ method public void setForeground(android.graphics.drawable.Drawable);
+ method public void setForegroundGravity(int);
+ method public void setForegroundTintList(android.content.res.ColorStateList);
+ method public void setForegroundTintMode(android.graphics.PorterDuff.Mode);
method public void setHapticFeedbackEnabled(boolean);
method public void setHasTransientState(boolean);
method public void setHorizontalFadingEdgeEnabled(boolean);
@@ -40812,34 +40867,34 @@
method public long getDate();
method public int getDateTextAppearance();
method public int getFirstDayOfWeek();
- method public int getFocusedMonthDateColor();
+ method public deprecated int getFocusedMonthDateColor();
method public long getMaxDate();
method public long getMinDate();
- method public android.graphics.drawable.Drawable getSelectedDateVerticalBar();
- method public int getSelectedWeekBackgroundColor();
+ method public deprecated android.graphics.drawable.Drawable getSelectedDateVerticalBar();
+ method public deprecated int getSelectedWeekBackgroundColor();
method public boolean getShowWeekNumber();
- method public int getShownWeekCount();
- method public int getUnfocusedMonthDateColor();
+ method public deprecated int getShownWeekCount();
+ method public deprecated int getUnfocusedMonthDateColor();
method public int getWeekDayTextAppearance();
- method public int getWeekNumberColor();
- method public int getWeekSeparatorLineColor();
+ method public deprecated int getWeekNumberColor();
+ method public deprecated int getWeekSeparatorLineColor();
method public void setDate(long);
method public void setDate(long, boolean, boolean);
method public void setDateTextAppearance(int);
method public void setFirstDayOfWeek(int);
- method public void setFocusedMonthDateColor(int);
+ method public deprecated void setFocusedMonthDateColor(int);
method public void setMaxDate(long);
method public void setMinDate(long);
method public void setOnDateChangeListener(android.widget.CalendarView.OnDateChangeListener);
- method public void setSelectedDateVerticalBar(int);
- method public void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
- method public void setSelectedWeekBackgroundColor(int);
+ method public deprecated void setSelectedDateVerticalBar(int);
+ method public deprecated void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
+ method public deprecated void setSelectedWeekBackgroundColor(int);
method public void setShowWeekNumber(boolean);
- method public void setShownWeekCount(int);
- method public void setUnfocusedMonthDateColor(int);
+ method public deprecated void setShownWeekCount(int);
+ method public deprecated void setUnfocusedMonthDateColor(int);
method public void setWeekDayTextAppearance(int);
- method public void setWeekNumberColor(int);
- method public void setWeekSeparatorLineColor(int);
+ method public deprecated void setWeekNumberColor(int);
+ method public deprecated void setWeekSeparatorLineColor(int);
}
public static abstract interface CalendarView.OnDateChangeListener {
@@ -41172,16 +41227,8 @@
ctor public FrameLayout(android.content.Context, android.util.AttributeSet, int);
ctor public FrameLayout(android.content.Context, android.util.AttributeSet, int, int);
method public deprecated boolean getConsiderGoneChildrenWhenMeasuring();
- method public android.graphics.drawable.Drawable getForeground();
- method public int getForegroundGravity();
- method public android.content.res.ColorStateList getForegroundTintList();
- method public android.graphics.PorterDuff.Mode getForegroundTintMode();
method public boolean getMeasureAllChildren();
method protected void onLayout(boolean, int, int, int, int);
- method public void setForeground(android.graphics.drawable.Drawable);
- method public void setForegroundGravity(int);
- method public void setForegroundTintList(android.content.res.ColorStateList);
- method public void setForegroundTintMode(android.graphics.PorterDuff.Mode);
method public void setMeasureAllChildren(boolean);
}
@@ -41772,7 +41819,7 @@
method public void setTouchInterceptor(android.view.View.OnTouchListener);
method public void setTouchable(boolean);
method public void setWidth(int);
- method public void setWindowLayoutMode(int, int);
+ method public deprecated void setWindowLayoutMode(int, int);
method public void showAsDropDown(android.view.View);
method public void showAsDropDown(android.view.View, int, int);
method public void showAsDropDown(android.view.View, int, int, int);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 1b209a9..c2b9d3e 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -1,3 +1,11 @@
+package android.content.pm {
+
+ public class PackageInfo implements android.os.Parcelable {
+ field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
+ }
+
+}
+
package android.media {
public class AudioFormat {
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index c48a618..89dd079 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -892,6 +892,8 @@
installFlags |= PackageManager.INSTALL_INTERNAL;
} else if (opt.equals("-d")) {
installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ } else if (opt.equals("-g")) {
+ installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
} else if (opt.equals("--originating-uri")) {
originatingUriString = nextOptionData();
if (originatingUriString == null) {
@@ -1517,6 +1519,15 @@
}
private int runGrantRevokePermission(boolean grant) {
+ int userId = UserHandle.USER_CURRENT;
+
+ String opt = null;
+ while ((opt = nextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = Integer.parseInt(nextArg());
+ }
+ }
+
String pkg = nextArg();
if (pkg == null) {
System.err.println("Error: no package specified");
@@ -1529,11 +1540,12 @@
showUsage();
return 1;
}
+
try {
if (grant) {
- mPm.grantPermission(pkg, perm);
+ mPm.grantPermission(pkg, perm, userId);
} else {
- mPm.revokePermission(pkg, perm);
+ mPm.revokePermission(pkg, perm, userId);
}
return 0;
} catch (RemoteException e) {
@@ -1815,8 +1827,8 @@
System.err.println(" pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm hide [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT");
- System.err.println(" pm grant PACKAGE PERMISSION");
- System.err.println(" pm revoke PACKAGE PERMISSION");
+ System.err.println(" pm grant [--user USER_ID] PACKAGE PERMISSION");
+ System.err.println(" pm revoke [--user USER_ID] PACKAGE PERMISSION");
System.err.println(" pm set-install-location [0/auto] [1/internal] [2/external]");
System.err.println(" pm get-install-location");
System.err.println(" pm set-permission-enforced PERMISSION [true|false]");
@@ -1868,6 +1880,7 @@
System.err.println(" -f: install application on internal flash");
System.err.println(" -d: allow version code downgrade");
System.err.println(" -p: partial application install");
+ System.err.println(" -g: grant all runtime permissions");
System.err.println(" -S: size in bytes of entire session");
System.err.println("");
System.err.println("pm install-write: write a package into existing session; path may");
@@ -1889,8 +1902,9 @@
System.err.println(" as \"package/class\").");
System.err.println("");
System.err.println("pm grant, revoke: these commands either grant or revoke permissions");
- System.err.println(" to applications. Only optional permissions the application has");
- System.err.println(" declared can be granted or revoked.");
+ System.err.println(" to apps. The permissions must be declared as used in the app's");
+ System.err.println(" manifest, be runtime permissions (protection level dangerous),");
+ System.err.println(" and the app targeting SDK greater than Lollipop MR1.");
System.err.println("");
System.err.println("pm get-install-location: returns the current install location.");
System.err.println(" 0 [auto]: Let system decide the best location");
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7fcbe35..b5817df5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3726,6 +3726,95 @@
}
/**
+ * Requests permissions to be granted to this application. These permissions
+ * must be requested in your manifest, they should not be granted to your app,
+ * and they should have protection level {@link android.content.pm.PermissionInfo
+ * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
+ * the platform or a third-party app.
+ * <p>
+ * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
+ * are granted at install time if requested in the manifest. Signature permissions
+ * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
+ * install time if requested in the manifest and the signature of your app matches
+ * the signature of the app declaring the permissions.
+ * </p>
+ * <p>
+ * If your app does not have the requested permissions the user will be presented
+ * with UI for accepting them. After the user has accepted or rejected the
+ * requested permissions you will receive a callback on {@link
+ * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
+ * permissions were granted or not.
+ * </p>
+ * <p>
+ * Note that requesting a permission does not guarantee it will be granted and
+ * your app should be able to run without having this permission.
+ * </p>
+ * <p>
+ * This method may start an activity allowing the user to choose which permissions
+ * to grant and which to reject. Hence, you should be prepared that your activity
+ * may be paused and resumed. Further, granting some permissions may require
+ * a restart of you application. In such a case, the system will recreate the
+ * activity stack before delivering the result to {@link
+ * #onRequestPermissionsResult(int, String[], int[])}.
+ * </p>
+ * <p>
+ * When checking whether you have a permission you should use {@link
+ * #checkSelfPermission(String)}.
+ * </p>
+ * <p>
+ * A sample permissions request looks like this:
+ * </p>
+ * <code><pre><p>
+ * private void showContacts() {
+ * if (checkSelfPermission(Manifest.permission.READ_CONTACTS)
+ * != PackageManager.PERMISSION_GRANTED) {
+ * requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
+ * PERMISSIONS_REQUEST_READ_CONTACTS);
+ * } else {
+ * doShowContacts();
+ * }
+ * }
+ *
+ * {@literal @}Override
+ * public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ * int[] grantResults) {
+ * if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS
+ * && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ * showContacts();
+ * }
+ * }
+ * </code></pre></p>
+ *
+ * @param permissions The requested permissions.
+ * @param requestCode Application specific request code to match with a result
+ * reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
+ *
+ * @see #onRequestPermissionsResult(int, String[], int[])
+ * @see #checkSelfPermission(String)
+ */
+ public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
+ Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
+ startActivityForResult(intent, requestCode);
+ }
+
+ /**
+ * Callback for the result from requesting permissions. This method
+ * is invoked for every call on {@link #requestPermissions(String[], int)}.
+ *
+ * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
+ * @param permissions The requested permissions. Never null.
+ * @param grantResults The grant results for the corresponding permissions
+ * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
+ * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
+ *
+ * @see #requestPermissions(String[], int)
+ */
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ /* callback - no nothing */
+ }
+
+ /**
* Same as calling {@link #startActivityForResult(Intent, int, Bundle)}
* with no options.
*
@@ -6269,11 +6358,19 @@
+ ", resCode=" + resultCode + ", data=" + data);
mFragments.noteStateNotSaved();
if (who == null) {
- onActivityResult(requestCode, resultCode, data);
+ if (isRequestPermissionResult(data)) {
+ dispatchRequestPermissionsResult(requestCode, data);
+ } else {
+ onActivityResult(requestCode, resultCode, data);
+ }
} else {
Fragment frag = mFragments.findFragmentByWho(who);
if (frag != null) {
- frag.onActivityResult(requestCode, resultCode, data);
+ if (isRequestPermissionResult(data)) {
+ dispatchRequestPermissionsResultToFragment(requestCode, data, frag);
+ } else {
+ frag.onActivityResult(requestCode, resultCode, data);
+ }
}
}
}
@@ -6343,4 +6440,26 @@
*/
public void onTranslucentConversionComplete(boolean drawComplete);
}
+
+ private void dispatchRequestPermissionsResult(int requestCode, Intent data) {
+ String[] permissions = data.getStringArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
+ final int[] grantResults = data.getIntArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS);
+ onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ private void dispatchRequestPermissionsResultToFragment(int requestCode, Intent data,
+ Fragment fragement) {
+ String[] permissions = data.getStringArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
+ final int[] grantResults = data.getIntArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS);
+ fragement.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ private static boolean isRequestPermissionResult(Intent intent) {
+ return intent != null
+ && PackageManager.ACTION_REQUEST_PERMISSIONS.equals(intent.getAction());
+ }
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 29b024ac..d143f8b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2493,7 +2493,8 @@
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
// Root, system server get to do everything.
- if (uid == 0 || uid == Process.SYSTEM_UID) {
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
return PackageManager.PERMISSION_GRANTED;
}
// Isolated processes don't get any permissions.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9f81670..6d74905 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -81,7 +81,6 @@
/*package*/
final class ApplicationPackageManager extends PackageManager {
private static final String TAG = "ApplicationPackageManager";
- private final static boolean DEBUG = false;
private final static boolean DEBUG_ICONS = false;
// Default flags to use with PackageManager when no flags are given.
@@ -186,8 +185,8 @@
public int[] getPackageGids(String packageName)
throws NameNotFoundException {
try {
- int[] gids = mPM.getPackageGids(packageName);
- if (gids == null || gids.length > 0) {
+ int[] gids = mPM.getPackageGids(packageName, mContext.getUserId());
+ if (gids != null) {
return gids;
}
} catch (RemoteException e) {
@@ -398,7 +397,7 @@
@Override
public int checkPermission(String permName, String pkgName) {
try {
- return mPM.checkPermission(permName, pkgName);
+ return mPM.checkPermission(permName, pkgName, mContext.getUserId());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -432,18 +431,18 @@
}
@Override
- public void grantPermission(String packageName, String permissionName) {
+ public void grantPermission(String packageName, String permissionName, UserHandle user) {
try {
- mPM.grantPermission(packageName, permissionName);
+ mPM.grantPermission(packageName, permissionName, user.getIdentifier());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
}
@Override
- public void revokePermission(String packageName, String permissionName) {
+ public void revokePermission(String packageName, String permissionName, UserHandle user) {
try {
- mPM.revokePermission(packageName, permissionName);
+ mPM.revokePermission(packageName, permissionName, user.getIdentifier());
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index 25153fc5..c435ccb 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -17,6 +17,7 @@
package android.app;
import android.content.ComponentName;
+import android.content.res.Resources;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
@@ -53,7 +54,7 @@
final ComponentName mActivityComponent;
- final ArrayList<ViewNodeImpl> mRootViews = new ArrayList<>();
+ final ArrayList<WindowNode> mWindowNodes = new ArrayList<>();
ViewAssistStructureImpl mTmpViewAssistStructureImpl = new ViewAssistStructureImpl();
Bundle mTmpExtras = new Bundle();
@@ -178,7 +179,91 @@
}
}
- final static class ViewNodeImpl {
+ /**
+ * Describes a window in the assist data.
+ */
+ static public class WindowNode {
+ final int mX;
+ final int mY;
+ final int mWidth;
+ final int mHeight;
+ final CharSequence mTitle;
+ final ViewNode mRoot;
+
+ WindowNode(AssistStructure assist, ViewRootImpl root) {
+ View view = root.getView();
+ Rect rect = new Rect();
+ view.getBoundsOnScreen(rect);
+ mX = rect.left - view.getLeft();
+ mY = rect.top - view.getTop();
+ mWidth = rect.width();
+ mHeight = rect.height();
+ mTitle = root.getTitle();
+ mRoot = new ViewNode(assist, view);
+ }
+
+ WindowNode(Parcel in, PooledStringReader preader) {
+ mX = in.readInt();
+ mY = in.readInt();
+ mWidth = in.readInt();
+ mHeight = in.readInt();
+ mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mRoot = new ViewNode(in, preader);
+ }
+
+ void writeToParcel(Parcel out, PooledStringWriter pwriter) {
+ out.writeInt(mX);
+ out.writeInt(mY);
+ out.writeInt(mWidth);
+ out.writeInt(mHeight);
+ TextUtils.writeToParcel(mTitle, out, 0);
+ mRoot.writeToParcel(out, pwriter);
+ }
+
+ public int getLeft() {
+ return mX;
+ }
+
+ public int getTop() {
+ return mY;
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ public ViewNode getRootViewNode() {
+ return mRoot;
+ }
+ }
+
+ /**
+ * Describes a single view in the assist data.
+ */
+ static public class ViewNode {
+ /**
+ * Magic value for text color that has not been defined, which is very unlikely
+ * to be confused with a real text color.
+ */
+ public static final int TEXT_COLOR_UNDEFINED = 1;
+
+ public static final int TEXT_STYLE_BOLD = 1<<0;
+ public static final int TEXT_STYLE_ITALIC = 1<<1;
+ public static final int TEXT_STYLE_UNDERLINE = 1<<2;
+ public static final int TEXT_STYLE_STRIKE_THRU = 1<<3;
+
+ final int mId;
+ final String mIdPackage;
+ final String mIdType;
+ final String mIdEntry;
final int mX;
final int mY;
final int mScrollX;
@@ -201,17 +286,34 @@
final int mFlags;
final String mClassName;
- final String mContentDescription;
+ final CharSequence mContentDescription;
final ViewNodeTextImpl mText;
final Bundle mExtras;
- final ViewNodeImpl[] mChildren;
+ final ViewNode[] mChildren;
- ViewNodeImpl(AssistStructure assistStructure, View view, int left, int top,
- CharSequence contentDescription) {
- mX = left;
- mY = top;
+ ViewNode(AssistStructure assistStructure, View view) {
+ mId = view.getId();
+ if (mId > 0 && (mId&0xff000000) != 0 && (mId&0x00ff0000) != 0
+ && (mId&0x0000ffff) != 0) {
+ String pkg, type, entry;
+ try {
+ Resources res = view.getResources();
+ entry = res.getResourceEntryName(mId);
+ type = res.getResourceTypeName(mId);
+ pkg = res.getResourcePackageName(mId);
+ } catch (Resources.NotFoundException e) {
+ entry = type = pkg = null;
+ }
+ mIdPackage = pkg;
+ mIdType = type;
+ mIdEntry = entry;
+ } else {
+ mIdPackage = mIdType = mIdEntry = null;
+ }
+ mX = view.getLeft();
+ mY = view.getTop();
mScrollX = view.getScrollX();
mScrollY = view.getScrollY();
mWidth = view.getWidth();
@@ -249,7 +351,7 @@
}
mFlags = flags;
mClassName = view.getAccessibilityClassName().toString();
- mContentDescription = contentDescription != null ? contentDescription.toString() : null;
+ mContentDescription = view.getContentDescription();
final ViewAssistStructureImpl viewData = assistStructure.mTmpViewAssistStructureImpl;
final Bundle extras = assistStructure.mTmpExtras;
view.onProvideAssistStructure(viewData, extras);
@@ -269,9 +371,9 @@
ViewGroup vg = (ViewGroup)view;
final int NCHILDREN = vg.getChildCount();
if (NCHILDREN > 0) {
- mChildren = new ViewNodeImpl[NCHILDREN];
+ mChildren = new ViewNode[NCHILDREN];
for (int i=0; i<NCHILDREN; i++) {
- mChildren[i] = new ViewNodeImpl(assistStructure, vg.getChildAt(i));
+ mChildren[i] = new ViewNode(assistStructure, vg.getChildAt(i));
}
} else {
mChildren = null;
@@ -281,11 +383,19 @@
}
}
- ViewNodeImpl(AssistStructure assistStructure, View view) {
- this(assistStructure, view, view.getLeft(), view.getTop(), view.getContentDescription());
- }
-
- ViewNodeImpl(Parcel in, PooledStringReader preader) {
+ ViewNode(Parcel in, PooledStringReader preader) {
+ mId = in.readInt();
+ if (mId != 0) {
+ mIdEntry = preader.readString();
+ if (mIdEntry != null) {
+ mIdType = preader.readString();
+ mIdPackage = preader.readString();
+ } else {
+ mIdPackage = mIdType = null;
+ }
+ } else {
+ mIdPackage = mIdType = mIdEntry = null;
+ }
mX = in.readInt();
mY = in.readInt();
mScrollX = in.readInt();
@@ -294,7 +404,7 @@
mHeight = in.readInt();
mFlags = in.readInt();
mClassName = preader.readString();
- mContentDescription = in.readString();
+ mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
if (in.readInt() != 0) {
mText = new ViewNodeTextImpl(in);
} else {
@@ -303,9 +413,9 @@
mExtras = in.readBundle();
final int NCHILDREN = in.readInt();
if (NCHILDREN > 0) {
- mChildren = new ViewNodeImpl[NCHILDREN];
+ mChildren = new ViewNode[NCHILDREN];
for (int i=0; i<NCHILDREN; i++) {
- mChildren[i] = new ViewNodeImpl(in, preader);
+ mChildren[i] = new ViewNode(in, preader);
}
} else {
mChildren = null;
@@ -313,6 +423,14 @@
}
void writeToParcel(Parcel out, PooledStringWriter pwriter) {
+ out.writeInt(mId);
+ if (mId != 0) {
+ pwriter.writeString(mIdEntry);
+ if (mIdEntry != null) {
+ pwriter.writeString(mIdType);
+ pwriter.writeString(mIdPackage);
+ }
+ }
out.writeInt(mX);
out.writeInt(mY);
out.writeInt(mScrollX);
@@ -321,7 +439,7 @@
out.writeInt(mHeight);
out.writeInt(mFlags);
pwriter.writeString(mClassName);
- out.writeString(mContentDescription);
+ TextUtils.writeToParcel(mContentDescription, out, 0);
if (mText != null) {
out.writeInt(1);
mText.writeToParcel(out);
@@ -339,146 +457,141 @@
out.writeInt(0);
}
}
- }
- /**
- * Provides access to information about a single view in the assist data.
- */
- static public class ViewNode {
- /**
- * Magic value for text color that has not been defined, which is very unlikely
- * to be confused with a real text color.
- */
- public static final int TEXT_COLOR_UNDEFINED = 1;
+ public int getId() {
+ return mId;
+ }
- public static final int TEXT_STYLE_BOLD = 1<<0;
- public static final int TEXT_STYLE_ITALIC = 1<<1;
- public static final int TEXT_STYLE_UNDERLINE = 1<<2;
- public static final int TEXT_STYLE_STRIKE_THRU = 1<<3;
+ public String getIdPackage() {
+ return mIdPackage;
+ }
- ViewNodeImpl mImpl;
+ public String getIdType() {
+ return mIdType;
+ }
- public ViewNode() {
+ public String getIdEntry() {
+ return mIdEntry;
}
public int getLeft() {
- return mImpl.mX;
+ return mX;
}
public int getTop() {
- return mImpl.mY;
+ return mY;
}
public int getScrollX() {
- return mImpl.mScrollX;
+ return mScrollX;
}
public int getScrollY() {
- return mImpl.mScrollY;
+ return mScrollY;
}
public int getWidth() {
- return mImpl.mWidth;
+ return mWidth;
}
public int getHeight() {
- return mImpl.mHeight;
+ return mHeight;
}
public int getVisibility() {
- return mImpl.mFlags&ViewNodeImpl.FLAGS_VISIBILITY_MASK;
+ return mFlags&ViewNode.FLAGS_VISIBILITY_MASK;
}
public boolean isEnabled() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_DISABLED) == 0;
+ return (mFlags&ViewNode.FLAGS_DISABLED) == 0;
}
public boolean isClickable() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_CLICKABLE) != 0;
+ return (mFlags&ViewNode.FLAGS_CLICKABLE) != 0;
}
public boolean isFocusable() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_FOCUSABLE) != 0;
+ return (mFlags&ViewNode.FLAGS_FOCUSABLE) != 0;
}
public boolean isFocused() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_FOCUSED) != 0;
+ return (mFlags&ViewNode.FLAGS_FOCUSED) != 0;
}
public boolean isAccessibilityFocused() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_ACCESSIBILITY_FOCUSED) != 0;
+ return (mFlags&ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) != 0;
}
public boolean isCheckable() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_CHECKABLE) != 0;
+ return (mFlags&ViewNode.FLAGS_CHECKABLE) != 0;
}
public boolean isChecked() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_CHECKED) != 0;
+ return (mFlags&ViewNode.FLAGS_CHECKED) != 0;
}
public boolean isSelected() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_SELECTED) != 0;
+ return (mFlags&ViewNode.FLAGS_SELECTED) != 0;
}
public boolean isActivated() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_ACTIVATED) != 0;
+ return (mFlags&ViewNode.FLAGS_ACTIVATED) != 0;
}
public boolean isLongClickable() {
- return (mImpl.mFlags&ViewNodeImpl.FLAGS_LONG_CLICKABLE) != 0;
+ return (mFlags&ViewNode.FLAGS_LONG_CLICKABLE) != 0;
}
public String getClassName() {
- return mImpl.mClassName;
+ return mClassName;
}
- public String getContentDescription() {
- return mImpl.mContentDescription;
+ public CharSequence getContentDescription() {
+ return mContentDescription;
}
public CharSequence getText() {
- return mImpl.mText != null ? mImpl.mText.mText : null;
+ return mText != null ? mText.mText : null;
}
public int getTextSelectionStart() {
- return mImpl.mText != null ? mImpl.mText.mTextSelectionStart : -1;
+ return mText != null ? mText.mTextSelectionStart : -1;
}
public int getTextSelectionEnd() {
- return mImpl.mText != null ? mImpl.mText.mTextSelectionEnd : -1;
+ return mText != null ? mText.mTextSelectionEnd : -1;
}
public int getTextColor() {
- return mImpl.mText != null ? mImpl.mText.mTextColor : TEXT_COLOR_UNDEFINED;
+ return mText != null ? mText.mTextColor : TEXT_COLOR_UNDEFINED;
}
public int getTextBackgroundColor() {
- return mImpl.mText != null ? mImpl.mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
+ return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
}
public float getTextSize() {
- return mImpl.mText != null ? mImpl.mText.mTextSize : 0;
+ return mText != null ? mText.mTextSize : 0;
}
public int getTextStyle() {
- return mImpl.mText != null ? mImpl.mText.mTextStyle : 0;
+ return mText != null ? mText.mTextStyle : 0;
}
public String getHint() {
- return mImpl.mText != null ? mImpl.mText.mHint : null;
+ return mText != null ? mText.mHint : null;
}
public Bundle getExtras() {
- return mImpl.mExtras;
+ return mExtras;
}
public int getChildCount() {
- return mImpl.mChildren != null ? mImpl.mChildren.length : 0;
+ return mChildren != null ? mChildren.length : 0;
}
- public void getChildAt(int index, ViewNode outNode) {
- outNode.mImpl = mImpl.mChildren[index];
+ public ViewNode getChildAt(int index) {
+ return mChildren[index];
}
}
@@ -488,12 +601,7 @@
activity.getActivityToken());
for (int i=0; i<views.size(); i++) {
ViewRootImpl root = views.get(i);
- View view = root.getView();
- Rect rect = new Rect();
- view.getBoundsOnScreen(rect);
- CharSequence title = root.getTitle();
- mRootViews.add(new ViewNodeImpl(this, view, rect.left, rect.top,
- title != null ? title : view.getContentDescription()));
+ mWindowNodes.add(new WindowNode(this, root));
}
}
@@ -502,7 +610,7 @@
mActivityComponent = ComponentName.readFromParcel(in);
final int N = in.readInt();
for (int i=0; i<N; i++) {
- mRootViews.add(new ViewNodeImpl(in, preader));
+ mWindowNodes.add(new WindowNode(in, preader));
}
//dump();
}
@@ -510,24 +618,37 @@
/** @hide */
public void dump() {
Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString());
- ViewNode node = new ViewNode();
- final int N = getWindowCount();
+ final int N = getWindowNodeCount();
for (int i=0; i<N; i++) {
- Log.i(TAG, "Window #" + i + ":");
- getWindowAt(i, node);
- dump(" ", node);
+ WindowNode node = getWindowNodeAt(i);
+ Log.i(TAG, "Window #" + i + " [" + node.getLeft() + "," + node.getTop()
+ + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getTitle());
+ dump(" ", node.getRootViewNode());
}
}
void dump(String prefix, ViewNode node) {
Log.i(TAG, prefix + "View [" + node.getLeft() + "," + node.getTop()
+ " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getClassName());
+ int id = node.getId();
+ if (id != 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(prefix); sb.append(" ID: #"); sb.append(Integer.toHexString(id));
+ String entry = node.getIdEntry();
+ if (entry != null) {
+ String type = node.getIdType();
+ String pkg = node.getIdPackage();
+ sb.append(" "); sb.append(pkg); sb.append(":"); sb.append(type);
+ sb.append("/"); sb.append(entry);
+ }
+ Log.i(TAG, sb.toString());
+ }
int scrollX = node.getScrollX();
int scrollY = node.getScrollY();
if (scrollX != 0 || scrollY != 0) {
Log.i(TAG, prefix + " Scroll: " + scrollX + "," + scrollY);
}
- String contentDescription = node.getContentDescription();
+ CharSequence contentDescription = node.getContentDescription();
if (contentDescription != null) {
Log.i(TAG, prefix + " Content description: " + contentDescription);
}
@@ -552,9 +673,8 @@
if (NCHILDREN > 0) {
Log.i(TAG, prefix + " Children:");
String cprefix = prefix + " ";
- ViewNode cnode = new ViewNode();
for (int i=0; i<NCHILDREN; i++) {
- node.getChildAt(i, cnode);
+ ViewNode cnode = node.getChildAt(i);
dump(cprefix, cnode);
}
}
@@ -575,17 +695,16 @@
/**
* Return the number of window contents that have been collected in this assist data.
*/
- public int getWindowCount() {
- return mRootViews.size();
+ public int getWindowNodeCount() {
+ return mWindowNodes.size();
}
/**
- * Return the root view for one of the windows in the assist data.
- * @param index Which window to retrieve, may be 0 to {@link #getWindowCount()}-1.
- * @param outNode Node in which to place the window's root view.
+ * Return one of the windows in the assist data.
+ * @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1.
*/
- public void getWindowAt(int index, ViewNode outNode) {
- outNode.mImpl = mRootViews.get(index);
+ public WindowNode getWindowNodeAt(int index) {
+ return mWindowNodes.get(index);
}
public int describeContents() {
@@ -596,10 +715,10 @@
int start = out.dataPosition();
PooledStringWriter pwriter = new PooledStringWriter(out);
ComponentName.writeToParcel(mActivityComponent, out);
- final int N = mRootViews.size();
+ final int N = mWindowNodes.size();
out.writeInt(N);
for (int i=0; i<N; i++) {
- mRootViews.get(i).writeToParcel(out, pwriter);
+ mWindowNodes.get(i).writeToParcel(out, pwriter);
}
pwriter.finish();
Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes");
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index eb27830..4ccd69f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1323,6 +1323,15 @@
Binder.getCallingUid());
}
+ @Override
+ public int checkSelfPermission(String permission) {
+ if (permission == null) {
+ throw new IllegalArgumentException("permission is null");
+ }
+
+ return checkPermission(permission, Process.myPid(), Process.myUid());
+ }
+
private void enforce(
String permission, int resultOfCheck,
boolean selfToo, int uid, String message) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index bdcc312..4fdae7f 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -17,6 +17,7 @@
package android.app;
import android.animation.Animator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.content.ComponentCallbacks2;
@@ -1092,13 +1093,7 @@
if (mActivity == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
- if (options != null) {
- mActivity.startActivityFromFragment(this, intent, requestCode, options);
- } else {
- // Note we want to go through this call for compatibility with
- // applications that may have overridden the method.
- mActivity.startActivityFromFragment(this, intent, requestCode, options);
- }
+ mActivity.startActivityFromFragment(this, intent, requestCode, options);
}
/**
@@ -1119,6 +1114,98 @@
}
/**
+ * Requests permissions to be granted to this application. These permissions
+ * must be requested in your manifest, they should not be granted to your app,
+ * and they should have protection level {@link android.content.pm.PermissionInfo
+ * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
+ * the platform or a third-party app.
+ * <p>
+ * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
+ * are granted at install time if requested in the manifest. Signature permissions
+ * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
+ * install time if requested in the manifest and the signature of your app matches
+ * the signature of the app declaring the permissions.
+ * </p>
+ * <p>
+ * If your app does not have the requested permissions the user will be presented
+ * with UI for accepting them. After the user has accepted or rejected the
+ * requested permissions you will receive a callback on {@link
+ * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
+ * permissions were granted or not.
+ * </p>
+ * <p>
+ * Note that requesting a permission does not guarantee it will be granted and
+ * your app should be able to run without having this permission.
+ * </p>
+ * <p>
+ * This method may start an activity allowing the user to choose which permissions
+ * to grant and which to reject. Hence, you should be prepared that your activity
+ * may be paused and resumed. Further, granting some permissions may require
+ * a restart of you application. In such a case, the system will recreate the
+ * activity stack before delivering the result to {@link
+ * #onRequestPermissionsResult(int, String[], int[])}.
+ * </p>
+ * <p>
+ * When checking whether you have a permission you should use {@link
+ * android.content.Context#checkSelfPermission(String)}.
+ * </p>
+ * <p>
+ * A sample permissions request looks like this:
+ * </p>
+ * <code><pre><p>
+ * private void showContacts() {
+ * if (getActivity().checkSelfPermission(Manifest.permission.READ_CONTACTS)
+ * != PackageManager.PERMISSION_GRANTED) {
+ * requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
+ * PERMISSIONS_REQUEST_READ_CONTACTS);
+ * } else {
+ * doShowContacts();
+ * }
+ * }
+ *
+ * {@literal @}Override
+ * public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ * int[] grantResults) {
+ * if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS
+ * && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ * doShowContacts();
+ * }
+ * }
+ * </code></pre></p>
+ *
+ * @param permissions The requested permissions.
+ * @param requestCode Application specific request code to match with a result
+ * reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
+ *
+ * @see #onRequestPermissionsResult(int, String[], int[])
+ * @see android.content.Context#checkSelfPermission(String)
+ */
+ public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
+ if (mActivity == null) {
+ throw new IllegalStateException("Fragment " + this + " not attached to Activity");
+ }
+ Intent intent = mActivity.getPackageManager().buildRequestPermissionsIntent(permissions);
+ mActivity.startActivityFromFragment(this, intent, requestCode, null);
+ }
+
+ /**
+ * Callback for the result from requesting permissions. This method
+ * is invoked for every call on {@link #requestPermissions(String[], int)}.
+ *
+ * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
+ * @param permissions The requested permissions. Never null.
+ * @param grantResults The grant results for the corresponding permissions
+ * which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
+ * or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
+ *
+ * @see #requestPermissions(String[], int)
+ */
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ /* callback - do nothing */
+ }
+
+ /**
* @hide Hack so that DialogFragment can make its Dialog before creating
* its views, and the view construction can use the dialog's context for
* inflation. Maybe this should become a public API. Note sure.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0a0ce33..8c81133 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -341,6 +341,18 @@
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
/**
+ * An int extra holding a minimum required version code for the device admin package. If the
+ * device admin is already installed on the device, it will only be re-downloaded from
+ * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION} if the version of the
+ * installed package is less than this version code.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ */
+ public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE
+ = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE";
+
+ /**
* A String extra holding a http cookie header which should be used in the http request to the
* url specified in {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
*
@@ -411,6 +423,18 @@
= "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
/**
+ * An int extra holding a minimum required version code for the device initializer package.
+ * If the initializer is already installed on the device, it will only be re-downloaded from
+ * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION} if the version of
+ * the installed package is less than this version code.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ */
+ public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE
+ = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE";
+
+ /**
* A String extra holding a http cookie header which should be used in the http request to the
* url specified in {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}.
*
@@ -434,6 +458,50 @@
= "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
/**
+ * A String extra holding the MAC address of the Bluetooth device to connect to with status
+ * updates during provisioning.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ */
+ public static final String EXTRA_PROVISIONING_BT_MAC_ADDRESS
+ = "android.app.extra.PROVISIONING_BT_MAC_ADDRESS";
+
+ /**
+ * A String extra holding the Bluetooth service UUID on the device to connect to with status
+ * updates during provisioning.
+ *
+ * <p>This value must be specified when {@code #EXTRA_PROVISIONING_BT_MAC_ADDRESS} is present.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ */
+ public static final String EXTRA_PROVISIONING_BT_UUID
+ = "android.app.extra.PROVISIONING_BT_UUID";
+
+ /**
+ * A String extra holding a unique identifier used to identify the device connecting over
+ * Bluetooth. This identifier will be part of every status message sent to the remote device.
+ *
+ * <p>This value must be specified when {@code #EXTRA_PROVISIONING_BT_MAC_ADDRESS} is present.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ */
+ public static final String EXTRA_PROVISIONING_BT_DEVICE_ID
+ = "android.app.extra.PROVISIONING_BT_DEVICE_ID";
+
+ /**
+ * A Boolean extra that that will cause a provisioned device to temporarily proxy network
+ * traffic over Bluetooth. When a Wi-Fi network is available, the network proxy will stop.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+ * provisioning via an NFC bump.
+ */
+ public static final String EXTRA_PROVISIONING_BT_USE_PROXY
+ = "android.app.extra.PROVISIONING_BT_USE_PROXY";
+
+ /**
* This MIME type is used for starting the Device Owner provisioning.
*
* <p>During device owner provisioning a device admin app is set as the owner of the device.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 80b5e0b..39a70be 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3109,6 +3109,20 @@
public abstract int checkCallingOrSelfPermission(@NonNull String permission);
/**
+ * Determine whether <em>you</em> have been granted a particular permission.
+ *
+ * @param permission The name of the permission being checked.
+ *
+ * @return {@link PackageManager#PERMISSION_GRANTED} if you have the
+ * permission, or {@link PackageManager#PERMISSION_DENIED} if not.
+ *
+ * @see PackageManager#checkPermission(String, String)
+ * @see #checkCallingPermission(String)
+ */
+ @PackageManager.PermissionResult
+ public abstract int checkSelfPermission(@NonNull String permission);
+
+ /**
* If the given permission is not allowed for a particular process
* and user ID running in the system, throw a {@link SecurityException}.
*
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 6e8b7c1..8c5a87c 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -602,6 +602,11 @@
}
@Override
+ public int checkSelfPermission(String permission) {
+ return mBase.checkSelfPermission(permission);
+ }
+
+ @Override
public void enforcePermission(
String permission, int pid, int uid, String message) {
mBase.enforcePermission(permission, pid, uid, message);
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 29befc8..8f17845 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -346,6 +346,11 @@
public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27;
/**
+ * When set installer extracts native libs from .apk files.
+ */
+ public static final int FLAG_EXTRACT_NATIVE_LIBS = 1<<28;
+
+ /**
* Value for {@link #flags}: true if code from this application will need to be
* loaded into other applications' processes. On devices that support multiple
* instruction sets, this implies the code might be loaded into a process that's
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 3e5d362..c6d97f1 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -46,32 +46,34 @@
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
import android.net.Uri;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.content.IntentSender;
+import com.android.internal.os.IResultReceiver;
/**
* See {@link PackageManager} for documentation on most of the APIs
* here.
- *
+ *
* {@hide}
*/
interface IPackageManager {
boolean isPackageAvailable(String packageName, int userId);
PackageInfo getPackageInfo(String packageName, int flags, int userId);
int getPackageUid(String packageName, int userId);
- int[] getPackageGids(String packageName);
-
+ int[] getPackageGids(String packageName, int userId);
+
String[] currentToCanonicalPackageNames(in String[] names);
String[] canonicalToCurrentPackageNames(in String[] names);
PermissionInfo getPermissionInfo(String name, int flags);
-
+
List<PermissionInfo> queryPermissionsByGroup(String group, int flags);
-
+
PermissionGroupInfo getPermissionGroupInfo(String name, int flags);
-
+
List<PermissionGroupInfo> getAllPermissionGroups(int flags);
-
+
ApplicationInfo getApplicationInfo(String packageName, int flags ,int userId);
ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);
@@ -85,28 +87,28 @@
ProviderInfo getProviderInfo(in ComponentName className, int flags, int userId);
- int checkPermission(String permName, String pkgName);
-
+ int checkPermission(String permName, String pkgName, int userId);
+
int checkUidPermission(String permName, int uid);
-
+
boolean addPermission(in PermissionInfo info);
-
+
void removePermission(String name);
- void grantPermission(String packageName, String permissionName);
+ boolean grantPermission(String packageName, String permissionName, int userId);
- void revokePermission(String packageName, String permissionName);
+ boolean revokePermission(String packageName, String permissionName, int userId);
boolean isProtectedBroadcast(String actionName);
-
+
int checkSignatures(String pkg1, String pkg2);
-
+
int checkUidSignatures(int uid1, int uid2);
-
+
String[] getPackagesForUid(int uid);
-
+
String getNameForUid(int uid);
-
+
int getUidForSharedUser(String sharedUserName);
int getFlagsForUid(int uid);
@@ -121,7 +123,7 @@
boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId);
- List<ResolveInfo> queryIntentActivities(in Intent intent,
+ List<ResolveInfo> queryIntentActivities(in Intent intent,
String resolvedType, int flags, int userId);
List<ResolveInfo> queryIntentActivityOptions(
@@ -168,7 +170,7 @@
/**
* Retrieve all applications that are marked as persistent.
- *
+ *
* @return A List<applicationInfo> containing one entry for each persistent
* application.
*/
@@ -178,7 +180,7 @@
/**
* Retrieve sync information for all content providers.
- *
+ *
* @param outNames Filled in with a list of the root names of the content
* providers that can sync.
* @param outInfo Filled in with a list of the ProviderInfo for each
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 9223269..9e6c6b5 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -167,8 +167,7 @@
* or null if there were none. This is only filled in if the flag
* {@link PackageManager#GET_PERMISSIONS} was set. Each value matches
* the corresponding entry in {@link #requestedPermissions}, and will have
- * the flags {@link #REQUESTED_PERMISSION_REQUIRED} and
- * {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
+ * the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
*/
public int[] requestedPermissionsFlags;
@@ -176,6 +175,8 @@
* Flag for {@link #requestedPermissionsFlags}: the requested permission
* is required for the application to run; the user can not optionally
* disable it. Currently all permissions are required.
+ *
+ * @removed We do not support required permissions.
*/
public static final int REQUESTED_PERMISSION_REQUIRED = 1<<0;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3da57cb..59a16da 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -44,6 +44,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.AndroidException;
+import com.android.internal.util.ArrayUtils;
import java.io.File;
import java.lang.annotation.Retention;
@@ -377,6 +378,16 @@
public static final int INSTALL_ALLOW_DOWNGRADE = 0x00000080;
/**
+ * Flag parameter for {@link #installPackage} to indicate that all runtime
+ * permissions should be granted to the package. If {@link #INSTALL_ALL_USERS}
+ * is set the runtime permissions will be granted to all users, otherwise
+ * only to the owner.
+ *
+ * @hide
+ */
+ public static final int INSTALL_GRANT_RUNTIME_PERMISSIONS = 0x00000100;
+
+ /**
* Flag parameter for
* {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
* that you don't want to kill the app containing the component. Be careful when you set this
@@ -1663,21 +1674,46 @@
= "android.content.pm.extra.VERIFICATION_VERSION_CODE";
/**
- * The action used to request that the user approve a permission request
- * from the application.
+ * The action used to request that the user approve a grant permissions
+ * request from the application.
*
* @hide
*/
- public static final String ACTION_REQUEST_PERMISSION
- = "android.content.pm.action.REQUEST_PERMISSION";
+ @SystemApi
+ public static final String ACTION_REQUEST_PERMISSIONS =
+ "android.content.pm.action.REQUEST_PERMISSIONS";
/**
- * Extra field name for the list of permissions, which the user must approve.
+ * The component name handling runtime permission grants.
*
* @hide
*/
- public static final String EXTRA_REQUEST_PERMISSION_PERMISSION_LIST
- = "android.content.pm.extra.PERMISSION_LIST";
+ public static final String GRANT_PERMISSIONS_PACKAGE_NAME =
+ "com.android.packageinstaller";
+
+ /**
+ * The names of the requested permissions.
+ * <p>
+ * <strong>Type:</strong> String[]
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_REQUEST_PERMISSIONS_NAMES =
+ "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
+
+ /**
+ * The results from the permissions request.
+ * <p>
+ * <strong>Type:</strong> int[] of #PermissionResult
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS
+ = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
/**
* String extra for {@link PackageInstallObserver} in the 'extras' Bundle in case of
@@ -2184,53 +2220,72 @@
public abstract void removePermission(String name);
/**
- * Returns an {@link Intent} suitable for passing to {@code startActivityForResult}
- * which prompts the user to grant {@code permissions} to this application.
- * @hide
+ * Grant a runtime permission to an application which the application does not
+ * already have. The permission must have been requested by the application.
+ * If the application is not allowed to hold the permission, a {@link
+ * java.lang.SecurityException} is thrown.
+ * <p>
+ * <strong>Note: </strong>Using this API requires holding
+ * android.permission.GRANT_REVOKE_PERMISSIONS and if the user id is
+ * not the current user android.permission.INTERACT_ACROSS_USERS_FULL.
+ * </p>
*
- * @throws NullPointerException if {@code permissions} is {@code null}.
- * @throws IllegalArgumentException if {@code permissions} contains {@code null}.
+ * @param packageName The package to which to grant the permission.
+ * @param permissionName The permission name to grant.
+ * @param user The user for which to grant the permission.
+ *
+ * @see #revokePermission(String, String, android.os.UserHandle)
+ *
+ * @hide
*/
- public Intent buildPermissionRequestIntent(String... permissions) {
- if (permissions == null) {
- throw new NullPointerException("permissions cannot be null");
- }
- for (String permission : permissions) {
- if (permission == null) {
- throw new IllegalArgumentException("permissions cannot contain null");
- }
- }
+ @SystemApi
+ public abstract void grantPermission(@NonNull String packageName,
+ @NonNull String permissionName, @NonNull UserHandle user);
- Intent i = new Intent(ACTION_REQUEST_PERMISSION);
- i.putExtra(EXTRA_REQUEST_PERMISSION_PERMISSION_LIST, permissions);
- i.setPackage("com.android.packageinstaller");
- return i;
+ /**
+ * Revoke a runtime permission that was previously granted by {@link
+ * #grantPermission(String, String, android.os.UserHandle)}. The permission
+ * must have been requested by and granted to the application. If the
+ * application is not allowed to hold the permission, a {@link
+ * java.lang.SecurityException} is thrown.
+ * <p>
+ * <strong>Note: </strong>Using this API requires holding
+ * android.permission.GRANT_REVOKE_PERMISSIONS and if the user id is
+ * not the current user android.permission.INTERACT_ACROSS_USERS_FULL.
+ * </p>
+ *
+ * @param packageName The package from which to revoke the permission.
+ * @param permissionName The permission name to revoke.
+ * @param user The user for which to revoke the permission.
+ *
+ * @see #grantPermission(String, String, android.os.UserHandle)
+ *
+ * @hide
+ */
+ @SystemApi
+ public abstract void revokePermission(@NonNull String packageName,
+ @NonNull String permissionName, @NonNull UserHandle user);
+
+ /**
+ * Returns an {@link android.content.Intent} suitable for passing to
+ * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
+ * which prompts the user to grant permissions to this application.
+ *
+ * @throws NullPointerException if {@code permissions} is {@code null} or empty.
+ *
+ * @hide
+ */
+ public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
+ if (ArrayUtils.isEmpty(permissions)) {
+ throw new NullPointerException("permission cannot be null or empty");
+ }
+ Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
+ intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
+ intent.setPackage(GRANT_PERMISSIONS_PACKAGE_NAME);
+ return intent;
}
/**
- * Grant a permission to an application which the application does not
- * already have. The permission must have been requested by the application,
- * but as an optional permission. If the application is not allowed to
- * hold the permission, a SecurityException is thrown.
- * @hide
- *
- * @param packageName The name of the package that the permission will be
- * granted to.
- * @param permissionName The name of the permission.
- */
- public abstract void grantPermission(String packageName, String permissionName);
-
- /**
- * Revoke a permission that was previously granted by {@link #grantPermission}.
- * @hide
- *
- * @param packageName The name of the package that the permission will be
- * granted to.
- * @param permissionName The name of the permission.
- */
- public abstract void revokePermission(String packageName, String permissionName);
-
- /**
* Compare the signatures of two packages to determine if the same
* signature appears in both of them. If they do contain the same
* signature, then they are allowed special privileges when working
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c443ff3..e5859d03 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -269,6 +269,7 @@
public final boolean coreApp;
public final boolean multiArch;
+ public final boolean extractNativeLibs;
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
String[] splitCodePaths, int[] splitRevisionCodes) {
@@ -284,6 +285,7 @@
this.splitRevisionCodes = splitRevisionCodes;
this.coreApp = baseApk.coreApp;
this.multiArch = baseApk.multiArch;
+ this.extractNativeLibs = baseApk.extractNativeLibs;
}
public List<String> getAllCodePaths() {
@@ -310,10 +312,12 @@
public final Signature[] signatures;
public final boolean coreApp;
public final boolean multiArch;
+ public final boolean extractNativeLibs;
public ApkLite(String codePath, String packageName, String splitName, int versionCode,
int revisionCode, int installLocation, List<VerifierInfo> verifiers,
- Signature[] signatures, boolean coreApp, boolean multiArch) {
+ Signature[] signatures, boolean coreApp, boolean multiArch,
+ boolean extractNativeLibs) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -324,6 +328,7 @@
this.signatures = signatures;
this.coreApp = coreApp;
this.multiArch = multiArch;
+ this.extractNativeLibs = extractNativeLibs;
}
}
@@ -410,7 +415,7 @@
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- ArraySet<String> grantedPermissions, PackageUserState state, int userId) {
+ Set<String> grantedPermissions, PackageUserState state, int userId) {
if (!checkUseInstalledOrHidden(flags, state)) {
return null;
@@ -569,9 +574,8 @@
for (int i=0; i<N; i++) {
final String perm = p.requestedPermissions.get(i);
pi.requestedPermissions[i] = perm;
- if (p.requestedPermissionsRequired.get(i)) {
- pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
- }
+ // The notion of requried permissions is deprecated but for compatibility.
+ pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
if (grantedPermissions != null && grantedPermissions.contains(perm)) {
pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
}
@@ -1270,6 +1274,7 @@
int revisionCode = 0;
boolean coreApp = false;
boolean multiArch = false;
+ boolean extractNativeLibs = true;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
final String attr = attrs.getAttributeName(i);
@@ -1308,14 +1313,17 @@
final String attr = attrs.getAttributeName(i);
if ("multiArch".equals(attr)) {
multiArch = attrs.getAttributeBooleanValue(i, false);
- break;
+ }
+ if ("extractNativeLibs".equals(attr)) {
+ extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
}
}
}
}
return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
- revisionCode, installLocation, verifiers, signatures, coreApp, multiArch);
+ revisionCode, installLocation, verifiers, signatures, coreApp, multiArch,
+ extractNativeLibs);
}
/**
@@ -1812,7 +1820,6 @@
}
implicitPerms.append(npi.name);
pkg.requestedPermissions.add(npi.name);
- pkg.requestedPermissionsRequired.add(Boolean.TRUE);
}
}
if (implicitPerms != null) {
@@ -1831,7 +1838,6 @@
final String perm = spi.newPerms[in];
if (!pkg.requestedPermissions.contains(perm)) {
pkg.requestedPermissions.add(perm);
- pkg.requestedPermissionsRequired.add(Boolean.TRUE);
}
}
}
@@ -1865,17 +1871,6 @@
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
}
- /*
- * b/8528162: Ignore the <uses-permission android:required> attribute if
- * targetSdkVersion < JELLY_BEAN_MR2. There are lots of apps in the wild
- * which are improperly using this attribute, even though it never worked.
- */
- if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) {
- for (int i = 0; i < pkg.requestedPermissionsRequired.size(); i++) {
- pkg.requestedPermissionsRequired.set(i, Boolean.TRUE);
- }
- }
-
return pkg;
}
@@ -1911,11 +1906,6 @@
// that may change.
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
-/*
- boolean required = sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
-*/
- boolean required = true; // Optional <uses-permission> not supported
int maxSdkVersion = 0;
TypedValue val = sa.peekValue(
@@ -1933,13 +1923,9 @@
int index = pkg.requestedPermissions.indexOf(name);
if (index == -1) {
pkg.requestedPermissions.add(name.intern());
- pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE);
} else {
- if (pkg.requestedPermissionsRequired.get(index) != required) {
- outError[0] = "conflicting <uses-permission> entries";
- mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
- return false;
- }
+ Slog.w(TAG, "Ignoring duplicate uses-permission: " + name + " in package: "
+ + pkg.packageName + " at: " + parser.getPositionDescription());
}
}
}
@@ -2569,6 +2555,12 @@
ai.flags |= ApplicationInfo.FLAG_MULTIARCH;
}
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs,
+ true)) {
+ ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
+ }
+
String str;
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
@@ -3116,8 +3108,7 @@
}
a.info.resizeable = sa.getBoolean(
- R.styleable.AndroidManifestActivity_resizeableActivity,
- owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.MNC);
+ R.styleable.AndroidManifestActivity_resizeableActivity, false);
if (a.info.resizeable) {
// Fixed screen orientation isn't supported with resizeable activities.
a.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -4218,7 +4209,6 @@
public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
- public final ArrayList<Boolean> requestedPermissionsRequired = new ArrayList<Boolean>();
public ArrayList<String> protectedBroadcasts;
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 841b09d..7d8dff3 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -464,6 +464,33 @@
return mColors;
}
+ /**
+ * Returns whether the specified state is referenced in any of the state
+ * specs contained within this ColorStateList.
+ * <p>
+ * Any reference, either positive or negative {ex. ~R.attr.state_enabled},
+ * will cause this method to return {@code true}. Wildcards are not counted
+ * as references.
+ *
+ * @param state the state to search for
+ * @return {@code true} if the state if referenced, {@code false} otherwise
+ * @hide Use only as directed. For internal use only.
+ */
+ public boolean hasState(int state) {
+ final int[][] stateSpecs = mStateSpecs;
+ final int specCount = stateSpecs.length;
+ for (int specIndex = 0; specIndex < specCount; specIndex++) {
+ final int[] states = stateSpecs[specIndex];
+ final int stateCount = states.length;
+ for (int stateIndex = 0; stateIndex < stateCount; stateIndex++) {
+ if (states[stateIndex] == state || states[stateIndex] == ~state) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
@Override
public String toString() {
return "ColorStateList{" +
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cab03da..26e6b850 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -613,6 +613,9 @@
if ((initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
out.append('p');
}
+ if ((initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0) {
+ out.append('i');
+ }
switch ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
case Display.STATE_OFF: out.append('F'); break;
case Display.STATE_ON: out.append('O'); break;
@@ -622,6 +625,9 @@
if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
out.append('P');
}
+ if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0) {
+ out.append('I');
+ }
out.append('-');
appendHex(level, 4, out);
out.append('-');
@@ -648,6 +654,9 @@
case 'p': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE)
<< STEP_LEVEL_INITIAL_MODE_SHIFT);
break;
+ case 'i': out |= (((long)STEP_LEVEL_MODE_DEVICE_IDLE)
+ << STEP_LEVEL_INITIAL_MODE_SHIFT);
+ break;
case 'F': out |= (((long)Display.STATE_OFF-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
break;
case 'O': out |= (((long)Display.STATE_ON-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
@@ -660,6 +669,9 @@
case 'P': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE)
<< STEP_LEVEL_MODIFIED_MODE_SHIFT);
break;
+ case 'I': out |= (((long)STEP_LEVEL_MODE_DEVICE_IDLE)
+ << STEP_LEVEL_MODIFIED_MODE_SHIFT);
+ break;
}
}
i++;
@@ -820,11 +832,18 @@
}
}
+ public static final class PackageChange {
+ public String mPackageName;
+ public boolean mUpdate;
+ public int mVersionCode;
+ }
+
public static final class DailyItem {
public long mStartTime;
public long mEndTime;
public LevelStepTracker mDischargeSteps;
public LevelStepTracker mChargeSteps;
+ public ArrayList<PackageChange> mPackageChanges;
}
public abstract DailyItem getDailyItemLocked(int daysAgo);
@@ -1524,6 +1543,23 @@
public abstract int getDeviceIdleModeEnabledCount(int which);
/**
+ * Returns the time in microseconds that device has been in idling while on
+ * battery. This is broader than {@link #getDeviceIdleModeEnabledTime} -- it
+ * counts all of the time that we consider the device to be idle, whether or not
+ * it is currently in the actual device idle mode.
+ *
+ * {@hide}
+ */
+ public abstract long getDeviceIdlingTime(long elapsedRealtimeUs, int which);
+
+ /**
+ * Returns the number of times that the devie has started idling.
+ *
+ * {@hide}
+ */
+ public abstract int getDeviceIdlingCount(int which);
+
+ /**
* Returns the number of times that connectivity state changed.
*
* {@hide}
@@ -2069,45 +2105,44 @@
// Step duration mode: power save is on.
public static final int STEP_LEVEL_MODE_POWER_SAVE = 0x04;
+ // Step duration mode: device is currently in idle mode.
+ public static final int STEP_LEVEL_MODE_DEVICE_IDLE = 0x08;
+
public static final int[] STEP_LEVEL_MODES_OF_INTEREST = new int[] {
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE|STEP_LEVEL_MODE_DEVICE_IDLE,
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_DEVICE_IDLE,
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
- STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
- STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE|STEP_LEVEL_MODE_DEVICE_IDLE,
+ STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_DEVICE_IDLE,
};
public static final int[] STEP_LEVEL_MODE_VALUES = new int[] {
(Display.STATE_OFF-1),
(Display.STATE_OFF-1)|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_OFF-1)|STEP_LEVEL_MODE_DEVICE_IDLE,
(Display.STATE_ON-1),
(Display.STATE_ON-1)|STEP_LEVEL_MODE_POWER_SAVE,
(Display.STATE_DOZE-1),
(Display.STATE_DOZE-1)|STEP_LEVEL_MODE_POWER_SAVE,
(Display.STATE_DOZE_SUSPEND-1),
(Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_POWER_SAVE,
+ (Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_DEVICE_IDLE,
};
public static final String[] STEP_LEVEL_MODE_LABELS = new String[] {
"screen off",
"screen off power save",
+ "screen off device idle",
"screen on",
"screen on power save",
"screen doze",
"screen doze power save",
"screen doze-suspend",
"screen doze-suspend power save",
- };
- public static final String[] STEP_LEVEL_MODE_TAGS = new String[] {
- "off",
- "off-save",
- "on",
- "on-save",
- "doze",
- "doze-save",
- "susp",
- "susp-save",
+ "screen doze-suspend device idle",
};
/**
@@ -2140,6 +2175,8 @@
*/
public abstract LevelStepTracker getDailyChargeLevelStepTracker();
+ public abstract ArrayList<PackageChange> getDailyPackageChanges();
+
public abstract Map<String, ? extends Timer> getWakeupReasonStats();
public abstract Map<String, ? extends Timer> getKernelWakelockStats();
@@ -2338,6 +2375,7 @@
final long interactiveTime = getInteractiveTime(rawRealtime, which);
final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which);
+ final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which);
final int connChanges = getNumConnectivityChange(which);
final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
final long wifiOnTime = getWifiOnTime(rawRealtime, which);
@@ -2410,7 +2448,8 @@
0 /*legacy input event count*/, getMobileRadioActiveTime(rawRealtime, which) / 1000,
getMobileRadioActiveAdjustedTime(which) / 1000, interactiveTime / 1000,
powerSaveModeEnabledTime / 1000, connChanges, deviceIdleModeEnabledTime / 1000,
- getDeviceIdleModeEnabledCount(which));
+ getDeviceIdleModeEnabledCount(which), deviceIdlingTime / 1000,
+ getDeviceIdlingCount(which));
// Dump screen brightness stats
Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -2879,6 +2918,7 @@
final long interactiveTime = getInteractiveTime(rawRealtime, which);
final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which);
+ final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which);
final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
final long wifiOnTime = getWifiOnTime(rawRealtime, which);
@@ -2923,10 +2963,21 @@
sb.append(")");
pw.println(sb.toString());
}
- if (deviceIdleModeEnabledTime != 0) {
+ if (deviceIdlingTime != 0) {
sb.setLength(0);
sb.append(prefix);
sb.append(" Device idling: ");
+ formatTimeMs(sb, deviceIdlingTime / 1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(deviceIdlingTime, whichBatteryRealtime));
+ sb.append(") "); sb.append(getDeviceIdlingCount(which));
+ sb.append("x");
+ pw.println(sb.toString());
+ }
+ if (deviceIdleModeEnabledTime != 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Idle mode time: ");
formatTimeMs(sb, deviceIdleModeEnabledTime / 1000);
sb.append("(");
sb.append(formatRatioLocked(deviceIdleModeEnabledTime, whichBatteryRealtime));
@@ -4403,6 +4454,11 @@
} else {
lineArgs[3] = "";
}
+ if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+ lineArgs[3] = (initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0 ? "i+" : "i-";
+ } else {
+ lineArgs[3] = "";
+ }
dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs);
} else {
pw.print(prefix);
@@ -4427,6 +4483,12 @@
? "power-save-on" : "power-save-off");
haveModes = true;
}
+ if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+ pw.print(haveModes ? ", " : " (");
+ pw.print((initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0
+ ? "device-idle-on" : "device-idle-off");
+ haveModes = true;
+ }
if (haveModes) {
pw.print(")");
}
@@ -4558,6 +4620,23 @@
}
}
+ private void dumpDailyPackageChanges(PrintWriter pw, String prefix,
+ ArrayList<PackageChange> changes) {
+ if (changes == null) {
+ return;
+ }
+ pw.print(prefix); pw.println("Package changes:");
+ for (int i=0; i<changes.size(); i++) {
+ PackageChange pc = changes.get(i);
+ if (pc.mUpdate) {
+ pw.print(prefix); pw.print(" Update "); pw.print(pc.mPackageName);
+ pw.print(" vers="); pw.println(pc.mVersionCode);
+ } else {
+ pw.print(prefix); pw.print(" Uninstall "); pw.println(pc.mPackageName);
+ }
+ }
+ }
+
/**
* Dumps a human-readable summary of the battery statistics to the given PrintWriter.
*
@@ -4688,7 +4767,8 @@
int[] outInt = new int[1];
LevelStepTracker dsteps = getDailyDischargeLevelStepTracker();
LevelStepTracker csteps = getDailyChargeLevelStepTracker();
- if (dsteps.mNumStepDurations > 0 || csteps.mNumStepDurations > 0) {
+ ArrayList<PackageChange> pkgc = getDailyPackageChanges();
+ if (dsteps.mNumStepDurations > 0 || csteps.mNumStepDurations > 0 || pkgc != null) {
if ((flags&DUMP_DAILY_ONLY) != 0) {
if (dumpDurationSteps(pw, " ", " Current daily discharge step durations:",
dsteps, false)) {
@@ -4700,6 +4780,7 @@
dumpDailyLevelStepSummary(pw, " ", "Charge", csteps,
sb, outInt);
}
+ dumpDailyPackageChanges(pw, " ", pkgc);
} else {
pw.println(" Current daily steps:");
dumpDailyLevelStepSummary(pw, " ", "Discharge", dsteps,
@@ -4731,6 +4812,7 @@
dumpDailyLevelStepSummary(pw, " ", "Charge", dit.mChargeSteps,
sb, outInt);
}
+ dumpDailyPackageChanges(pw, " ", dit.mPackageChanges);
} else {
dumpDailyLevelStepSummary(pw, " ", "Discharge", dit.mDischargeSteps,
sb, outInt);
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 16dac7d..418641f 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -43,6 +43,7 @@
boolean isInteractive();
boolean isPowerSaveMode();
boolean setPowerSaveMode(boolean mode);
+ boolean isDeviceIdleMode();
void reboot(boolean confirm, String reason, boolean wait);
void shutdown(boolean confirm, boolean wait);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index de970cb..81745b3 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -856,6 +856,23 @@
}
/**
+ * Returns true if the device is currently in idle mode. This happens when a device
+ * has been sitting unused and unmoving for a sufficiently long period of time, so that
+ * it decides to go into a lower power-use state. This may involve things like turning
+ * off network access to apps. You can monitor for changes to this state with
+ * {@link #ACTION_DEVICE_IDLE_MODE_CHANGED}.
+ *
+ * @return Returns true if currently in low power mode, else false.
+ */
+ public boolean isDeviceIdleMode() {
+ try {
+ return mService.isDeviceIdleMode();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Turn off the device.
*
* @param confirm If true, shows a shutdown confirmation dialog.
@@ -879,6 +896,14 @@
= "android.os.action.POWER_SAVE_MODE_CHANGED";
/**
+ * Intent that is broadcast when the state of {@link #isDeviceIdleMode()} changes.
+ * This broadcast is only sent to registered receivers.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DEVICE_IDLE_MODE_CHANGED
+ = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
+
+ /**
* Intent that is broadcast when the state of {@link #isPowerSaveMode()} is about to change.
* This broadcast is only sent to registered receivers.
*
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 6f31768..00ab262 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -117,7 +117,7 @@
/**
* Used by the dream manager to override certain properties while dozing.
*
- * @param screenState The overridden screen state, or {@link Display.STATE_UNKNOWN}
+ * @param screenState The overridden screen state, or {@link Display#STATE_UNKNOWN}
* to disable the override.
* @param screenBrightness The overridden screen brightness, or
* {@link PowerManager#BRIGHTNESS_DEFAULT} to disable the override.
@@ -132,4 +132,6 @@
public interface LowPowerModeListener {
public void onLowPowerModeChanged(boolean enabled);
}
+
+ public abstract void setDeviceIdleMode(boolean enabled);
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 06862d7..74b0a1c 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1516,8 +1516,14 @@
/**
* Build a {@link #CONTENT_LOOKUP_URI} lookup {@link Uri} using the
* given {@link ContactsContract.Contacts#_ID} and {@link #LOOKUP_KEY}.
+ * <p>
+ * Returns null if unable to construct a valid lookup URI from the
+ * provided parameters.
*/
public static Uri getLookupUri(long contactId, String lookupKey) {
+ if (TextUtils.isEmpty(lookupKey)) {
+ return null;
+ }
return ContentUris.withAppendedId(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
lookupKey), contactId);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e6b39b5..de536bd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5375,6 +5375,7 @@
BACKUP_AUTO_RESTORE,
ENABLED_ACCESSIBILITY_SERVICES,
ENABLED_NOTIFICATION_LISTENERS,
+ ENABLED_INPUT_METHODS,
TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
TOUCH_EXPLORATION_ENABLED,
ACCESSIBILITY_ENABLED,
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 4902a71..d46b6f5 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -17,15 +17,12 @@
package android.service.wallpaper;
import android.content.res.TypedArray;
-import android.os.Build;
import android.os.SystemProperties;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.ViewRootImpl;
import android.view.WindowInsets;
import com.android.internal.R;
import com.android.internal.os.HandlerCaller;
+import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.BaseIWindow;
import com.android.internal.view.BaseSurfaceHolder;
@@ -158,7 +155,7 @@
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
int mCurWindowFlags = mWindowFlags;
int mCurWindowPrivateFlags = mWindowPrivateFlags;
- TypedValue mOutsetBottom;
+ int mOutsetBottomPx;
final Rect mVisibleInsets = new Rect();
final Rect mWinFrame = new Rect();
final Rect mOverscanInsets = new Rect();
@@ -171,8 +168,6 @@
final Rect mFinalStableInsets = new Rect();
final Configuration mConfiguration = new Configuration();
- private boolean mIsEmulator;
- private boolean mIsCircularEmulator;
private boolean mWindowIsRound;
final WindowManager.LayoutParams mLayout
@@ -637,23 +632,13 @@
final Display display = windowService.getDefaultDisplay();
final boolean shouldUseBottomOutset =
display.getDisplayId() == Display.DEFAULT_DISPLAY;
- if (shouldUseBottomOutset && windowStyle.hasValue(
- R.styleable.Window_windowOutsetBottom)) {
- if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
- windowStyle.getValue(R.styleable.Window_windowOutsetBottom,
- mOutsetBottom);
- } else {
- mOutsetBottom = null;
+ if (shouldUseBottomOutset) {
+ mOutsetBottomPx = ScreenShapeHelper.getWindowOutsetBottomPx(
+ getResources().getDisplayMetrics(), windowStyle);
}
- mWindowIsRound = getResources().getBoolean(
- com.android.internal.R.bool.config_windowIsRound);
+ mWindowIsRound = ScreenShapeHelper.getWindowIsRound(getResources());
windowStyle.recycle();
- // detect emulator
- mIsEmulator = Build.HARDWARE.contains("goldfish");
- mIsCircularEmulator = SystemProperties.getBoolean(
- ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false);
-
// Add window
mLayout.type = mIWallpaperEngine.mWindowType;
mLayout.gravity = Gravity.START|Gravity.TOP;
@@ -783,18 +768,14 @@
mDispatchedOverscanInsets.set(mOverscanInsets);
mDispatchedContentInsets.set(mContentInsets);
mDispatchedStableInsets.set(mStableInsets);
- final boolean isRound = (mIsEmulator && mIsCircularEmulator)
- || mWindowIsRound;
mFinalSystemInsets.set(mDispatchedOverscanInsets);
mFinalStableInsets.set(mDispatchedStableInsets);
- if (mOutsetBottom != null) {
- final DisplayMetrics metrics = getResources().getDisplayMetrics();
+ if (mOutsetBottomPx != 0) {
mFinalSystemInsets.bottom =
- ( (int) mOutsetBottom.getDimension(metrics) )
- + mIWallpaperEngine.mDisplayPadding.bottom;
+ mIWallpaperEngine.mDisplayPadding.bottom + mOutsetBottomPx;
}
WindowInsets insets = new WindowInsets(mFinalSystemInsets,
- null, mFinalStableInsets, isRound);
+ null, mFinalStableInsets, mWindowIsRound);
onApplyWindowInsets(insets);
}
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 1a07aee..457d6ad 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -452,9 +452,10 @@
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
+ final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
- Context lastContext = (Context)mConstructorArgs[0];
- mConstructorArgs[0] = mContext;
+ Context lastContext = (Context) mConstructorArgs[0];
+ mConstructorArgs[0] = inflaterContext;
View result = root;
try {
@@ -485,10 +486,10 @@
+ "ViewGroup root and attachToRoot=true");
}
- rInflate(parser, root, attrs, false, false);
+ rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
- final View temp = createViewFromTag(root, name, attrs, false);
+ final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
@@ -509,8 +510,10 @@
if (DEBUG) {
System.out.println("-----> start inflating children");
}
- // Inflate all children under temp
- rInflate(parser, temp, attrs, true, true);
+
+ // Inflate all children under temp against its context.
+ rInflateChildren(parser, temp, attrs, true);
+
if (DEBUG) {
System.out.println("-----> done inflating children");
}
@@ -692,59 +695,68 @@
}
/**
+ * Convenience method for calling through to the five-arg createViewFromTag
+ * method. This method passes {@code false} for the {@code ignoreThemeAttr}
+ * argument and should be used for everything except {@code >include>}
+ * tag parsing.
+ */
+ private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
+ return createViewFromTag(parent, name, context, attrs, false);
+ }
+
+ /**
* Creates a view from a tag name using the supplied attribute set.
* <p>
- * If {@code inheritContext} is true and the parent is non-null, the view
- * will be inflated in parent view's context. If the view specifies a
- * <theme> attribute, the inflation context will be wrapped with the
- * specified theme.
- * <p>
- * Note: Default visibility so the BridgeInflater can override it.
+ * <strong>Note:</strong> Default visibility so the BridgeInflater can
+ * override it.
+ *
+ * @param parent the parent view, used to inflate layout params
+ * @param name the name of the XML tag used to define the view
+ * @param context the inflation context for the view, typically the
+ * {@code parent} or base layout inflater context
+ * @param attrs the attribute set for the XML tag used to define the view
+ * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme}
+ * attribute (if set) for the view being inflated,
+ * {@code false} otherwise
*/
- View createViewFromTag(View parent, String name, AttributeSet attrs, boolean inheritContext) {
+ View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
+ boolean ignoreThemeAttr) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
- Context viewContext;
- if (parent != null && inheritContext) {
- viewContext = parent.getContext();
- } else {
- viewContext = mContext;
+ // Apply a theme wrapper, if allowed and one is specified.
+ if (!ignoreThemeAttr) {
+ final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
+ final int themeResId = ta.getResourceId(0, 0);
+ if (themeResId != 0) {
+ context = new ContextThemeWrapper(context, themeResId);
+ }
+ ta.recycle();
}
- // Apply a theme wrapper, if requested.
- final TypedArray ta = viewContext.obtainStyledAttributes(attrs, ATTRS_THEME);
- final int themeResId = ta.getResourceId(0, 0);
- if (themeResId != 0) {
- viewContext = new ContextThemeWrapper(viewContext, themeResId);
- }
- ta.recycle();
-
if (name.equals(TAG_1995)) {
// Let's party like it's 1995!
- return new BlinkLayout(viewContext, attrs);
+ return new BlinkLayout(context, attrs);
}
- if (DEBUG) System.out.println("******** Creating view: " + name);
-
try {
View view;
if (mFactory2 != null) {
- view = mFactory2.onCreateView(parent, name, viewContext, attrs);
+ view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
- view = mFactory.onCreateView(name, viewContext, attrs);
+ view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {
- view = mPrivateFactory.onCreateView(parent, name, viewContext, attrs);
+ view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
- mConstructorArgs[0] = viewContext;
+ mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
@@ -756,20 +768,18 @@
}
}
- if (DEBUG) System.out.println("Created view is: " + view);
return view;
-
} catch (InflateException e) {
throw e;
} catch (ClassNotFoundException e) {
- InflateException ie = new InflateException(attrs.getPositionDescription()
+ final InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name);
ie.initCause(e);
throw ie;
} catch (Exception e) {
- InflateException ie = new InflateException(attrs.getPositionDescription()
+ final InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name);
ie.initCause(e);
throw ie;
@@ -777,16 +787,26 @@
}
/**
+ * Recursive method used to inflate internal (non-root) children. This
+ * method calls through to {@link #rInflate} using the parent context as
+ * the inflation context.
+ * <strong>Note:</strong> Default visibility so the BridgeInflater can
+ * call it.
+ */
+ final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
+ boolean finishInflate) throws XmlPullParserException, IOException {
+ rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
+ }
+
+ /**
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
- *
- * @param inheritContext Whether the root view should be inflated in its
- * parent's context. This should be true when called inflating
- * child views recursively, or false otherwise.
+ * <p>
+ * <strong>Note:</strong> Default visibility so the BridgeInflater can
+ * override it.
*/
- void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
- boolean finishInflate, boolean inheritContext) throws XmlPullParserException,
- IOException {
+ void rInflate(XmlPullParser parser, View parent, Context context,
+ AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
@@ -808,19 +828,21 @@
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
- parseInclude(parser, parent, attrs, inheritContext);
+ parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
- final View view = createViewFromTag(parent, name, attrs, inheritContext);
+ final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
- rInflate(parser, view, attrs, true, true);
+ rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
- if (finishInflate) parent.onFinishInflate();
+ if (finishInflate) {
+ parent.onFinishInflate();
+ }
}
/**
@@ -829,13 +851,9 @@
*/
private void parseRequestFocus(XmlPullParser parser, View view)
throws XmlPullParserException, IOException {
- int type;
view.requestFocus();
- final int currentDepth = parser.getDepth();
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
- // Empty
- }
+
+ consumeChildElements(parser);
}
/**
@@ -844,33 +862,29 @@
*/
private void parseViewTag(XmlPullParser parser, View view, AttributeSet attrs)
throws XmlPullParserException, IOException {
- int type;
-
- final TypedArray ta = view.getContext().obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.ViewTag);
- final int key = ta.getResourceId(com.android.internal.R.styleable.ViewTag_id, 0);
- final CharSequence value = ta.getText(com.android.internal.R.styleable.ViewTag_value);
+ final Context context = view.getContext();
+ final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewTag);
+ final int key = ta.getResourceId(R.styleable.ViewTag_id, 0);
+ final CharSequence value = ta.getText(R.styleable.ViewTag_value);
view.setTag(key, value);
ta.recycle();
- final int currentDepth = parser.getDepth();
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
- // Empty
- }
+ consumeChildElements(parser);
}
- private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs,
- boolean inheritContext) throws XmlPullParserException, IOException {
+ private void parseInclude(XmlPullParser parser, Context context, View parent,
+ AttributeSet attrs) throws XmlPullParserException, IOException {
int type;
if (parent instanceof ViewGroup) {
- Context context = inheritContext ? parent.getContext() : mContext;
-
- // Apply a theme wrapper, if requested.
+ // Apply a theme wrapper, if requested. This is sort of a weird
+ // edge case, since developers think the <include> overwrites
+ // values in the AttributeSet of the included View. So, if the
+ // included View has a theme attribute, we'll need to ignore it.
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
final int themeResId = ta.getResourceId(0, 0);
- if (themeResId != 0) {
+ final boolean hasThemeOverride = themeResId != 0;
+ if (hasThemeOverride) {
context = new ContextThemeWrapper(context, themeResId);
}
ta.recycle();
@@ -880,11 +894,12 @@
int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
if (layout == 0) {
final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
- if (value == null || value.length() < 1) {
+ if (value == null || value.length() <= 0) {
throw new InflateException("You must specify a layout in the"
+ " include tag: <include layout=\"@layout/layoutID\" />");
}
+ // Attempt to resolve the "?attr/name" string to an identifier.
layout = context.getResources().getIdentifier(value.substring(1), null, null);
}
@@ -901,8 +916,7 @@
throw new InflateException("You must specify a valid layout "
+ "reference. The layout ID " + value + " is not valid.");
} else {
- final XmlResourceParser childParser =
- getContext().getResources().getLayout(layout);
+ final XmlResourceParser childParser = context.getResources().getLayout(layout);
try {
final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
@@ -920,11 +934,12 @@
final String childName = childParser.getName();
if (TAG_MERGE.equals(childName)) {
- // Inflate all children.
- rInflate(childParser, parent, childAttrs, false, inheritContext);
+ // The <merge> tag doesn't support android:theme, so
+ // nothing special to do here.
+ rInflate(childParser, parent, context, childAttrs, false);
} else {
- final View view = createViewFromTag(parent, childName, childAttrs,
- inheritContext);
+ final View view = createViewFromTag(parent, childName,
+ context, childAttrs, hasThemeOverride);
final ViewGroup group = (ViewGroup) parent;
final TypedArray a = context.obtainStyledAttributes(
@@ -957,7 +972,7 @@
view.setLayoutParams(params);
// Inflate all children.
- rInflate(childParser, view, childAttrs, true, true);
+ rInflateChildren(childParser, view, childAttrs, true);
if (id != View.NO_ID) {
view.setId(id);
@@ -985,6 +1000,16 @@
throw new InflateException("<include /> can only be used inside of a ViewGroup");
}
+ LayoutInflater.consumeChildElements(parser);
+ }
+
+ /**
+ * <strong>Note:</strong> default visibility so that
+ * LayoutInflater_Delegate can call it.
+ */
+ final static void consumeChildElements(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type;
final int currentDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
diff --git a/core/java/android/view/PhoneFallbackEventHandler.java b/core/java/android/view/PhoneFallbackEventHandler.java
index fbf5732..350650d 100644
--- a/core/java/android/view/PhoneFallbackEventHandler.java
+++ b/core/java/android/view/PhoneFallbackEventHandler.java
@@ -25,8 +25,13 @@
import android.media.AudioManager;
import android.media.session.MediaSessionLegacyHelper;
import android.os.UserHandle;
+import android.provider.Settings;
import android.telephony.TelephonyManager;
-import android.util.Slog;
+import android.util.Log;
+import android.view.View;
+import android.view.HapticFeedbackConstants;
+import android.view.FallbackEventHandler;
+import android.view.KeyEvent;
/**
* @hide
@@ -112,15 +117,20 @@
dispatcher.startTracking(event, this);
} else if (event.isLongPress() && dispatcher.isTracking(event)) {
dispatcher.performedLongPress(event);
- mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- // launch the VoiceDialer
- Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- sendCloseSystemWindows();
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- startCallActivity();
+ if (isUserSetupComplete()) {
+ mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ // launch the VoiceDialer
+ Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ sendCloseSystemWindows();
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ startCallActivity();
+ }
+ } else {
+ Log.i(TAG, "Not starting call activity because user "
+ + "setup is in progress.");
}
}
return true;
@@ -134,13 +144,18 @@
dispatcher.startTracking(event, this);
} else if (event.isLongPress() && dispatcher.isTracking(event)) {
dispatcher.performedLongPress(event);
- mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- sendCloseSystemWindows();
- // Broadcast an intent that the Camera button was longpressed
- Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
- intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
- mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF,
- null, null, null, 0, null, null);
+ if (isUserSetupComplete()) {
+ mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ sendCloseSystemWindows();
+ // Broadcast an intent that the Camera button was longpressed
+ Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
+ intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF,
+ null, null, null, 0, null, null);
+ } else {
+ Log.i(TAG, "Not dispatching CAMERA long press because user "
+ + "setup is in progress.");
+ }
}
return true;
}
@@ -155,21 +170,26 @@
Configuration config = mContext.getResources().getConfiguration();
if (config.keyboard == Configuration.KEYBOARD_NOKEYS
|| config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
- // launch the search activity
- Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- sendCloseSystemWindows();
- getSearchManager().stopSearch();
- mContext.startActivity(intent);
- // Only clear this if we successfully start the
- // activity; otherwise we will allow the normal short
- // press action to be performed.
- dispatcher.performedLongPress(event);
- return true;
- } catch (ActivityNotFoundException e) {
- // Ignore
+ if (isUserSetupComplete()) {
+ // launch the search activity
+ Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ sendCloseSystemWindows();
+ getSearchManager().stopSearch();
+ mContext.startActivity(intent);
+ // Only clear this if we successfully start the
+ // activity; otherwise we will allow the normal short
+ // press action to be performed.
+ dispatcher.performedLongPress(event);
+ return true;
+ } catch (ActivityNotFoundException e) {
+ // Ignore
+ }
+ } else {
+ Log.i(TAG, "Not dispatching SEARCH long press because user "
+ + "setup is in progress.");
}
}
}
@@ -181,7 +201,7 @@
boolean onKeyUp(int keyCode, KeyEvent event) {
if (DEBUG) {
- Slog.d(TAG, "up " + keyCode);
+ Log.d(TAG, "up " + keyCode);
}
final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();
if (dispatcher != null) {
@@ -229,7 +249,12 @@
break;
}
if (event.isTracking() && !event.isCanceled()) {
- startCallActivity();
+ if (isUserSetupComplete()) {
+ startCallActivity();
+ } else {
+ Log.i(TAG, "Not starting call activity because user "
+ + "setup is in progress.");
+ }
}
return true;
}
@@ -244,7 +269,7 @@
try {
mContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
- Slog.w(TAG, "No activity found for android.intent.action.CALL_BUTTON.");
+ Log.w(TAG, "No activity found for android.intent.action.CALL_BUTTON.");
}
}
@@ -284,5 +309,10 @@
private void handleMediaKeyEvent(KeyEvent keyEvent) {
MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
}
+
+ private boolean isUserSetupComplete() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+ }
}
diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java
index 05796bb..cb32697 100644
--- a/core/java/android/view/PhoneWindow.java
+++ b/core/java/android/view/PhoneWindow.java
@@ -28,6 +28,7 @@
import android.os.UserHandle;
import com.android.internal.R;
+import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.StandaloneActionMode;
import com.android.internal.view.menu.ContextMenuBuilder;
@@ -64,6 +65,7 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.transition.Scene;
import android.transition.Transition;
import android.transition.TransitionInflater;
@@ -125,7 +127,7 @@
TypedValue mFixedWidthMinor;
TypedValue mFixedHeightMajor;
TypedValue mFixedHeightMinor;
- TypedValue mOutsetBottom;
+ int mOutsetBottomPx;
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
@@ -2368,12 +2370,10 @@
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
- if (mOutsetBottom != null) {
- final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
- int bottom = (int) mOutsetBottom.getDimension(metrics);
+ if (mOutsetBottomPx != 0) {
WindowInsets newInsets = insets.replaceSystemWindowInsets(
insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
- insets.getSystemWindowInsetRight(), bottom);
+ insets.getSystemWindowInsetRight(), mOutsetBottomPx);
return super.dispatchApplyWindowInsets(newInsets);
} else {
return super.dispatchApplyWindowInsets(insets);
@@ -2592,12 +2592,11 @@
}
}
- if (mOutsetBottom != null) {
+ if (mOutsetBottomPx != 0) {
int mode = MeasureSpec.getMode(heightMeasureSpec);
if (mode != MeasureSpec.UNSPECIFIED) {
- int outset = (int) mOutsetBottom.getDimension(metrics);
int height = MeasureSpec.getSize(heightMeasureSpec);
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + outset, mode);
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + mOutsetBottomPx, mode);
}
}
@@ -3472,10 +3471,9 @@
final boolean shouldUseBottomOutset =
display.getDisplayId() == Display.DEFAULT_DISPLAY
|| (getForcedWindowFlags() & FLAG_FULLSCREEN) != 0;
- if (shouldUseBottomOutset && a.hasValue(R.styleable.Window_windowOutsetBottom)) {
- if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
- a.getValue(R.styleable.Window_windowOutsetBottom,
- mOutsetBottom);
+ if (shouldUseBottomOutset) {
+ mOutsetBottomPx = ScreenShapeHelper.getWindowOutsetBottomPx(
+ getContext().getResources().getDisplayMetrics(), a);
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f5de8e3..c29b5c9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3168,6 +3168,9 @@
private Drawable mBackground;
private TintInfo mBackgroundTint;
+ @ViewDebug.ExportedProperty(deepExport = true, prefix = "fg_")
+ private ForegroundInfo mForegroundInfo;
+
/**
* RenderNode used for backgrounds.
* <p>
@@ -3182,13 +3185,23 @@
private String mTransitionName;
- private static class TintInfo {
+ static class TintInfo {
ColorStateList mTintList;
PorterDuff.Mode mTintMode;
boolean mHasTintMode;
boolean mHasTintList;
}
+ private static class ForegroundInfo {
+ private Drawable mDrawable;
+ private TintInfo mTintInfo;
+ private int mGravity = Gravity.START | Gravity.TOP;
+ private boolean mInsidePadding = true;
+ private boolean mBoundsChanged;
+ private final Rect mSelfBounds = new Rect();
+ private final Rect mOverlayBounds = new Rect();
+ }
+
static class ListenerInfo {
/**
* Listener used to dispatch focus change events.
@@ -4056,6 +4069,25 @@
setOutlineProviderFromAttribute(a.getInt(R.styleable.View_outlineProvider,
PROVIDER_BACKGROUND));
break;
+ case R.styleable.View_foreground:
+ setForeground(a.getDrawable(attr));
+ break;
+ case R.styleable.View_foregroundGravity:
+ setForegroundGravity(a.getInt(attr, Gravity.NO_GRAVITY));
+ break;
+ case R.styleable.View_foregroundTintMode:
+ setForegroundTintMode(Drawable.parseTintMode(a.getInt(attr, -1), null));
+ break;
+ case R.styleable.View_foregroundTint:
+ setForegroundTintList(a.getColorStateList(attr));
+ break;
+ case R.styleable.View_foregroundInsidePadding:
+ if (mForegroundInfo == null) {
+ mForegroundInfo = new ForegroundInfo();
+ }
+ mForegroundInfo.mInsidePadding = a.getBoolean(attr,
+ mForegroundInfo.mInsidePadding);
+ break;
}
}
@@ -8801,6 +8833,10 @@
if (dr != null && visible != dr.isVisible()) {
dr.setVisible(visible, false);
}
+ final Drawable fg = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+ if (fg != null && visible != fg.isVisible()) {
+ fg.setVisible(visible, false);
+ }
}
/**
@@ -9917,6 +9953,9 @@
}
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
final AttachInfo ai = mAttachInfo;
if (ai != null) {
@@ -10755,6 +10794,9 @@
invalidate(true);
}
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
invalidateParentIfNeeded();
if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
// View was rejected last time it was drawn by its parent; this may have changed
@@ -10820,6 +10862,9 @@
invalidate(true);
}
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
invalidateParentIfNeeded();
if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
// View was rejected last time it was drawn by its parent; this may have changed
@@ -10879,6 +10924,9 @@
invalidate(true);
}
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
invalidateParentIfNeeded();
if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
// View was rejected last time it was drawn by its parent; this may have changed
@@ -10935,6 +10983,9 @@
invalidate(true);
}
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
invalidateParentIfNeeded();
if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
// View was rejected last time it was drawn by its parent; this may have changed
@@ -15313,13 +15364,14 @@
// Step 4, draw the children
dispatchDraw(canvas);
- // Step 6, draw decorations (scrollbars)
- onDrawScrollBars(canvas);
-
+ // Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
+ // Step 6, draw decorations (foreground, scrollbars)
+ onDrawForeground(canvas);
+
// we're done...
return;
}
@@ -15461,12 +15513,13 @@
canvas.restoreToCount(saveCount);
- // Step 6, draw decorations (scrollbars)
- onDrawScrollBars(canvas);
-
+ // Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
+
+ // Step 6, draw decorations (foreground, scrollbars)
+ onDrawForeground(canvas);
}
/**
@@ -15849,6 +15902,9 @@
mPrivateFlags |= drawn;
mBackgroundSizeChanged = true;
+ if (mForegroundInfo != null) {
+ mForegroundInfo.mBoundsChanged = true;
+ }
notifySubtreeAccessibilityStateChangedIfNeeded();
}
@@ -15992,6 +16048,9 @@
if (mBackground != null) {
mBackground.setLayoutDirection(layoutDirection);
}
+ if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
+ mForegroundInfo.mDrawable.setLayoutDirection(layoutDirection);
+ }
mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
onResolveDrawables(layoutDirection);
}
@@ -16047,7 +16106,8 @@
*/
@CallSuper
protected boolean verifyDrawable(Drawable who) {
- return who == mBackground || (mScrollCache != null && mScrollCache.scrollBar == who);
+ return who == mBackground || (mScrollCache != null && mScrollCache.scrollBar == who)
+ || (mForegroundInfo != null && mForegroundInfo.mDrawable == who);
}
/**
@@ -16065,9 +16125,14 @@
protected void drawableStateChanged() {
final int[] state = getDrawableState();
- final Drawable d = mBackground;
- if (d != null && d.isStateful()) {
- d.setState(state);
+ final Drawable bg = mBackground;
+ if (bg != null && bg.isStateful()) {
+ bg.setState(state);
+ }
+
+ final Drawable fg = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+ if (fg != null && fg.isStateful()) {
+ fg.setState(state);
}
if (mScrollCache != null) {
@@ -16099,6 +16164,9 @@
if (mBackground != null) {
mBackground.setHotspot(x, y);
}
+ if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
+ mForegroundInfo.mDrawable.setHotspot(x, y);
+ }
dispatchDrawableHotspotChanged(x, y);
}
@@ -16270,6 +16338,9 @@
if (mStateListAnimator != null) {
mStateListAnimator.jumpToCurrentState();
}
+ if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
+ mForegroundInfo.mDrawable.jumpToCurrentState();
+ }
}
/**
@@ -16554,6 +16625,248 @@
}
/**
+ * Returns the drawable used as the foreground of this View. The
+ * foreground drawable, if non-null, is always drawn on top of the view's content.
+ *
+ * @return a Drawable or null if no foreground was set
+ *
+ * @see #onDrawForeground(Canvas)
+ */
+ public Drawable getForeground() {
+ return mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+ }
+
+ /**
+ * Supply a Drawable that is to be rendered on top of all of the content in the view.
+ *
+ * @param foreground the Drawable to be drawn on top of the children
+ *
+ * @attr ref android.R.styleable#View_foreground
+ */
+ public void setForeground(Drawable foreground) {
+ if (mForegroundInfo == null) {
+ if (foreground == null) {
+ // Nothing to do.
+ return;
+ }
+ mForegroundInfo = new ForegroundInfo();
+ }
+
+ if (foreground == mForegroundInfo.mDrawable) {
+ // Nothing to do
+ return;
+ }
+
+ if (mForegroundInfo.mDrawable != null) {
+ mForegroundInfo.mDrawable.setCallback(null);
+ unscheduleDrawable(mForegroundInfo.mDrawable);
+ }
+
+ mForegroundInfo.mDrawable = foreground;
+ if (foreground != null) {
+ setWillNotDraw(false);
+ foreground.setCallback(this);
+ foreground.setLayoutDirection(getLayoutDirection());
+ if (foreground.isStateful()) {
+ foreground.setState(getDrawableState());
+ }
+ applyForegroundTint();
+ }
+ requestLayout();
+ invalidate();
+ }
+
+ /**
+ * Magic bit used to support features of framework-internal window decor implementation details.
+ * This used to live exclusively in FrameLayout.
+ *
+ * @return true if the foreground should draw inside the padding region or false
+ * if it should draw inset by the view's padding
+ * @hide internal use only; only used by FrameLayout and internal screen layouts.
+ */
+ public boolean isForegroundInsidePadding() {
+ return mForegroundInfo != null ? mForegroundInfo.mInsidePadding : true;
+ }
+
+ /**
+ * Describes how the foreground is positioned.
+ *
+ * @return foreground gravity.
+ *
+ * @see #setForegroundGravity(int)
+ *
+ * @attr ref android.R.styleable#View_foregroundGravity
+ */
+ public int getForegroundGravity() {
+ return mForegroundInfo != null ? mForegroundInfo.mGravity
+ : Gravity.START | Gravity.TOP;
+ }
+
+ /**
+ * Describes how the foreground is positioned. Defaults to START and TOP.
+ *
+ * @param gravity see {@link android.view.Gravity}
+ *
+ * @see #getForegroundGravity()
+ *
+ * @attr ref android.R.styleable#View_foregroundGravity
+ */
+ public void setForegroundGravity(int gravity) {
+ if (mForegroundInfo == null) {
+ mForegroundInfo = new ForegroundInfo();
+ }
+
+ if (mForegroundInfo.mGravity != gravity) {
+ if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
+ gravity |= Gravity.START;
+ }
+
+ if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
+ gravity |= Gravity.TOP;
+ }
+
+ mForegroundInfo.mGravity = gravity;
+ requestLayout();
+ }
+ }
+
+ /**
+ * Applies a tint to the foreground drawable. Does not modify the current tint
+ * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+ * <p>
+ * Subsequent calls to {@link #setForeground(Drawable)} will automatically
+ * mutate the drawable and apply the specified tint and tint mode using
+ * {@link Drawable#setTintList(ColorStateList)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#View_foregroundTint
+ * @see #getForegroundTintList()
+ * @see Drawable#setTintList(ColorStateList)
+ */
+ public void setForegroundTintList(@Nullable ColorStateList tint) {
+ if (mForegroundInfo == null) {
+ mForegroundInfo = new ForegroundInfo();
+ }
+ if (mForegroundInfo.mTintInfo == null) {
+ mForegroundInfo.mTintInfo = new TintInfo();
+ }
+ mForegroundInfo.mTintInfo.mTintList = tint;
+ mForegroundInfo.mTintInfo.mHasTintList = true;
+
+ applyForegroundTint();
+ }
+
+ /**
+ * Return the tint applied to the foreground drawable, if specified.
+ *
+ * @return the tint applied to the foreground drawable
+ * @attr ref android.R.styleable#View_foregroundTint
+ * @see #setForegroundTintList(ColorStateList)
+ */
+ @Nullable
+ public ColorStateList getForegroundTintList() {
+ return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
+ ? mBackgroundTint.mTintList : null;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setForegroundTintList(ColorStateList)}} to the background
+ * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#View_foregroundTintMode
+ * @see #getForegroundTintMode()
+ * @see Drawable#setTintMode(PorterDuff.Mode)
+ */
+ public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+ if (mBackgroundTint == null) {
+ mBackgroundTint = new TintInfo();
+ }
+ mBackgroundTint.mTintMode = tintMode;
+ mBackgroundTint.mHasTintMode = true;
+
+ applyBackgroundTint();
+ }
+
+ /**
+ * Return the blending mode used to apply the tint to the foreground
+ * drawable, if specified.
+ *
+ * @return the blending mode used to apply the tint to the foreground
+ * drawable
+ * @attr ref android.R.styleable#View_foregroundTintMode
+ * @see #setBackgroundTintMode(PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getForegroundTintMode() {
+ return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
+ ? mForegroundInfo.mTintInfo.mTintMode : null;
+ }
+
+ private void applyForegroundTint() {
+ if (mForegroundInfo != null && mForegroundInfo.mDrawable != null
+ && mForegroundInfo.mTintInfo != null) {
+ final TintInfo tintInfo = mForegroundInfo.mTintInfo;
+ if (tintInfo.mHasTintList || tintInfo.mHasTintMode) {
+ mForegroundInfo.mDrawable = mForegroundInfo.mDrawable.mutate();
+
+ if (tintInfo.mHasTintList) {
+ mForegroundInfo.mDrawable.setTintList(tintInfo.mTintList);
+ }
+
+ if (tintInfo.mHasTintMode) {
+ mForegroundInfo.mDrawable.setTintMode(tintInfo.mTintMode);
+ }
+
+ // The drawable (or one of its children) may not have been
+ // stateful before applying the tint, so let's try again.
+ if (mForegroundInfo.mDrawable.isStateful()) {
+ mForegroundInfo.mDrawable.setState(getDrawableState());
+ }
+ }
+ }
+ }
+
+ /**
+ * Draw any foreground content for this view.
+ *
+ * <p>Foreground content may consist of scroll bars, a {@link #setForeground foreground}
+ * drawable or other view-specific decorations. The foreground is drawn on top of the
+ * primary view content.</p>
+ *
+ * @param canvas canvas to draw into
+ */
+ public void onDrawForeground(Canvas canvas) {
+ onDrawScrollBars(canvas);
+
+ final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+ if (foreground != null) {
+ if (mForegroundInfo.mBoundsChanged) {
+ mForegroundInfo.mBoundsChanged = false;
+ final Rect selfBounds = mForegroundInfo.mSelfBounds;
+ final Rect overlayBounds = mForegroundInfo.mOverlayBounds;
+
+ if (mForegroundInfo.mInsidePadding) {
+ selfBounds.set(0, 0, getWidth(), getHeight());
+ } else {
+ selfBounds.set(getPaddingLeft(), getPaddingTop(),
+ getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
+ }
+
+ final int ld = getLayoutDirection();
+ Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(),
+ foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld);
+ foreground.setBounds(overlayBounds);
+ }
+
+ foreground.draw(canvas);
+ }
+ }
+
+ /**
* Sets the padding. The view may add on the space required to display
* the scrollbars, depending on the style and visibility of the scrollbars.
* So the values returned from {@link #getPaddingLeft}, {@link #getPaddingTop},
@@ -18090,6 +18403,10 @@
// parts from this transparent region.
applyDrawableToTransparentRegion(mBackground, region);
}
+ final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+ if (foreground != null) {
+ applyDrawableToTransparentRegion(mForegroundInfo.mDrawable, region);
+ }
}
return true;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e790d4c..294174a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -40,7 +40,6 @@
import android.hardware.display.DisplayManager.DisplayListener;
import android.media.AudioManager;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
@@ -77,6 +76,7 @@
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
@@ -118,10 +118,11 @@
* at 60 Hz. This can be used to measure the potential framerate.
*/
private static final String PROPERTY_PROFILE_RENDERING = "viewroot.profile_rendering";
- private static final String PROPERTY_MEDIA_DISABLED = "config.disable_media";
- // property used by emulator to determine display shape
+ // properties used by emulator to determine display shape
public static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
+ public static final String PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX =
+ "ro.emu.win_outset_bottom_px";
/**
* Maximum time we allow the user to roll the trackball enough to generate
@@ -299,8 +300,6 @@
private Choreographer.FrameCallback mRenderProfiler;
private boolean mRenderProfilingEnabled;
- private boolean mMediaDisabled;
-
// Variables to track frames per second, enabled via DEBUG_FPS flag
private long mFpsStartTime = -1;
private long mFpsPrevTime = -1;
@@ -334,8 +333,6 @@
/** Set to true once doDie() has been called. */
private boolean mRemoved;
- private boolean mIsEmulator;
- private boolean mIsCircularEmulator;
private final boolean mWindowIsRound;
/**
@@ -392,8 +389,7 @@
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
loadSystemProperties();
- mWindowIsRound = context.getResources().getBoolean(
- com.android.internal.R.bool.config_windowIsRound);
+ mWindowIsRound = ScreenShapeHelper.getWindowIsRound(context.getResources());
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -1248,9 +1244,8 @@
contentInsets = mPendingContentInsets;
stableInsets = mPendingStableInsets;
}
- final boolean isRound = (mIsEmulator && mIsCircularEmulator) || mWindowIsRound;
mLastWindowInsets = new WindowInsets(contentInsets,
- null /* windowDecorInsets */, stableInsets, isRound);
+ null /* windowDecorInsets */, stableInsets, mWindowIsRound);
}
return mLastWindowInsets;
}
@@ -5362,10 +5357,6 @@
public void playSoundEffect(int effectId) {
checkThread();
- if (mMediaDisabled) {
- return;
- }
-
try {
final AudioManager audioManager = getAudioManager();
@@ -5572,9 +5563,6 @@
mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false);
profileRendering(mAttachInfo.mHasWindowFocus);
- // Media (used by sound effects)
- mMediaDisabled = SystemProperties.getBoolean(PROPERTY_MEDIA_DISABLED, false);
-
// Hardware rendering
if (mAttachInfo.mHardwareRenderer != null) {
if (mAttachInfo.mHardwareRenderer.loadSystemProperties()) {
@@ -5590,11 +5578,6 @@
mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200);
}
}
-
- // detect emulator
- mIsEmulator = Build.HARDWARE.contains("goldfish");
- mIsCircularEmulator =
- SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false);
}
});
}
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 5c05b5a..6feb94b 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -16,6 +16,7 @@
*/
package android.widget;
+import android.os.UserHandle;
import com.android.internal.R;
import android.app.AlertDialog;
@@ -243,7 +244,8 @@
@Override
public void onClick(DialogInterface dialog, int which) {
PackageManager pm = getContext().getPackageManager();
- pm.revokePermission(mPackageName, mPerm.name);
+ pm.revokePermission(mPackageName, mPerm.name,
+ new UserHandle(mContext.getUserId()));
PermissionItemView.this.setVisibility(View.GONE);
}
};
@@ -298,7 +300,7 @@
}
extractPerms(info, permSet, installedPkgInfo);
}
- // Get permissions related to shared user if any
+ // Get permissions related to shared user if any
if (info.sharedUserId != null) {
int sharedUid;
try {
@@ -358,7 +360,7 @@
String permName = strList[i];
// If we are only looking at an existing app, then we only
// care about permissions that have actually been granted to it.
- if (installedPkgInfo != null && info == installedPkgInfo) {
+ if (installedPkgInfo != null && info != installedPkgInfo) {
if ((flagsList[i]&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) {
continue;
}
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 5bc16cb..2aaa356 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -18,6 +18,7 @@
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.StyleRes;
import android.annotation.Widget;
import android.content.Context;
import android.content.res.Configuration;
@@ -31,6 +32,7 @@
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
+import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@@ -118,7 +120,9 @@
* @param count The shown week count.
*
* @attr ref android.R.styleable#CalendarView_shownWeekCount
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setShownWeekCount(int count) {
mDelegate.setShownWeekCount(count);
}
@@ -129,7 +133,9 @@
* @return The shown week count.
*
* @attr ref android.R.styleable#CalendarView_shownWeekCount
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public int getShownWeekCount() {
return mDelegate.getShownWeekCount();
}
@@ -140,7 +146,9 @@
* @param color The week background color.
*
* @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setSelectedWeekBackgroundColor(@ColorInt int color) {
mDelegate.setSelectedWeekBackgroundColor(color);
}
@@ -151,8 +159,10 @@
* @return The week background color.
*
* @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getSelectedWeekBackgroundColor() {
return mDelegate.getSelectedWeekBackgroundColor();
}
@@ -163,7 +173,9 @@
* @param color The focused month date color.
*
* @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setFocusedMonthDateColor(@ColorInt int color) {
mDelegate.setFocusedMonthDateColor(color);
}
@@ -174,8 +186,10 @@
* @return The focused month date color.
*
* @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getFocusedMonthDateColor() {
return mDelegate.getFocusedMonthDateColor();
}
@@ -186,7 +200,9 @@
* @param color A not focused month date color.
*
* @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setUnfocusedMonthDateColor(@ColorInt int color) {
mDelegate.setUnfocusedMonthDateColor(color);
}
@@ -197,8 +213,10 @@
* @return A not focused month date color.
*
* @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getUnfocusedMonthDateColor() {
return mDelegate.getUnfocusedMonthDateColor();
}
@@ -209,7 +227,9 @@
* @param color The week number color.
*
* @attr ref android.R.styleable#CalendarView_weekNumberColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setWeekNumberColor(@ColorInt int color) {
mDelegate.setWeekNumberColor(color);
}
@@ -220,8 +240,10 @@
* @return The week number color.
*
* @attr ref android.R.styleable#CalendarView_weekNumberColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getWeekNumberColor() {
return mDelegate.getWeekNumberColor();
}
@@ -232,7 +254,9 @@
* @param color The week separator color.
*
* @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setWeekSeparatorLineColor(@ColorInt int color) {
mDelegate.setWeekSeparatorLineColor(color);
}
@@ -243,8 +267,10 @@
* @return The week separator color.
*
* @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getWeekSeparatorLineColor() {
return mDelegate.getWeekSeparatorLineColor();
}
@@ -256,7 +282,9 @@
* @param resourceId The vertical bar drawable resource id.
*
* @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setSelectedDateVerticalBar(@DrawableRes int resourceId) {
mDelegate.setSelectedDateVerticalBar(resourceId);
}
@@ -268,7 +296,9 @@
* @param drawable The vertical bar drawable.
*
* @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setSelectedDateVerticalBar(Drawable drawable) {
mDelegate.setSelectedDateVerticalBar(drawable);
}
@@ -278,7 +308,9 @@
* the end of the selected date.
*
* @return The vertical bar drawable.
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public Drawable getSelectedDateVerticalBar() {
return mDelegate.getSelectedDateVerticalBar();
}
@@ -519,29 +551,36 @@
void setShownWeekCount(int count);
int getShownWeekCount();
- void setSelectedWeekBackgroundColor(int color);
+ void setSelectedWeekBackgroundColor(@ColorInt int color);
+ @ColorInt
int getSelectedWeekBackgroundColor();
- void setFocusedMonthDateColor(int color);
+ void setFocusedMonthDateColor(@ColorInt int color);
+ @ColorInt
int getFocusedMonthDateColor();
- void setUnfocusedMonthDateColor(int color);
+ void setUnfocusedMonthDateColor(@ColorInt int color);
+ @ColorInt
int getUnfocusedMonthDateColor();
- void setWeekNumberColor(int color);
+ void setWeekNumberColor(@ColorInt int color);
+ @ColorInt
int getWeekNumberColor();
- void setWeekSeparatorLineColor(int color);
+ void setWeekSeparatorLineColor(@ColorInt int color);
+ @ColorInt
int getWeekSeparatorLineColor();
- void setSelectedDateVerticalBar(int resourceId);
+ void setSelectedDateVerticalBar(@DrawableRes int resourceId);
void setSelectedDateVerticalBar(Drawable drawable);
Drawable getSelectedDateVerticalBar();
- void setWeekDayTextAppearance(int resourceId);
+ void setWeekDayTextAppearance(@StyleRes int resourceId);
+ @StyleRes
int getWeekDayTextAppearance();
- void setDateTextAppearance(int resourceId);
+ void setDateTextAppearance(@StyleRes int resourceId);
+ @StyleRes
int getDateTextAppearance();
void setMinDate(long minDate);
@@ -569,18 +608,12 @@
* An abstract class which can be used as a start for CalendarView implementations
*/
abstract static class AbstractCalendarViewDelegate implements CalendarViewDelegate {
- /** 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;
protected Context mContext;
protected Locale mCurrentLocale;
@@ -600,21 +633,131 @@
mCurrentLocale = locale;
}
- /**
- * Parses the given <code>date</code> and in case of success sets
- * the result to the <code>outDate</code>.
- *
- * @return True if the date was parsed.
- */
- protected boolean parseDate(String date, Calendar outDate) {
- try {
- outDate.setTime(DATE_FORMATTER.parse(date));
- return true;
- } catch (ParseException e) {
- Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
- return false;
- }
+ @Override
+ public void setShownWeekCount(int count) {
+ // Deprecated.
+ }
+
+ @Override
+ public int getShownWeekCount() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setSelectedWeekBackgroundColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getSelectedWeekBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public void setFocusedMonthDateColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getFocusedMonthDateColor() {
+ return 0;
+ }
+
+ @Override
+ public void setUnfocusedMonthDateColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getUnfocusedMonthDateColor() {
+ return 0;
+ }
+
+ @Override
+ public void setWeekNumberColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getWeekNumberColor() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setWeekSeparatorLineColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getWeekSeparatorLineColor() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(@DrawableRes int resId) {
+ // Deprecated.
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(Drawable drawable) {
+ // Deprecated.
+ }
+
+ @Override
+ public Drawable getSelectedDateVerticalBar() {
+ // Deprecated.
+ return null;
+ }
+
+ @Override
+ public void setShowWeekNumber(boolean showWeekNumber) {
+ // Deprecated.
+ }
+
+ @Override
+ public boolean getShowWeekNumber() {
+ // Deprecated.
+ return false;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Nothing to do here, configuration changes are already propagated
+ // by ViewGroup.
}
}
+ /** String for parsing dates. */
+ private static final String DATE_FORMAT = "MM/dd/yyyy";
+
+ /** Date format for parsing dates. */
+ private static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
+
+ /**
+ * Utility method for the date format used by CalendarView's min/max date.
+ *
+ * @hide Use only as directed. For internal use only.
+ */
+ public static boolean parseDate(String date, Calendar outDate) {
+ if (date == null || date.isEmpty()) {
+ return false;
+ }
+
+ try {
+ final Date parsedDate = DATE_FORMATTER.parse(date);
+ outDate.setTime(parsedDate);
+ return true;
+ } catch (ParseException e) {
+ Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
+ return false;
+ }
+ }
}
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index 2ab3548..6ab3828 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -27,7 +27,6 @@
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;
@@ -267,12 +266,12 @@
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);
+ if (!CalendarView.parseDate(minDate, mMinDate)) {
+ CalendarView.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 (!CalendarView.parseDate(maxDate, mMaxDate)) {
+ CalendarView.parseDate(DEFAULT_MAX_DATE, mMaxDate);
}
if (mMaxDate.before(mMinDate)) {
throw new IllegalArgumentException("Max date cannot be before min date.");
diff --git a/core/java/android/widget/CalendarViewMaterialDelegate.java b/core/java/android/widget/CalendarViewMaterialDelegate.java
index b0f3740..7bce756 100644
--- a/core/java/android/widget/CalendarViewMaterialDelegate.java
+++ b/core/java/android/widget/CalendarViewMaterialDelegate.java
@@ -16,20 +16,11 @@
package android.widget;
-import com.android.internal.R;
-
+import android.annotation.StyleRes;
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;
@@ -40,142 +31,32 @@
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 = new DayPickerView(context, attrs, defStyleAttr, defStyleRes);
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.
+ public void setWeekDayTextAppearance(@StyleRes int resId) {
+ mDayPickerView.setDayOfWeekTextAppearance(resId);
}
- @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) {
-
- }
-
+ @StyleRes
@Override
public int getWeekDayTextAppearance() {
- return 0;
+ return mDayPickerView.getDayOfWeekTextAppearance();
}
@Override
- public void setDateTextAppearance(int resourceId) {
-
+ public void setDateTextAppearance(@StyleRes int resId) {
+ mDayPickerView.setDayTextAppearance(resId);
}
+ @StyleRes
@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;
+ return mDayPickerView.getDayTextAppearance();
}
@Override
@@ -199,17 +80,6 @@
}
@Override
- public void setShowWeekNumber(boolean showWeekNumber) {
- // Deprecated.
- }
-
- @Override
- public boolean getShowWeekNumber() {
- // Deprecated.
- return false;
- }
-
- @Override
public void setFirstDayOfWeek(int firstDayOfWeek) {
mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
}
@@ -221,12 +91,12 @@
@Override
public void setDate(long date) {
- mDayPickerView.setDate(date, true, false);
+ mDayPickerView.setDate(date, true);
}
@Override
public void setDate(long date, boolean animate, boolean center) {
- mDayPickerView.setDate(date, animate, center);
+ mDayPickerView.setDate(date, animate);
}
@Override
@@ -239,12 +109,6 @@
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
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 45998f7..5f5943f 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -98,7 +98,7 @@
private final DatePickerDelegate mDelegate;
/**
- * The callback used to indicate the user changes\d the date.
+ * The callback used to indicate the user changed the date.
*/
public interface OnDateChangedListener {
@@ -348,9 +348,13 @@
}
/**
- * Gets whether the {@link CalendarView} is shown.
+ * Returns whether the {@link CalendarView} is shown.
+ * <p>
+ * <strong>Note:</strong> This method returns {@code false} when the
+ * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+ * to {@code calendar}.
*
- * @return True if the calendar view is shown.
+ * @return {@code true} if the calendar view is shown
* @see #getCalendarView()
*/
public boolean getCalendarViewShown() {
@@ -358,13 +362,13 @@
}
/**
- * Gets the {@link CalendarView}.
+ * Returns the {@link CalendarView} used by this picker.
* <p>
- * This method returns {@code null} when the
+ * <strong>Note:</strong> This method returns {@code null} when the
* {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
* to {@code calendar}.
*
- * @return The calendar view.
+ * @return the calendar view
* @see #getCalendarViewShown()
*/
public CalendarView getCalendarView() {
@@ -374,20 +378,25 @@
/**
* Sets whether the {@link CalendarView} is shown.
* <p>
- * Calling this method has no effect when the
+ * <strong>Note:</strong> Calling this method has no effect when the
* {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
* to {@code calendar}.
*
- * @param shown True if the calendar view is to be shown.
+ * @param shown {@code true} to show the calendar view, {@code false} to
+ * hide it
*/
public void setCalendarViewShown(boolean shown) {
mDelegate.setCalendarViewShown(shown);
}
/**
- * Gets whether the spinners are shown.
+ * Returns whether the spinners are shown.
+ * <p>
+ * <strong>Note:</strong> his method returns {@code false} when the
+ * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+ * to {@code calendar}.
*
- * @return True if the spinners are shown.
+ * @return {@code true} if the spinners are shown
*/
public boolean getSpinnersShown() {
return mDelegate.getSpinnersShown();
@@ -395,8 +404,13 @@
/**
* Sets whether the spinners are shown.
+ * <p>
+ * Calling this method has no effect when the
+ * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+ * to {@code calendar}.
*
- * @param shown True if the spinners are to be shown.
+ * @param shown {@code true} to show the spinners, {@code false} to hide
+ * them
*/
public void setSpinnersShown(boolean shown) {
mDelegate.setSpinnersShown(shown);
@@ -489,15 +503,14 @@
mDelegator = delegator;
mContext = context;
- // initialization based on locale
setCurrentLocale(Locale.getDefault());
}
protected void setCurrentLocale(Locale locale) {
- if (locale.equals(mCurrentLocale)) {
- return;
+ if (!locale.equals(mCurrentLocale)) {
+ mCurrentLocale = locale;
+ onLocaleChanged(locale);
}
- mCurrentLocale = locale;
}
@Override
@@ -510,6 +523,10 @@
mValidationCallback.onValidationChanged(valid);
}
}
+
+ protected void onLocaleChanged(Locale locale) {
+ // Stub.
+ }
}
/**
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 0e3ec7f..7b8a979 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -26,90 +27,84 @@
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.util.StateSet;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
+import android.widget.DayPickerView.OnDaySelectedListener;
+import android.widget.YearPickerView.OnYearSelectedListener;
import com.android.internal.R;
-import com.android.internal.widget.AccessibleDateAnimator;
import java.text.SimpleDateFormat;
import java.util.Calendar;
-import java.util.HashSet;
import java.util.Locale;
/**
* A delegate for picking up a date (day / month / year).
*/
-class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate implements
- View.OnClickListener, DatePickerController {
+class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
+
private static final int USE_LOCALE = 0;
private static final int UNINITIALIZED = -1;
- private static final int MONTH_AND_DAY_VIEW = 0;
- private static final int YEAR_VIEW = 1;
+ private static final int VIEW_MONTH_DAY = 0;
+ private static final int VIEW_YEAR = 1;
private static final int DEFAULT_START_YEAR = 1900;
private static final int DEFAULT_END_YEAR = 2100;
private static final int ANIMATION_DURATION = 300;
- private static final int MONTH_INDEX = 0;
- private static final int DAY_INDEX = 1;
- private static final int YEAR_INDEX = 2;
+ public static final int[] ATTRS_TEXT_COLOR = new int[]{com.android.internal.R.attr.textColor};
- private SimpleDateFormat mYearFormat = new SimpleDateFormat("y", Locale.getDefault());
- private SimpleDateFormat mDayFormat = new SimpleDateFormat("d", Locale.getDefault());
+ public static final int[] ATTRS_DISABLED_ALPHA = new int[]{
+ com.android.internal.R.attr.disabledAlpha};
- private TextView mDayOfWeekView;
+ private SimpleDateFormat mYearFormat;
+ private SimpleDateFormat mMonthDayFormat;
- /** Layout that contains the current month, day, and year. */
- private LinearLayout mMonthDayYearLayout;
+ // Top-level container.
+ private ViewGroup mContainer;
- /** Clickable layout that contains the current day and year. */
- private LinearLayout mMonthAndDayLayout;
+ // Header views.
+ private TextView mHeaderYear;
+ private TextView mHeaderMonthDay;
- private TextView mHeaderMonthTextView;
- private TextView mHeaderDayOfMonthTextView;
- private TextView mHeaderYearTextView;
+ // Picker views.
+ private ViewAnimator mAnimator;
private DayPickerView mDayPickerView;
private YearPickerView mYearPickerView;
- private boolean mIsEnabled = true;
-
// Accessibility strings.
- private String mDayPickerDescription;
private String mSelectDay;
- private String mYearPickerDescription;
private String mSelectYear;
- private AccessibleDateAnimator mAnimator;
-
private DatePicker.OnDateChangedListener mDateChangedListener;
private int mCurrentView = UNINITIALIZED;
- private Calendar mCurrentDate;
- private Calendar mTempDate;
- private Calendar mMinDate;
- private Calendar mMaxDate;
+ private final Calendar mCurrentDate;
+ private final Calendar mTempDate;
+ private final Calendar mMinDate;
+ private final Calendar mMaxDate;
private int mFirstDayOfWeek = USE_LOCALE;
- private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>();
-
public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(delegator, context);
- final Locale locale = Locale.getDefault();
- mMinDate = getCalendarForLocale(mMinDate, locale);
- mMaxDate = getCalendarForLocale(mMaxDate, locale);
- mTempDate = getCalendarForLocale(mMaxDate, locale);
- mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
+ final Locale locale = mCurrentLocale;
+ mCurrentDate = Calendar.getInstance(locale);
+ mTempDate = Calendar.getInstance(locale);
+ mMinDate = Calendar.getInstance(locale);
+ mMaxDate = Calendar.getInstance(locale);
mMinDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1);
mMaxDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31);
@@ -120,71 +115,64 @@
final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
final int layoutResourceId = a.getResourceId(
- R.styleable.DatePicker_internalLayout, R.layout.date_picker_holo);
- final View mainView = inflater.inflate(layoutResourceId, null);
- mDelegator.addView(mainView);
+ R.styleable.DatePicker_internalLayout, R.layout.date_picker_material);
- mDayOfWeekView = (TextView) mainView.findViewById(R.id.date_picker_header);
+ // Set up and attach container.
+ mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator);
- // Layout that contains the current date and day name header.
- final LinearLayout dateLayout = (LinearLayout) mainView.findViewById(
- R.id.day_picker_selector_layout);
- mMonthDayYearLayout = (LinearLayout) mainView.findViewById(
- R.id.date_picker_month_day_year_layout);
- mMonthAndDayLayout = (LinearLayout) mainView.findViewById(
- R.id.date_picker_month_and_day_layout);
- mMonthAndDayLayout.setOnClickListener(this);
- mHeaderMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_month);
- mHeaderDayOfMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_day);
- mHeaderYearTextView = (TextView) mainView.findViewById(R.id.date_picker_year);
- mHeaderYearTextView.setOnClickListener(this);
+ // Set up header views.
+ final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header);
+ mHeaderYear = (TextView) header.findViewById(R.id.date_picker_header_year);
+ mHeaderYear.setOnClickListener(mOnHeaderClickListener);
+ mHeaderMonthDay = (TextView) header.findViewById(R.id.date_picker_header_date);
+ mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener);
- // Obtain default highlight color from the theme.
- final int defaultHighlightColor = mHeaderYearTextView.getHighlightColor();
+ // For the sake of backwards compatibility, attempt to extract the text
+ // color from the header month text appearance. If it's set, we'll let
+ // that override the "real" header text color.
+ ColorStateList headerTextColor = null;
- // Use Theme attributes if possible
- final int dayOfWeekTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_dayOfWeekTextAppearance, 0);
- if (dayOfWeekTextAppearanceResId != 0) {
- mDayOfWeekView.setTextAppearance(context, dayOfWeekTextAppearanceResId);
- }
-
- mDayOfWeekView.setBackground(a.getDrawable(R.styleable.DatePicker_dayOfWeekBackground));
-
- dateLayout.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
-
- final int monthTextAppearanceResId = a.getResourceId(
+ @SuppressWarnings("deprecation")
+ final int monthHeaderTextAppearance = a.getResourceId(
R.styleable.DatePicker_headerMonthTextAppearance, 0);
- if (monthTextAppearanceResId != 0) {
- mHeaderMonthTextView.setTextAppearance(context, monthTextAppearanceResId);
+ if (monthHeaderTextAppearance != 0) {
+ final TypedArray textAppearance = mContext.obtainStyledAttributes(null,
+ ATTRS_TEXT_COLOR, 0, monthHeaderTextAppearance);
+ final ColorStateList legacyHeaderTextColor = textAppearance.getColorStateList(0);
+ headerTextColor = applyLegacyColorFixes(legacyHeaderTextColor);
+ textAppearance.recycle();
}
- final int dayOfMonthTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_headerDayOfMonthTextAppearance, 0);
- if (dayOfMonthTextAppearanceResId != 0) {
- mHeaderDayOfMonthTextView.setTextAppearance(context, dayOfMonthTextAppearanceResId);
+ if (headerTextColor == null) {
+ headerTextColor = a.getColorStateList(R.styleable.DatePicker_headerTextColor);
}
- final int headerYearTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_headerYearTextAppearance, 0);
- if (headerYearTextAppearanceResId != 0) {
- mHeaderYearTextView.setTextAppearance(context, headerYearTextAppearanceResId);
+ if (headerTextColor != null) {
+ mHeaderYear.setTextColor(headerTextColor);
+ mHeaderMonthDay.setTextColor(headerTextColor);
}
- mDayPickerView = new DayPickerView(mContext);
+ // Set up header background, if available.
+ if (a.hasValueOrEmpty(R.styleable.DatePicker_headerBackground)) {
+ header.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
+ }
+
+ // Set up picker container.
+ mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator);
+
+ // Set up day picker view.
+ mDayPickerView = (DayPickerView) mAnimator.findViewById(R.id.date_picker_day_picker);
mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek);
mDayPickerView.setMinDate(mMinDate.getTimeInMillis());
mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis());
mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
- mYearPickerView = new YearPickerView(mContext);
- mYearPickerView.init(this);
+ // Set up year picker view.
+ mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker);
mYearPickerView.setRange(mMinDate, mMaxDate);
-
- final ColorStateList yearBackgroundColor = a.getColorStateList(
- R.styleable.DatePicker_yearListSelectorColor);
- mYearPickerView.setYearBackgroundColor(yearBackgroundColor);
+ mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+ mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
final int yearTextAppearanceResId = a.getResourceId(
R.styleable.DatePicker_yearListItemTextAppearance, 0);
@@ -192,170 +180,201 @@
mYearPickerView.setYearTextAppearance(yearTextAppearanceResId);
}
- final ColorStateList calendarTextColor = a.getColorStateList(
- R.styleable.DatePicker_calendarTextColor);
- mDayPickerView.setCalendarTextColor(calendarTextColor);
+ final int yearActivatedTextAppearanceResId = a.getResourceId(
+ R.styleable.DatePicker_yearListItemActivatedTextAppearance, 0);
+ if (yearActivatedTextAppearanceResId != 0) {
+ mYearPickerView.setYearActivatedTextAppearance(yearActivatedTextAppearanceResId);
+ }
- final ColorStateList calendarDayBackgroundColor = a.getColorStateList(
- R.styleable.DatePicker_calendarDayBackgroundColor);
- mDayPickerView.setCalendarDayBackgroundColor(calendarDayBackgroundColor);
+ a.recycle();
- mDayPickerDescription = res.getString(R.string.day_picker_description);
+ // Set up content descriptions.
mSelectDay = res.getString(R.string.select_day);
- mYearPickerDescription = res.getString(R.string.year_picker_description);
mSelectYear = res.getString(R.string.select_year);
- mAnimator = (AccessibleDateAnimator) mainView.findViewById(R.id.animator);
- mAnimator.addView(mDayPickerView);
- mAnimator.addView(mYearPickerView);
- mAnimator.setDateMillis(mCurrentDate.getTimeInMillis());
+ final Animation inAnim = new AlphaAnimation(0, 1);
+ inAnim.setDuration(ANIMATION_DURATION);
+ mAnimator.setInAnimation(inAnim);
- final Animation animation = new AlphaAnimation(0.0f, 1.0f);
- animation.setDuration(ANIMATION_DURATION);
- mAnimator.setInAnimation(animation);
+ final Animation outAnim = new AlphaAnimation(1, 0);
+ outAnim.setDuration(ANIMATION_DURATION);
+ mAnimator.setOutAnimation(outAnim);
- final Animation animation2 = new AlphaAnimation(1.0f, 0.0f);
- animation2.setDuration(ANIMATION_DURATION);
- mAnimator.setOutAnimation(animation2);
+ // Initialize for current locale. This also initializes the date, so no
+ // need to call onDateChanged.
+ onLocaleChanged(mCurrentLocale);
- updateDisplay(false);
- setCurrentView(MONTH_AND_DAY_VIEW);
+ setCurrentView(VIEW_MONTH_DAY);
}
/**
- * Gets a calendar for locale bootstrapped with the value of a given calendar.
+ * The legacy text color might have been poorly defined. Ensures that it
+ * has an appropriate activated state, using the selected state if one
+ * exists or modifying the default text color otherwise.
*
- * @param oldCalendar The old calendar.
- * @param locale The locale.
+ * @param color a legacy text color, or {@code null}
+ * @return a color state list with an appropriate activated state, or
+ * {@code null} if a valid activated state could not be generated
*/
- private 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;
+ @Nullable
+ private ColorStateList applyLegacyColorFixes(@Nullable ColorStateList color) {
+ if (color == null || color.hasState(R.attr.state_activated)) {
+ return color;
}
+
+ final int activatedColor;
+ final int defaultColor;
+ if (color.hasState(R.attr.state_selected)) {
+ activatedColor = color.getColorForState(StateSet.get(
+ StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_SELECTED), 0);
+ defaultColor = color.getColorForState(StateSet.get(
+ StateSet.VIEW_STATE_ENABLED), 0);
+ } else {
+ activatedColor = color.getDefaultColor();
+
+ // Generate a non-activated color using the disabled alpha.
+ final TypedArray ta = mContext.obtainStyledAttributes(ATTRS_DISABLED_ALPHA);
+ final float disabledAlpha = ta.getFloat(0, 0.30f);
+ defaultColor = multiplyAlphaComponent(activatedColor, disabledAlpha);
+ }
+
+ if (activatedColor == 0 || defaultColor == 0) {
+ // We somehow failed to obtain the colors.
+ return null;
+ }
+
+ final int[][] stateSet = new int[][] {{ R.attr.state_activated }, {}};
+ final int[] colors = new int[] { activatedColor, defaultColor };
+ return new ColorStateList(stateSet, colors);
+ }
+
+ private int multiplyAlphaComponent(int color, float alphaMod) {
+ final int srcRgb = color & 0xFFFFFF;
+ final int srcAlpha = (color >> 24) & 0xFF;
+ final int dstAlpha = (int) (srcAlpha * alphaMod + 0.5f);
+ return srcRgb | (dstAlpha << 24);
}
/**
- * Compute the array representing the order of Month / Day / Year views in their layout.
- * Will be used for I18N purpose as the order of them depends on the Locale.
+ * Listener called when the user selects a day in the day picker view.
*/
- private int[] getMonthDayYearIndexes(String pattern) {
- int[] result = new int[3];
+ private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() {
+ @Override
+ public void onDaySelected(DayPickerView view, Calendar day) {
+ mCurrentDate.setTimeInMillis(day.getTimeInMillis());
+ onDateChanged(true, true);
+ }
+ };
- final String filteredPattern = pattern.replaceAll("'.*?'", "");
-
- final int dayIndex = filteredPattern.indexOf('d');
- final int monthMIndex = filteredPattern.indexOf("M");
- final int monthIndex = (monthMIndex != -1) ? monthMIndex : filteredPattern.indexOf("L");
- final int yearIndex = filteredPattern.indexOf("y");
-
- if (yearIndex < monthIndex) {
- result[YEAR_INDEX] = 0;
-
- if (monthIndex < dayIndex) {
- result[MONTH_INDEX] = 1;
- result[DAY_INDEX] = 2;
- } else {
- result[MONTH_INDEX] = 2;
- result[DAY_INDEX] = 1;
+ /**
+ * Listener called when the user selects a year in the year picker view.
+ */
+ private final OnYearSelectedListener mOnYearSelectedListener = new OnYearSelectedListener() {
+ @Override
+ public void onYearChanged(YearPickerView view, int year) {
+ // If the newly selected month / year does not contain the
+ // currently selected day number, change the selected day number
+ // to the last day of the selected month or year.
+ // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
+ // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
+ final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
+ final int month = mCurrentDate.get(Calendar.MONTH);
+ final int daysInMonth = getDaysInMonth(month, year);
+ if (day > daysInMonth) {
+ mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
}
- } else {
- result[YEAR_INDEX] = 2;
- if (monthIndex < dayIndex) {
- result[MONTH_INDEX] = 0;
- result[DAY_INDEX] = 1;
- } else {
- result[MONTH_INDEX] = 1;
- result[DAY_INDEX] = 0;
+ mCurrentDate.set(Calendar.YEAR, year);
+ onDateChanged(true, true);
+
+ // Automatically switch to day picker.
+ setCurrentView(VIEW_MONTH_DAY);
+ }
+ };
+
+ /**
+ * Listener called when the user clicks on a header item.
+ */
+ private final OnClickListener mOnHeaderClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ tryVibrate();
+
+ switch (v.getId()) {
+ case R.id.date_picker_header_year:
+ setCurrentView(VIEW_YEAR);
+ break;
+ case R.id.date_picker_header_date:
+ setCurrentView(VIEW_MONTH_DAY);
+ break;
}
}
- return result;
+ };
+
+ @Override
+ protected void onLocaleChanged(Locale locale) {
+ final TextView headerYear = mHeaderYear;
+ if (headerYear == null) {
+ // Abort, we haven't initialized yet. This method will get called
+ // again later after everything has been set up.
+ return;
+ }
+
+ // Update the date formatter.
+ final String datePattern = DateFormat.getBestDateTimePattern(locale, "EMMMd");
+ mMonthDayFormat = new SimpleDateFormat(datePattern, locale);
+ mYearFormat = new SimpleDateFormat("y", locale);
+
+ // Update the header text.
+ onCurrentDateChanged(false);
}
- private void updateDisplay(boolean announce) {
- if (mDayOfWeekView != null) {
- mDayOfWeekView.setText(mCurrentDate.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG,
- Locale.getDefault()));
+ private void onCurrentDateChanged(boolean announce) {
+ if (mHeaderYear == null) {
+ // Abort, we haven't initialized yet. This method will get called
+ // again later after everything has been set up.
+ return;
}
- // Compute indices of Month, Day and Year views
- final String bestDateTimePattern =
- DateFormat.getBestDateTimePattern(mCurrentLocale, "yMMMd");
- final int[] viewIndices = getMonthDayYearIndexes(bestDateTimePattern);
+ final String year = mYearFormat.format(mCurrentDate.getTime());
+ mHeaderYear.setText(year);
- // Position the Year and MonthAndDay views within the header.
- mMonthDayYearLayout.removeAllViews();
- if (viewIndices[YEAR_INDEX] == 0) {
- mMonthDayYearLayout.addView(mHeaderYearTextView);
- mMonthDayYearLayout.addView(mMonthAndDayLayout);
- } else {
- mMonthDayYearLayout.addView(mMonthAndDayLayout);
- mMonthDayYearLayout.addView(mHeaderYearTextView);
- }
+ final String monthDay = mMonthDayFormat.format(mCurrentDate.getTime());
+ mHeaderMonthDay.setText(monthDay);
- // Position Day and Month views within the MonthAndDay view.
- mMonthAndDayLayout.removeAllViews();
- if (viewIndices[MONTH_INDEX] > viewIndices[DAY_INDEX]) {
- mMonthAndDayLayout.addView(mHeaderDayOfMonthTextView);
- mMonthAndDayLayout.addView(mHeaderMonthTextView);
- } else {
- mMonthAndDayLayout.addView(mHeaderMonthTextView);
- mMonthAndDayLayout.addView(mHeaderDayOfMonthTextView);
- }
-
- mHeaderMonthTextView.setText(mCurrentDate.getDisplayName(Calendar.MONTH, Calendar.SHORT,
- Locale.getDefault()).toUpperCase(Locale.getDefault()));
- mHeaderDayOfMonthTextView.setText(mDayFormat.format(mCurrentDate.getTime()));
- mHeaderYearTextView.setText(mYearFormat.format(mCurrentDate.getTime()));
-
- // Accessibility.
- long millis = mCurrentDate.getTimeInMillis();
- mAnimator.setDateMillis(millis);
- int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR;
- String monthAndDayText = DateUtils.formatDateTime(mContext, millis, flags);
- mMonthAndDayLayout.setContentDescription(monthAndDayText);
-
+ // TODO: This should use live regions.
if (announce) {
- flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
- String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
+ final long millis = mCurrentDate.getTimeInMillis();
+ final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
+ final String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
mAnimator.announceForAccessibility(fullDateText);
}
}
private void setCurrentView(final int viewIndex) {
- long millis = mCurrentDate.getTimeInMillis();
-
switch (viewIndex) {
- case MONTH_AND_DAY_VIEW:
- mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
+ case VIEW_MONTH_DAY:
+ mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
+
if (mCurrentView != viewIndex) {
- mMonthAndDayLayout.setSelected(true);
- mHeaderYearTextView.setSelected(false);
- mAnimator.setDisplayedChild(MONTH_AND_DAY_VIEW);
+ mHeaderMonthDay.setActivated(true);
+ mHeaderYear.setActivated(false);
+ mAnimator.setDisplayedChild(VIEW_MONTH_DAY);
mCurrentView = viewIndex;
}
- final int flags = DateUtils.FORMAT_SHOW_DATE;
- final String dayString = DateUtils.formatDateTime(mContext, millis, flags);
- mAnimator.setContentDescription(mDayPickerDescription + ": " + dayString);
mAnimator.announceForAccessibility(mSelectDay);
break;
- case YEAR_VIEW:
- mYearPickerView.onDateChanged();
+ case VIEW_YEAR:
+ mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+
if (mCurrentView != viewIndex) {
- mMonthAndDayLayout.setSelected(false);
- mHeaderYearTextView.setSelected(true);
- mAnimator.setDisplayedChild(YEAR_VIEW);
+ mHeaderMonthDay.setActivated(false);
+ mHeaderYear.setActivated(true);
+ mAnimator.setDisplayedChild(VIEW_YEAR);
mCurrentView = viewIndex;
}
- final CharSequence yearString = mYearFormat.format(millis);
- mAnimator.setContentDescription(mYearPickerDescription + ": " + yearString);
mAnimator.announceForAccessibility(mSelectYear);
break;
}
@@ -383,20 +402,18 @@
}
private void onDateChanged(boolean fromUser, boolean callbackToClient) {
+ final int year = mCurrentDate.get(Calendar.YEAR);
+
if (callbackToClient && mDateChangedListener != null) {
- final int year = mCurrentDate.get(Calendar.YEAR);
final int monthOfYear = mCurrentDate.get(Calendar.MONTH);
final int dayOfMonth = mCurrentDate.get(Calendar.DAY_OF_MONTH);
mDateChangedListener.onDateChanged(mDelegator, year, monthOfYear, dayOfMonth);
}
- for (OnDateChangedListener listener : mListeners) {
- listener.onDateChanged();
- }
+ mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
+ mYearPickerView.setYear(year);
- mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
-
- updateDisplay(fromUser);
+ onCurrentDateChanged(fromUser);
if (fromUser) {
tryVibrate();
@@ -477,15 +494,12 @@
@Override
public void setEnabled(boolean enabled) {
- mMonthAndDayLayout.setEnabled(enabled);
- mHeaderYearTextView.setEnabled(enabled);
- mAnimator.setEnabled(enabled);
- mIsEnabled = enabled;
+ mContainer.setEnabled(false);
}
@Override
public boolean isEnabled() {
- return mIsEnabled;
+ return mContainer.isEnabled();
}
@Override
@@ -516,8 +530,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
- mYearFormat = new SimpleDateFormat("y", newConfig.locale);
- mDayFormat = new SimpleDateFormat("d", newConfig.locale);
+ setCurrentLocale(newConfig.locale);
}
@Override
@@ -529,9 +542,9 @@
int listPosition = -1;
int listPositionOffset = -1;
- if (mCurrentView == MONTH_AND_DAY_VIEW) {
+ if (mCurrentView == VIEW_MONTH_DAY) {
listPosition = mDayPickerView.getMostVisiblePosition();
- } else if (mCurrentView == YEAR_VIEW) {
+ } else if (mCurrentView == VIEW_YEAR) {
listPosition = mYearPickerView.getFirstVisiblePosition();
listPositionOffset = mYearPickerView.getFirstPositionOffset();
}
@@ -544,20 +557,22 @@
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
+ // TODO: Move instance state into DayPickerView, YearPickerView.
mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
mCurrentView = ss.getCurrentView();
mMinDate.setTimeInMillis(ss.getMinDate());
mMaxDate.setTimeInMillis(ss.getMaxDate());
- updateDisplay(false);
+ onCurrentDateChanged(false);
setCurrentView(mCurrentView);
final int listPosition = ss.getListPosition();
if (listPosition != -1) {
- if (mCurrentView == MONTH_AND_DAY_VIEW) {
- mDayPickerView.postSetSelection(listPosition);
- } else if (mCurrentView == YEAR_VIEW) {
- mYearPickerView.postSetSelectionFromTop(listPosition, ss.getListPositionOffset());
+ if (mCurrentView == VIEW_MONTH_DAY) {
+ mDayPickerView.setCurrentItem(listPosition);
+ } else if (mCurrentView == VIEW_YEAR) {
+ final int listPositionOffset = ss.getListPositionOffset();
+ mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
}
}
}
@@ -577,28 +592,6 @@
return DatePicker.class.getName();
}
- @Override
- public void onYearSelected(int year) {
- adjustDayInMonthIfNeeded(mCurrentDate.get(Calendar.MONTH), year);
- mCurrentDate.set(Calendar.YEAR, year);
- onDateChanged(true, true);
-
- // Auto-advance to month and day view.
- setCurrentView(MONTH_AND_DAY_VIEW);
- }
-
- // If the newly selected month / year does not contain the currently selected day number,
- // change the selected day number to the last day of the selected month or year.
- // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
- // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
- private void adjustDayInMonthIfNeeded(int month, int year) {
- int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
- int daysInMonth = getDaysInMonth(month, year);
- if (day > daysInMonth) {
- mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
- }
- }
-
public static int getDaysInMonth(int month, int year) {
switch (month) {
case Calendar.JANUARY:
@@ -621,43 +614,10 @@
}
}
- @Override
- public void registerOnDateChangedListener(OnDateChangedListener listener) {
- mListeners.add(listener);
- }
-
- @Override
- public Calendar getSelectedDay() {
- return mCurrentDate;
- }
-
- @Override
- public void tryVibrate() {
+ private void tryVibrate() {
mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE);
}
- @Override
- public void onClick(View v) {
- tryVibrate();
- if (v.getId() == R.id.date_picker_year) {
- setCurrentView(YEAR_VIEW);
- } else if (v.getId() == R.id.date_picker_month_and_day_layout) {
- setCurrentView(MONTH_AND_DAY_VIEW);
- }
- }
-
- /**
- * Listener called when the user selects a day in the day picker view.
- */
- private final DayPickerView.OnDaySelectedListener
- mOnDaySelectedListener = new DayPickerView.OnDaySelectedListener() {
- @Override
- public void onDaySelected(DayPickerView view, Calendar day) {
- mCurrentDate.setTimeInMillis(day.getTimeInMillis());
- onDateChanged(true, true);
- }
- };
-
/**
* Class for managing state storing/restoring.
*/
diff --git a/core/java/android/widget/DayPickerAdapter.java b/core/java/android/widget/DayPickerAdapter.java
new file mode 100644
index 0000000..4f9f09e
--- /dev/null
+++ b/core/java/android/widget/DayPickerAdapter.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import com.android.internal.widget.PagerAdapter;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SimpleMonthView.OnDayClickListener;
+
+import java.util.Calendar;
+
+/**
+ * An adapter for a list of {@link android.widget.SimpleMonthView} items.
+ */
+class DayPickerAdapter extends PagerAdapter {
+ private static final int MONTHS_IN_YEAR = 12;
+
+ private final Calendar mMinDate = Calendar.getInstance();
+ private final Calendar mMaxDate = Calendar.getInstance();
+
+ private final SparseArray<SimpleMonthView> mItems = new SparseArray<>();
+
+ private Calendar mSelectedDay = Calendar.getInstance();
+
+ private int mMonthTextAppearance;
+ private int mDayOfWeekTextAppearance;
+ private int mDayTextAppearance;
+
+ private ColorStateList mCalendarTextColor;
+ private ColorStateList mDaySelectorColor;
+ private ColorStateList mDayHighlightColor;
+
+ private OnDaySelectedListener mOnDaySelectedListener;
+
+ private int mFirstDayOfWeek;
+
+ public DayPickerAdapter(Context context) {
+ final TypedArray ta = context.obtainStyledAttributes(new int[] {
+ com.android.internal.R.attr.colorControlHighlight});
+ mDayHighlightColor = ta.getColorStateList(0);
+ ta.recycle();
+ }
+
+ public void setRange(Calendar min, Calendar max) {
+ mMinDate.setTimeInMillis(min.getTimeInMillis());
+ mMaxDate.setTimeInMillis(max.getTimeInMillis());
+
+ // Positions are now invalid, clear everything and start over.
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Sets the first day of the week.
+ *
+ * @param weekStart which day the week should start on, valid values are
+ * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+ */
+ public void setFirstDayOfWeek(int weekStart) {
+ mFirstDayOfWeek = weekStart;
+
+ // Update displayed views.
+ final int count = mItems.size();
+ for (int i = 0; i < count; i++) {
+ final SimpleMonthView monthView = mItems.valueAt(i);
+ monthView.setFirstDayOfWeek(weekStart);
+ }
+ }
+
+ public int getFirstDayOfWeek() {
+ return mFirstDayOfWeek;
+ }
+
+ /**
+ * Sets the selected day.
+ *
+ * @param day the selected day
+ */
+ public void setSelectedDay(Calendar day) {
+ final int oldPosition = getPositionForDay(mSelectedDay);
+ final int newPosition = getPositionForDay(day);
+
+ // Clear the old position if necessary.
+ if (oldPosition != newPosition) {
+ final SimpleMonthView oldMonthView = mItems.get(oldPosition, null);
+ if (oldMonthView != null) {
+ oldMonthView.setSelectedDay(-1);
+ }
+ }
+
+ // Set the new position.
+ final SimpleMonthView newMonthView = mItems.get(newPosition, null);
+ if (newMonthView != null) {
+ final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
+ newMonthView.setSelectedDay(dayOfMonth);
+ }
+
+ mSelectedDay = day;
+ }
+
+ /**
+ * Sets the listener to call when the user selects a day.
+ *
+ * @param listener The listener to call.
+ */
+ public void setOnDaySelectedListener(OnDaySelectedListener listener) {
+ mOnDaySelectedListener = listener;
+ }
+
+ void setCalendarTextColor(ColorStateList calendarTextColor) {
+ mCalendarTextColor = calendarTextColor;
+ }
+
+ void setDaySelectorColor(ColorStateList selectorColor) {
+ mDaySelectorColor = selectorColor;
+ }
+
+ void setMonthTextAppearance(int resId) {
+ mMonthTextAppearance = resId;
+ }
+
+ void setDayOfWeekTextAppearance(int resId) {
+ mDayOfWeekTextAppearance = resId;
+ }
+
+ int getDayOfWeekTextAppearance() {
+ return mDayOfWeekTextAppearance;
+ }
+
+ void setDayTextAppearance(int resId) {
+ mDayTextAppearance = resId;
+ }
+
+ int getDayTextAppearance() {
+ return mDayTextAppearance;
+ }
+
+ @Override
+ public int getCount() {
+ final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
+ final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
+ return diffMonth + MONTHS_IN_YEAR * diffYear + 1;
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == object;
+ }
+
+ private int getMonthForPosition(int position) {
+ return position % MONTHS_IN_YEAR + mMinDate.get(Calendar.MONTH);
+ }
+
+ private int getYearForPosition(int position) {
+ return position / MONTHS_IN_YEAR + mMinDate.get(Calendar.YEAR);
+ }
+
+ private int getPositionForDay(Calendar day) {
+ final int yearOffset = (day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
+ final int monthOffset = (day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH));
+ return yearOffset * MONTHS_IN_YEAR + monthOffset;
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ final SimpleMonthView v = new SimpleMonthView(container.getContext());
+ v.setOnDayClickListener(mOnDayClickListener);
+ v.setMonthTextAppearance(mMonthTextAppearance);
+ v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance);
+ v.setDayTextAppearance(mDayTextAppearance);
+
+ if (mDaySelectorColor != null) {
+ v.setDaySelectorColor(mDaySelectorColor);
+ }
+
+ if (mDayHighlightColor != null) {
+ v.setDayHighlightColor(mDayHighlightColor);
+ }
+
+ if (mCalendarTextColor != null) {
+ v.setMonthTextColor(mCalendarTextColor);
+ v.setDayOfWeekTextColor(mCalendarTextColor);
+ v.setDayTextColor(mCalendarTextColor);
+ }
+
+ final int month = getMonthForPosition(position);
+ final int year = getYearForPosition(position);
+
+ final int selectedDay;
+ if (mSelectedDay.get(Calendar.MONTH) == month) {
+ selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
+ } else {
+ selectedDay = -1;
+ }
+
+ final int enabledDayRangeStart;
+ if (mMinDate.get(Calendar.MONTH) == month && mMinDate.get(Calendar.YEAR) == year) {
+ enabledDayRangeStart = mMinDate.get(Calendar.DAY_OF_MONTH);
+ } else {
+ enabledDayRangeStart = 1;
+ }
+
+ final int enabledDayRangeEnd;
+ if (mMaxDate.get(Calendar.MONTH) == month && mMaxDate.get(Calendar.YEAR) == year) {
+ enabledDayRangeEnd = mMaxDate.get(Calendar.DAY_OF_MONTH);
+ } else {
+ enabledDayRangeEnd = 31;
+ }
+
+ v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
+ enabledDayRangeStart, enabledDayRangeEnd);
+
+ mItems.put(position, v);
+
+ container.addView(v);
+
+ return v;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ container.removeView(mItems.get(position));
+
+ mItems.remove(position);
+ }
+
+ @Override
+ public int getItemPosition(Object object) {
+ final int index = mItems.indexOfValue((SimpleMonthView) object);
+ if (index < 0) {
+ return mItems.keyAt(index);
+ }
+ return -1;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ final SimpleMonthView v = mItems.get(position);
+ if (v != null) {
+ return v.getTitle();
+ }
+ return null;
+ }
+
+ private boolean isCalendarInRange(Calendar value) {
+ return value.compareTo(mMinDate) >= 0 && value.compareTo(mMaxDate) <= 0;
+ }
+
+ private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() {
+ @Override
+ public void onDayClick(SimpleMonthView view, Calendar day) {
+ if (day != null && isCalendarInRange(day)) {
+ setSelectedDay(day);
+
+ if (mOnDaySelectedListener != null) {
+ mOnDaySelectedListener.onDaySelected(DayPickerAdapter.this, day);
+ }
+ }
+ }
+ };
+
+ public interface OnDaySelectedListener {
+ public void onDaySelected(DayPickerAdapter view, Calendar day);
+ }
+}
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 65af45d..a7ae926 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -16,87 +16,177 @@
package android.widget;
+import com.android.internal.widget.ViewPager;
+import com.android.internal.R;
+
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.util.Log;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
import android.util.MathUtils;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
+import libcore.icu.LocaleData;
+
/**
* This displays a list of months in a calendar format with selectable days.
*/
-class DayPickerView extends ListView implements AbsListView.OnScrollListener {
- private static final String TAG = "DayPickerView";
+class DayPickerView extends ViewPager {
+ private static final int DEFAULT_START_YEAR = 1900;
+ private static final int DEFAULT_END_YEAR = 2100;
- // How long the GoTo fling animation should last
- private static final int GOTO_SCROLL_DURATION = 250;
+ private final Calendar mSelectedDay = Calendar.getInstance();
+ private final Calendar mMinDate = Calendar.getInstance();
+ private final Calendar mMaxDate = Calendar.getInstance();
- // How long to wait after receiving an onScrollStateChanged notification before acting on it
- private static final int SCROLL_CHANGE_DELAY = 40;
+ private final DayPickerAdapter mAdapter;
- // so that the top line will be under the separator
- private static final int LIST_TOP_OFFSET = -1;
-
- private final SimpleMonthAdapter mAdapter = new SimpleMonthAdapter(getContext());
-
- private final ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(this);
-
- private SimpleDateFormat mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
-
- // highlighted time
- private Calendar mSelectedDay = Calendar.getInstance();
- private Calendar mTempDay = Calendar.getInstance();
- private Calendar mMinDate = Calendar.getInstance();
- private Calendar mMaxDate = Calendar.getInstance();
-
+ /** Temporary calendar used for date calculations. */
private Calendar mTempCalendar;
private OnDaySelectedListener mOnDaySelectedListener;
- // which month should be displayed/highlighted [0-11]
- private int mCurrentMonthDisplayed;
- // used for tracking what state listview is in
- private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
- // used for tracking what state listview is in
- private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-
- private boolean mPerformingScroll;
-
public DayPickerView(Context context) {
- super(context);
+ this(context, null);
+ }
+
+ public DayPickerView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.calendarViewStyle);
+ }
+
+ public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ 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 String minDate = a.getString(R.styleable.CalendarView_minDate);
+ final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
+
+ final int monthTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_monthTextAppearance,
+ R.style.TextAppearance_Material_Widget_Calendar_Month);
+ final int dayOfWeekTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_weekDayTextAppearance,
+ R.style.TextAppearance_Material_Widget_Calendar_DayOfWeek);
+ final int dayTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_dateTextAppearance,
+ R.style.TextAppearance_Material_Widget_Calendar_Day);
+
+ final ColorStateList daySelectorColor = a.getColorStateList(
+ R.styleable.CalendarView_daySelectorColor);
+
+ a.recycle();
+
+ // Set up adapter.
+ mAdapter = new DayPickerAdapter(context);
+ mAdapter.setMonthTextAppearance(monthTextAppearanceResId);
+ mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId);
+ mAdapter.setDayTextAppearance(dayTextAppearanceResId);
+ mAdapter.setDaySelectorColor(daySelectorColor);
setAdapter(mAdapter);
- setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- setDrawSelectorOnTop(false);
- setUpListView();
- goTo(mSelectedDay.getTimeInMillis(), false, false, true);
+ // Set up min and max dates.
+ final Calendar tempDate = Calendar.getInstance();
+ if (!CalendarView.parseDate(minDate, tempDate)) {
+ tempDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1);
+ }
+ final long minDateMillis = tempDate.getTimeInMillis();
- mAdapter.setOnDaySelectedListener(mProxyOnDaySelectedListener);
+ if (!CalendarView.parseDate(maxDate, tempDate)) {
+ tempDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31);
+ }
+ final long maxDateMillis = tempDate.getTimeInMillis();
+
+ if (maxDateMillis < minDateMillis) {
+ throw new IllegalArgumentException("maxDate must be >= minDate");
+ }
+
+ final long setDateMillis = MathUtils.constrain(
+ System.currentTimeMillis(), minDateMillis, maxDateMillis);
+
+ setFirstDayOfWeek(firstDayOfWeek);
+ setMinDate(minDateMillis);
+ setMaxDate(maxDateMillis);
+ setDate(setDateMillis, false);
+
+ // Proxy selection callbacks to our own listener.
+ mAdapter.setOnDaySelectedListener(new DayPickerAdapter.OnDaySelectedListener() {
+ @Override
+ public void onDaySelected(DayPickerAdapter adapter, Calendar day) {
+ if (mOnDaySelectedListener != null) {
+ mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
+ }
+ }
+ });
+ }
+
+ public void setDayOfWeekTextAppearance(int resId) {
+ mAdapter.setDayOfWeekTextAppearance(resId);
+ }
+
+ public int getDayOfWeekTextAppearance() {
+ return mAdapter.getDayOfWeekTextAppearance();
+ }
+
+ public void setDayTextAppearance(int resId) {
+ mAdapter.setDayTextAppearance(resId);
+ }
+
+ public int getDayTextAppearance() {
+ return mAdapter.getDayTextAppearance();
}
/**
* 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)}.
+ * {@link #setDate(long, boolean)}.
*
- * @param timeInMillis
+ * @param timeInMillis the target day in milliseconds
*/
public void setDate(long timeInMillis) {
- setDate(timeInMillis, false, true);
+ setDate(timeInMillis, false);
}
- public void setDate(long timeInMillis, boolean animate, boolean forceScroll) {
- goTo(timeInMillis, animate, true, forceScroll);
+ /**
+ * Sets the currently selected date to the specified timestamp. Jumps
+ * immediately to the new date, optionally animating the transition.
+ *
+ * @param timeInMillis the target day in milliseconds
+ * @param animate whether to smooth scroll to the new position
+ */
+ public void setDate(long timeInMillis, boolean animate) {
+ setDate(timeInMillis, animate, true);
+ }
+
+ /**
+ * Moves to the month containing the specified day, optionally setting the
+ * day as selected.
+ *
+ * @param timeInMillis the target day in milliseconds
+ * @param animate whether to smooth scroll to the new position
+ * @param setSelected whether to set the specified day as selected
+ */
+ private void setDate(long timeInMillis, boolean animate, boolean setSelected) {
+ // Set the selected day
+ if (setSelected) {
+ mSelectedDay.setTimeInMillis(timeInMillis);
+ }
+
+ final int position = getPositionFromDay(timeInMillis);
+ if (position != getCurrentItem()) {
+ setCurrentItem(position, animate);
+ }
}
public long getDate() {
@@ -137,7 +227,7 @@
// Changing the min/max date changes the selection position since we
// don't really have stable IDs. Jumps immediately to the new position.
- goTo(mSelectedDay.getTimeInMillis(), false, false, true);
+ setDate(mSelectedDay.getTimeInMillis(), false, false);
}
/**
@@ -149,30 +239,9 @@
mOnDaySelectedListener = listener;
}
- /*
- * Sets all the required fields for the list view. Override this method to
- * set a different list view behavior.
- */
- private void setUpListView() {
- // Transparent background on scroll
- setCacheColorHint(0);
- // No dividers
- setDivider(null);
- // Items are clickable
- setItemsCanFocus(true);
- // The thumb gets in the way, so disable it
- setFastScrollEnabled(false);
- setVerticalScrollBarEnabled(false);
- setOnScrollListener(this);
- setFadingEdgeLength(0);
- // Make the scrolling behavior nicer
- setFriction(ViewConfiguration.getScrollFriction());
- }
-
private int getDiffMonths(Calendar start, Calendar end) {
final int diffYears = end.get(Calendar.YEAR) - start.get(Calendar.YEAR);
- final int diffMonths = end.get(Calendar.MONTH) - start.get(Calendar.MONTH) + 12 * diffYears;
- return diffMonths;
+ return end.get(Calendar.MONTH) - start.get(Calendar.MONTH) + 12 * diffYears;
}
private int getPositionFromDay(long timeInMillis) {
@@ -190,366 +259,13 @@
}
/**
- * 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 day The day 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
- * @return Whether or not the view animated to the new location
- */
- private boolean goTo(long day, boolean animate, boolean setSelected, boolean forceScroll) {
-
- // Set the selected day
- if (setSelected) {
- mSelectedDay.setTimeInMillis(day);
- }
-
- mTempDay.setTimeInMillis(day);
- final int position = getPositionFromDay(day);
-
- View child;
- int i = 0;
- int top = 0;
- // Find a child that's completely in the view
- do {
- child = getChildAt(i++);
- if (child == null) {
- break;
- }
- top = child.getTop();
- } while (top < 0);
-
- // Compute the first and last position visible
- int selectedPosition;
- if (child != null) {
- selectedPosition = getPositionForView(child);
- } else {
- selectedPosition = 0;
- }
-
- if (setSelected) {
- mAdapter.setSelectedDay(mSelectedDay);
- }
-
- // Check if the selected day is now outside of our visible range
- // and if so scroll to the month that contains it
- if (position != selectedPosition || forceScroll) {
- setMonthDisplayed(mTempDay);
- mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
- if (animate) {
- smoothScrollToPositionFromTop(
- position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION);
- return true;
- } else {
- postSetSelection(position);
- }
- } else if (setSelected) {
- setMonthDisplayed(mSelectedDay);
- }
- return false;
- }
-
- public void postSetSelection(final int position) {
- clearFocus();
- post(new Runnable() {
-
- @Override
- public void run() {
- setSelection(position);
- }
- });
- onScrollStateChanged(this, OnScrollListener.SCROLL_STATE_IDLE);
- }
-
- /**
- * Updates the title and selected month if the view has moved to a new
- * month.
- */
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- SimpleMonthView child = (SimpleMonthView) view.getChildAt(0);
- if (child == null) {
- return;
- }
-
- 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.
- */
- protected void setMonthDisplayed(Calendar date) {
- if (mCurrentMonthDisplayed != date.get(Calendar.MONTH)) {
- mCurrentMonthDisplayed = date.get(Calendar.MONTH);
- invalidateViews();
- }
- }
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- // use a post to prevent re-entering onScrollStateChanged before it
- // exits
- mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
- }
-
- void setCalendarTextColor(ColorStateList colors) {
- mAdapter.setCalendarTextColor(colors);
- }
-
- void setCalendarDayBackgroundColor(ColorStateList dayBackgroundColor) {
- mAdapter.setCalendarDayBackgroundColor(dayBackgroundColor);
- }
-
- void setCalendarTextAppearance(int resId) {
- mAdapter.setCalendarTextAppearance(resId);
- }
-
- protected class ScrollStateRunnable implements Runnable {
- private int mNewState;
- private View mParent;
-
- ScrollStateRunnable(View view) {
- mParent = view;
- }
-
- /**
- * 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) {
- mParent.removeCallbacks(this);
- mNewState = scrollState;
- mParent.postDelayed(this, SCROLL_CHANGE_DELAY);
- }
-
- @Override
- public void run() {
- mCurrentScrollState = mNewState;
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG,
- "new scroll state: " + mNewState + " old state: " + mPreviousScrollState);
- }
- // Fix the position after a scroll or a fling ends
- if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
- && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE
- && mPreviousScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
- mPreviousScrollState = mNewState;
- int i = 0;
- View child = getChildAt(i);
- while (child != null && child.getBottom() <= 0) {
- child = getChildAt(++i);
- }
- if (child == null) {
- // The view is no longer visible, just return
- return;
- }
- int firstPosition = getFirstVisiblePosition();
- int lastPosition = getLastVisiblePosition();
- boolean scroll = firstPosition != 0 && lastPosition != getCount() - 1;
- final int top = child.getTop();
- final int bottom = child.getBottom();
- final int midpoint = getHeight() / 2;
- if (scroll && top < LIST_TOP_OFFSET) {
- if (bottom > midpoint) {
- smoothScrollBy(top, GOTO_SCROLL_DURATION);
- } else {
- smoothScrollBy(bottom, GOTO_SCROLL_DURATION);
- }
- }
- } else {
- mPreviousScrollState = mNewState;
- }
- }
- }
-
- /**
* Gets the position of the view that is most prominently displayed within the list view.
*/
public int getMostVisiblePosition() {
- final int firstPosition = getFirstVisiblePosition();
- final int height = getHeight();
-
- int maxDisplayedHeight = 0;
- int mostVisibleIndex = 0;
- int i=0;
- int bottom = 0;
- while (bottom < height) {
- View child = getChildAt(i);
- if (child == null) {
- break;
- }
- bottom = child.getBottom();
- int displayedHeight = Math.min(bottom, height) - Math.max(0, child.getTop());
- if (displayedHeight > maxDisplayedHeight) {
- mostVisibleIndex = i;
- maxDisplayedHeight = displayedHeight;
- }
- i++;
- }
- return firstPosition + mostVisibleIndex;
- }
-
- /**
- * Attempts to return the date that has accessibility focus.
- *
- * @return The date that has accessibility focus, or {@code null} if no date
- * has focus.
- */
- private Calendar findAccessibilityFocus() {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child instanceof SimpleMonthView) {
- final Calendar focus = ((SimpleMonthView) child).getAccessibilityFocus();
- if (focus != null) {
- return focus;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Attempts to restore accessibility focus to a given date. No-op if
- * {@code day} is {@code null}.
- *
- * @param day The date that should receive accessibility focus
- * @return {@code true} if focus was restored
- */
- private boolean restoreAccessibilityFocus(Calendar day) {
- if (day == null) {
- return false;
- }
-
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child instanceof SimpleMonthView) {
- if (((SimpleMonthView) child).restoreAccessibilityFocus(day)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- @Override
- protected void layoutChildren() {
- final Calendar focusedDay = findAccessibilityFocus();
- super.layoutChildren();
- if (mPerformingScroll) {
- mPerformingScroll = false;
- } else {
- restoreAccessibilityFocus(focusedDay);
- }
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
- }
-
- /** @hide */
- @Override
- public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
- super.onInitializeAccessibilityEventInternal(event);
- event.setItemCount(-1);
- }
-
- private String getMonthAndYearString(Calendar day) {
- final StringBuilder sbuf = new StringBuilder();
- sbuf.append(day.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()));
- sbuf.append(" ");
- sbuf.append(mYearFormat.format(day.getTime()));
- return sbuf.toString();
- }
-
- /**
- * Necessary for accessibility, to ensure we support "scrolling" forward and backward
- * in the month list.
- *
- * @hide
- */
- @Override
- public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfoInternal(info);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
- }
-
- /**
- * When scroll forward/backward events are received, announce the newly scrolled-to month.
- *
- * @hide
- */
- @Override
- public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
- if (action != AccessibilityNodeInfo.ACTION_SCROLL_FORWARD &&
- action != AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
- return super.performAccessibilityActionInternal(action, arguments);
- }
-
- // Figure out what month is showing.
- final int firstVisiblePosition = getFirstVisiblePosition();
- final int month = firstVisiblePosition % 12;
- final int year = firstVisiblePosition / 12 + mMinDate.get(Calendar.YEAR);
- final Calendar day = Calendar.getInstance();
- day.set(year, month, 1);
-
- // Scroll either forward or backward one month.
- if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) {
- day.add(Calendar.MONTH, 1);
- if (day.get(Calendar.MONTH) == 12) {
- day.set(Calendar.MONTH, 0);
- day.add(Calendar.YEAR, 1);
- }
- } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
- View firstVisibleView = getChildAt(0);
- // If the view is fully visible, jump one month back. Otherwise, we'll just jump
- // to the first day of first visible month.
- if (firstVisibleView != null && firstVisibleView.getTop() >= -1) {
- // There's an off-by-one somewhere, so the top of the first visible item will
- // actually be -1 when it's at the exact top.
- day.add(Calendar.MONTH, -1);
- if (day.get(Calendar.MONTH) == -1) {
- day.set(Calendar.MONTH, 11);
- day.add(Calendar.YEAR, -1);
- }
- }
- }
-
- // Go to that month.
- announceForAccessibility(getMonthAndYearString(day));
- goTo(day.getTimeInMillis(), true, false, true);
- mPerformingScroll = true;
- return true;
+ return getCurrentItem();
}
public interface OnDaySelectedListener {
public void onDaySelected(DayPickerView view, Calendar day);
}
-
- private final SimpleMonthAdapter.OnDaySelectedListener
- mProxyOnDaySelectedListener = new SimpleMonthAdapter.OnDaySelectedListener() {
- @Override
- public void onDaySelected(SimpleMonthAdapter adapter, Calendar day) {
- if (mOnDaySelectedListener != null) {
- mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
- }
- }
- };
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 3fca463..87fcd81 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3348,7 +3348,7 @@
// Minimum touch target size for handles
private int mMinSize;
// Indicates the line of text that the handle is on.
- protected int mLine = -1;
+ protected int mPrevLine = -1;
public HandleView(Drawable drawableLtr, Drawable drawableRtl) {
super(mTextView.getContext());
@@ -3499,7 +3499,7 @@
addPositionToTouchUpFilter(offset);
}
final int line = layout.getLineForOffset(offset);
- mLine = line;
+ mPrevLine = line;
mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX -
getHorizontalOffset() + getCursorOffset());
@@ -3839,19 +3839,22 @@
public void updatePosition(float x, float y) {
final int trueOffset = mTextView.getOffsetForPosition(x, y);
final int currLine = mTextView.getLineAtCoordinate(y);
- int offset = trueOffset;
- boolean positionCursor = false;
+ // Don't select white space on different lines.
+ if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
+
+ boolean positionCursor = false;
+ int offset = trueOffset;
int end = getWordEnd(offset, true);
int start = getWordStart(offset);
if (offset < mPrevOffset) {
// User is increasing the selection.
- if (!mInWord || currLine < mLine) {
+ if (!mInWord || currLine < mPrevLine) {
// We're not in a word, or we're on a different line so we'll expand by
// word. First ensure the user has at least entered the next word.
int offsetToWord = Math.min((end - start) / 2, 2);
- if (offset <= end - offsetToWord || currLine < mLine) {
+ if (offset <= end - offsetToWord || currLine < mPrevLine) {
offset = start;
} else {
offset = mPrevOffset;
@@ -3863,7 +3866,7 @@
positionCursor = true;
} else if (offset - mTouchWordOffset > mPrevOffset) {
// User is shrinking the selection.
- if (currLine > mLine) {
+ if (currLine > mPrevLine) {
// We're on a different line, so we'll snap to word boundaries.
offset = end;
}
@@ -3878,7 +3881,7 @@
final int selectionEnd = mTextView.getSelectionEnd();
if (offset >= selectionEnd) {
// We can't cross the handles so let's just constrain the Y value.
- int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x);
+ int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x);
if (alteredOffset >= selectionEnd) {
// Can't pass the other drag handle.
offset = Math.max(0, selectionEnd - 1);
@@ -3939,6 +3942,10 @@
public void updatePosition(float x, float y) {
final int trueOffset = mTextView.getOffsetForPosition(x, y);
final int currLine = mTextView.getLineAtCoordinate(y);
+
+ // Don't select white space on different lines.
+ if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
+
int offset = trueOffset;
boolean positionCursor = false;
@@ -3947,11 +3954,11 @@
if (offset > mPrevOffset) {
// User is increasing the selection.
- if (!mInWord || currLine > mLine) {
+ if (!mInWord || currLine > mPrevLine) {
// We're not in a word, or we're on a different line so we'll expand by
// word. First ensure the user has at least entered the next word.
int midPoint = Math.min((end - start) / 2, 2);
- if (offset >= start + midPoint || currLine > mLine) {
+ if (offset >= start + midPoint || currLine > mPrevLine) {
offset = end;
} else {
offset = mPrevOffset;
@@ -3963,7 +3970,7 @@
positionCursor = true;
} else if (offset + mTouchWordOffset < mPrevOffset) {
// User is shrinking the selection.
- if (currLine > mLine) {
+ if (currLine > mPrevLine) {
// We're on a different line, so we'll snap to word boundaries.
offset = getWordStart(offset);
}
@@ -3977,7 +3984,7 @@
final int selectionStart = mTextView.getSelectionStart();
if (offset <= selectionStart) {
// We can't cross the handles so let's just constrain the Y value.
- int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x);
+ int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x);
int length = mTextView.getText().length();
if (alteredOffset <= selectionStart) {
// Can't pass the other drag handle.
@@ -4002,6 +4009,36 @@
}
/**
+ * Checks whether selection is happening on a different line than previous and
+ * if that line only contains whitespace up to the touch location.
+ *
+ * @param prevLine The previous line the selection was on.
+ * @param currLine The current line being selected.
+ * @param offset The offset in the text where the touch occurred.
+ * @return Whether or not it was just a white space line being selected.
+ */
+ private boolean isWhitespaceLine(int prevLine, int currLine, int offset) {
+ if (prevLine == currLine) {
+ // Same line; don't care.
+ return false;
+ }
+ CharSequence text = mTextView.getText();
+ if (offset == text.length()) {
+ // No character at the last position.
+ return false;
+ }
+ int lineEndOffset = mTextView.getLayout().getLineEnd(currLine);
+ for (int cp, i = offset; i < lineEndOffset; i += Character.charCount(cp)) {
+ cp = Character.codePointAt(text, i);
+ if (!Character.isSpaceChar(cp) && !Character.isWhitespace(cp)) {
+ // There are non white space chars on the line.
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* A CursorController instance can be used to control a cursor in the text.
*/
private interface CursorController extends ViewTreeObserver.OnTouchModeChangeListener {
@@ -4081,6 +4118,8 @@
private int mStartOffset = -1;
// Indicates whether the user is selecting text and using the drag accelerator.
private boolean mDragAcceleratorActive;
+ // Indicates the line of text the drag accelerator is on.
+ private int mPrevLine = -1;
SelectionModifierCursorController() {
resetTouchOffsets();
@@ -4173,6 +4212,8 @@
}
}
+ // New selection, reset line.
+ mPrevLine = mTextView.getLineAtCoordinate(y);
mDownPositionX = x;
mDownPositionY = y;
mGestureStayedInTapRegion = true;
@@ -4229,6 +4270,13 @@
if (my > fingerOffset) my -= fingerOffset;
offset = mTextView.getOffsetForPosition(mx, my);
+ int currLine = mTextView.getLineAtCoordinate(my);
+
+ // Don't select white space on different lines.
+ if (isWhitespaceLine(mPrevLine, currLine, offset)) return;
+
+ mPrevLine = currLine;
+
// Perform the check for closeness at edge of view, if we're very close
// don't adjust the offset to be in front of the finger - otherwise the
// user can't select words at the edge.
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index f6d198b..0602944 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -53,8 +53,6 @@
* only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()}
* is set to true.
*
- * @attr ref android.R.styleable#FrameLayout_foreground
- * @attr ref android.R.styleable#FrameLayout_foregroundGravity
* @attr ref android.R.styleable#FrameLayout_measureAllChildren
*/
@RemoteView
@@ -64,13 +62,6 @@
@ViewDebug.ExportedProperty(category = "measurement")
boolean mMeasureAllChildren = false;
- @ViewDebug.ExportedProperty(category = "drawing")
- private Drawable mForeground;
- private ColorStateList mForegroundTintList = null;
- private PorterDuff.Mode mForegroundTintMode = null;
- private boolean mHasForegroundTint = false;
- private boolean mHasForegroundTintMode = false;
-
@ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingLeft = 0;
@@ -85,15 +76,6 @@
private final Rect mSelfBounds = new Rect();
private final Rect mOverlayBounds = new Rect();
-
- @ViewDebug.ExportedProperty(category = "drawing")
- private int mForegroundGravity = Gravity.FILL;
-
- /** {@hide} */
- @ViewDebug.ExportedProperty(category = "drawing")
- protected boolean mForegroundInPadding = true;
-
- boolean mForegroundBoundsChanged = false;
private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1);
@@ -115,48 +97,12 @@
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.FrameLayout, defStyleAttr, defStyleRes);
-
- mForegroundGravity = a.getInt(
- com.android.internal.R.styleable.FrameLayout_foregroundGravity, mForegroundGravity);
-
- final Drawable d = a.getDrawable(com.android.internal.R.styleable.FrameLayout_foreground);
- if (d != null) {
- setForeground(d);
- }
if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) {
setMeasureAllChildren(true);
}
- if (a.hasValue(R.styleable.FrameLayout_foregroundTintMode)) {
- mForegroundTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.FrameLayout_foregroundTintMode, -1), mForegroundTintMode);
- mHasForegroundTintMode = true;
- }
-
- if (a.hasValue(R.styleable.FrameLayout_foregroundTint)) {
- mForegroundTintList = a.getColorStateList(R.styleable.FrameLayout_foregroundTint);
- mHasForegroundTint = true;
- }
-
- mForegroundInPadding = a.getBoolean(R.styleable.FrameLayout_foregroundInsidePadding, true);
-
a.recycle();
-
- applyForegroundTint();
- }
-
- /**
- * Describes how the foreground is positioned.
- *
- * @return foreground gravity.
- *
- * @see #setForegroundGravity(int)
- *
- * @attr ref android.R.styleable#FrameLayout_foregroundGravity
- */
- public int getForegroundGravity() {
- return mForegroundGravity;
}
/**
@@ -166,25 +112,18 @@
*
* @see #getForegroundGravity()
*
- * @attr ref android.R.styleable#FrameLayout_foregroundGravity
+ * @attr ref android.R.styleable#View_foregroundGravity
*/
@android.view.RemotableViewMethod
public void setForegroundGravity(int foregroundGravity) {
- if (mForegroundGravity != foregroundGravity) {
- if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
- foregroundGravity |= Gravity.START;
- }
+ if (getForegroundGravity() != foregroundGravity) {
+ super.setForegroundGravity(foregroundGravity);
- if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
- foregroundGravity |= Gravity.TOP;
- }
-
- mForegroundGravity = foregroundGravity;
-
-
- if (mForegroundGravity == Gravity.FILL && mForeground != null) {
+ // calling get* again here because the set above may apply default constraints
+ final Drawable foreground = getForeground();
+ if (getForegroundGravity() == Gravity.FILL && foreground != null) {
Rect padding = new Rect();
- if (mForeground.getPadding(padding)) {
+ if (foreground.getPadding(padding)) {
mForegroundPaddingLeft = padding.left;
mForegroundPaddingTop = padding.top;
mForegroundPaddingRight = padding.right;
@@ -201,53 +140,6 @@
}
}
- @Override
- protected void onVisibilityChanged(@NonNull View changedView, @Visibility int visibility) {
- super.onVisibilityChanged(changedView, visibility);
-
- final Drawable dr = mForeground;
- if (dr != null) {
- final boolean visible = visibility == VISIBLE && getVisibility() == VISIBLE;
- if (visible != dr.isVisible()) {
- dr.setVisible(visible, false);
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return super.verifyDrawable(who) || (who == mForeground);
- }
-
- @Override
- public void jumpDrawablesToCurrentState() {
- super.jumpDrawablesToCurrentState();
- if (mForeground != null) mForeground.jumpToCurrentState();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void drawableStateChanged() {
- super.drawableStateChanged();
- if (mForeground != null && mForeground.isStateful()) {
- mForeground.setState(getDrawableState());
- }
- }
-
- @Override
- public void drawableHotspotChanged(float x, float y) {
- super.drawableHotspotChanged(x, y);
-
- if (mForeground != null) {
- mForeground.setHotspot(x, y);
- }
- }
-
/**
* Returns a set of layout parameters with a width of
* {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT},
@@ -258,161 +150,23 @@
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
- /**
- * Supply a Drawable that is to be rendered on top of all of the child
- * views in the frame layout. Any padding in the Drawable will be taken
- * into account by ensuring that the children are inset to be placed
- * inside of the padding area.
- *
- * @param d The Drawable to be drawn on top of the children.
- *
- * @attr ref android.R.styleable#FrameLayout_foreground
- */
- public void setForeground(Drawable d) {
- if (mForeground != d) {
- if (mForeground != null) {
- mForeground.setCallback(null);
- unscheduleDrawable(mForeground);
- }
-
- mForeground = d;
- mForegroundPaddingLeft = 0;
- mForegroundPaddingTop = 0;
- mForegroundPaddingRight = 0;
- mForegroundPaddingBottom = 0;
-
- if (d != null) {
- setWillNotDraw(false);
- d.setCallback(this);
- d.setLayoutDirection(getLayoutDirection());
- if (d.isStateful()) {
- d.setState(getDrawableState());
- }
- applyForegroundTint();
- if (mForegroundGravity == Gravity.FILL) {
- Rect padding = new Rect();
- if (d.getPadding(padding)) {
- mForegroundPaddingLeft = padding.left;
- mForegroundPaddingTop = padding.top;
- mForegroundPaddingRight = padding.right;
- mForegroundPaddingBottom = padding.bottom;
- }
- }
- } else {
- setWillNotDraw(true);
- }
- requestLayout();
- invalidate();
- }
- }
-
- /**
- * Returns the drawable used as the foreground of this FrameLayout. The
- * foreground drawable, if non-null, is always drawn on top of the children.
- *
- * @return A Drawable or null if no foreground was set.
- */
- public Drawable getForeground() {
- return mForeground;
- }
-
- /**
- * Applies a tint to the foreground drawable. Does not modify the current
- * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
- * <p>
- * Subsequent calls to {@link #setForeground(Drawable)} will automatically
- * mutate the drawable and apply the specified tint and tint mode using
- * {@link Drawable#setTintList(ColorStateList)}.
- *
- * @param tint the tint to apply, may be {@code null} to clear tint
- *
- * @attr ref android.R.styleable#FrameLayout_foregroundTint
- * @see #getForegroundTintList()
- * @see Drawable#setTintList(ColorStateList)
- */
- public void setForegroundTintList(@Nullable ColorStateList tint) {
- mForegroundTintList = tint;
- mHasForegroundTint = true;
-
- applyForegroundTint();
- }
-
- /**
- * @return the tint applied to the foreground drawable
- * @attr ref android.R.styleable#FrameLayout_foregroundTint
- * @see #setForegroundTintList(ColorStateList)
- */
- @Nullable
- public ColorStateList getForegroundTintList() {
- return mForegroundTintList;
- }
-
- /**
- * Specifies the blending mode used to apply the tint specified by
- * {@link #setForegroundTintList(ColorStateList)}} to the foreground drawable.
- * The default mode is {@link PorterDuff.Mode#SRC_IN}.
- *
- * @param tintMode the blending mode used to apply the tint, may be
- * {@code null} to clear tint
- * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
- * @see #getForegroundTintMode()
- * @see Drawable#setTintMode(PorterDuff.Mode)
- */
- public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
- mForegroundTintMode = tintMode;
- mHasForegroundTintMode = true;
-
- applyForegroundTint();
- }
-
- /**
- * @return the blending mode used to apply the tint to the foreground
- * drawable
- * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
- * @see #setForegroundTintMode(PorterDuff.Mode)
- */
- @Nullable
- public PorterDuff.Mode getForegroundTintMode() {
- return mForegroundTintMode;
- }
-
- private void applyForegroundTint() {
- if (mForeground != null && (mHasForegroundTint || mHasForegroundTintMode)) {
- mForeground = mForeground.mutate();
-
- if (mHasForegroundTint) {
- mForeground.setTintList(mForegroundTintList);
- }
-
- if (mHasForegroundTintMode) {
- mForeground.setTintMode(mForegroundTintMode);
- }
-
- // The drawable (or one of its children) may not have been
- // stateful before applying the tint, so let's try again.
- if (mForeground.isStateful()) {
- mForeground.setState(getDrawableState());
- }
- }
- }
-
int getPaddingLeftWithForeground() {
- return mForegroundInPadding ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
+ return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
mPaddingLeft + mForegroundPaddingLeft;
}
int getPaddingRightWithForeground() {
- return mForegroundInPadding ? Math.max(mPaddingRight, mForegroundPaddingRight) :
+ return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) :
mPaddingRight + mForegroundPaddingRight;
}
private int getPaddingTopWithForeground() {
- return mForegroundInPadding ? Math.max(mPaddingTop, mForegroundPaddingTop) :
+ return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) :
mPaddingTop + mForegroundPaddingTop;
}
private int getPaddingBottomWithForeground() {
- return mForegroundInPadding ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
+ return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
mPaddingBottom + mForegroundPaddingBottom;
}
@@ -527,8 +281,6 @@
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
- mForegroundBoundsChanged = true;
-
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
@@ -585,62 +337,6 @@
}
/**
- * {@inheritDoc}
- */
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mForegroundBoundsChanged = true;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
-
- if (mForeground != null) {
- final Drawable foreground = mForeground;
-
- if (mForegroundBoundsChanged) {
- mForegroundBoundsChanged = false;
- final Rect selfBounds = mSelfBounds;
- final Rect overlayBounds = mOverlayBounds;
-
- final int w = mRight-mLeft;
- final int h = mBottom-mTop;
-
- if (mForegroundInPadding) {
- selfBounds.set(0, 0, w, h);
- } else {
- selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
- }
-
- final int layoutDirection = getLayoutDirection();
- Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
- foreground.getIntrinsicHeight(), selfBounds, overlayBounds,
- layoutDirection);
- foreground.setBounds(overlayBounds);
- }
-
- foreground.draw(canvas);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean gatherTransparentRegion(Region region) {
- boolean opaque = super.gatherTransparentRegion(region);
- if (region != null && mForeground != null) {
- applyDrawableToTransparentRegion(mForeground, region);
- }
- return opaque;
- }
-
- /**
* Sets whether to consider all children, or just those in
* the VISIBLE or INVISIBLE state, when measuring. Defaults to false.
*
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index d85bbb9..310412f9 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -567,13 +567,11 @@
public void show() {
int height = buildDropDown();
- int widthSpec = 0;
- int heightSpec = 0;
-
boolean noInputMethod = isInputMethodNotNeeded();
mPopup.setAllowScrollingAnchorParent(!noInputMethod);
if (mPopup.isShowing()) {
+ final int widthSpec;
if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
// The call to PopupWindow's update method below can accept -1 for any
// value you do not want to update.
@@ -584,19 +582,19 @@
widthSpec = mDropDownWidth;
}
+ final int heightSpec;
if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
// The call to PopupWindow's update method below can accept -1 for any
// value you do not want to update.
heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
if (noInputMethod) {
- mPopup.setWindowLayoutMode(
- mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
- ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
+ mPopup.setWidth(mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+ ViewGroup.LayoutParams.MATCH_PARENT : 0);
+ mPopup.setHeight(0);
} else {
- mPopup.setWindowLayoutMode(
- mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
- ViewGroup.LayoutParams.MATCH_PARENT : 0,
- ViewGroup.LayoutParams.MATCH_PARENT);
+ mPopup.setWidth(mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+ ViewGroup.LayoutParams.MATCH_PARENT : 0);
+ mPopup.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
}
} else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
heightSpec = height;
@@ -604,32 +602,37 @@
heightSpec = mDropDownHeight;
}
+ mPopup.setWidth(widthSpec);
+ mPopup.setHeight(heightSpec);
mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
mPopup.update(getAnchorView(), mDropDownHorizontalOffset,
- mDropDownVerticalOffset, widthSpec, heightSpec);
+ mDropDownVerticalOffset, -1, -1);
} else {
+ final int widthSpec;
if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
} else {
if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
- mPopup.setWidth(getAnchorView().getWidth());
+ widthSpec = getAnchorView().getWidth();
} else {
- mPopup.setWidth(mDropDownWidth);
+ widthSpec = mDropDownWidth;
}
}
+ final int heightSpec;
if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
} else {
if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
- mPopup.setHeight(height);
+ heightSpec = height;
} else {
- mPopup.setHeight(mDropDownHeight);
+ heightSpec = mDropDownHeight;
}
}
- mPopup.setWindowLayoutMode(widthSpec, heightSpec);
+ mPopup.setWidth(widthSpec);
+ mPopup.setHeight(heightSpec);
mPopup.setClipToScreenEnabled(true);
// use outside touchable to dismiss drop down when touching outside of it, so
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index f676254..8792323 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -46,6 +46,7 @@
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import java.lang.ref.WeakReference;
@@ -126,10 +127,10 @@
private OnTouchListener mTouchInterceptor;
private int mWidthMode;
- private int mWidth;
+ private int mWidth = LayoutParams.WRAP_CONTENT;
private int mLastWidth;
private int mHeightMode;
- private int mHeight;
+ private int mHeight = LayoutParams.WRAP_CONTENT;
private int mLastHeight;
private int mPopupWidth;
@@ -907,17 +908,19 @@
* {@link ViewGroup.LayoutParams#WRAP_CONTENT},
* {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
* height.
+ *
+ * @deprecated Use {@link #setWidth(int)} and {@link #setHeight(int)}.
*/
+ @Deprecated
public void setWindowLayoutMode(int widthSpec, int heightSpec) {
mWidthMode = widthSpec;
mHeightMode = heightSpec;
}
/**
- * <p>Return this popup's height MeasureSpec</p>
+ * Returns the popup's height MeasureSpec.
*
* @return the height MeasureSpec of the popup
- *
* @see #setHeight(int)
*/
public int getHeight() {
@@ -925,13 +928,12 @@
}
/**
- * <p>Change the popup's height MeasureSpec</p>
- *
- * <p>If the popup is showing, calling this method will take effect only
- * the next time the popup is shown.</p>
+ * Sets the popup's height MeasureSpec.
+ * <p>
+ * If the popup is showing, calling this method will take effect the next
+ * time the popup is shown.
*
* @param height the height MeasureSpec of the popup
- *
* @see #getHeight()
* @see #isShowing()
*/
@@ -940,10 +942,9 @@
}
/**
- * <p>Return this popup's width MeasureSpec</p>
+ * Returns the popup's width MeasureSpec.
*
* @return the width MeasureSpec of the popup
- *
* @see #setWidth(int)
*/
public int getWidth() {
@@ -951,13 +952,12 @@
}
/**
- * <p>Change the popup's width MeasureSpec</p>
- *
- * <p>If the popup is showing, calling this method will take effect only
- * the next time the popup is shown.</p>
+ * Sets the popup's width MeasureSpec.
+ * <p>
+ * If the popup is showing, calling this method will take effect the next
+ * time the popup is shown.
*
* @param width the width MeasureSpec of the popup
- *
* @see #getWidth()
* @see #isShowing()
*/
@@ -1658,10 +1658,17 @@
/**
* Updates the state of the popup window, if it is currently being displayed,
- * from the currently set state. This includes:
- * {@link #setClippingEnabled(boolean)}, {@link #setFocusable(boolean)},
- * {@link #setIgnoreCheekPress()}, {@link #setInputMethodMode(int)},
- * {@link #setTouchable(boolean)}, and {@link #setAnimationStyle(int)}.
+ * from the currently set state.
+ * <p>
+ * This includes:
+ * <ul>
+ * <li>{@link #setClippingEnabled(boolean)}</li>
+ * <li>{@link #setFocusable(boolean)}</li>
+ * <li>{@link #setIgnoreCheekPress()}</li>
+ * <li>{@link #setInputMethodMode(int)}</li>
+ * <li>{@link #setTouchable(boolean)}</li>
+ * <li>{@link #setAnimationStyle(int)}</li>
+ * </ul>
*/
public void update() {
if (!isShowing() || mContentView == null) {
@@ -1692,12 +1699,13 @@
}
/**
- * <p>Updates the dimension of the popup window. Calling this function
- * also updates the window with the current popup state as described
- * for {@link #update()}.</p>
+ * Updates the dimension of the popup window.
+ * <p>
+ * Calling this function also updates the window with the current popup
+ * state as described for {@link #update()}.
*
- * @param width the new width
- * @param height the new height
+ * @param width the new width, must be >= 0 or -1 to ignore
+ * @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(int width, int height) {
final WindowManager.LayoutParams p =
@@ -1706,40 +1714,43 @@
}
/**
- * <p>Updates the position and the dimension of the popup window. Width and
- * height can be set to -1 to update location only. Calling this function
- * also updates the window with the current popup state as
- * described for {@link #update()}.</p>
+ * Updates the position and the dimension of the popup window.
+ * <p>
+ * Width and height can be set to -1 to update location only. Calling this
+ * function also updates the window with the current popup state as
+ * described for {@link #update()}.
*
* @param x the new x location
* @param y the new y location
- * @param width the new width, can be -1 to ignore
- * @param height the new height, can be -1 to ignore
+ * @param width the new width, must be >= 0 or -1 to ignore
+ * @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(int x, int y, int width, int height) {
update(x, y, width, height, false);
}
/**
- * <p>Updates the position and the dimension of the popup window. Width and
- * height can be set to -1 to update location only. Calling this function
- * also updates the window with the current popup state as
- * described for {@link #update()}.</p>
+ * Updates the position and the dimension of the popup window.
+ * <p>
+ * Width and height can be set to -1 to update location only. Calling this
+ * function also updates the window with the current popup state as
+ * described for {@link #update()}.
*
* @param x the new x location
* @param y the new y location
- * @param width the new width, can be -1 to ignore
- * @param height the new height, can be -1 to ignore
- * @param force reposition the window even if the specified position
- * already seems to correspond to the LayoutParams
+ * @param width the new width, must be >= 0 or -1 to ignore
+ * @param height the new height, must be >= 0 or -1 to ignore
+ * @param force {@code true} to reposition the window even if the specified
+ * position already seems to correspond to the LayoutParams,
+ * {@code false} to only reposition if needed
*/
public void update(int x, int y, int width, int height, boolean force) {
- if (width != -1) {
+ if (width >= 0) {
mLastWidth = width;
setWidth(width);
}
- if (height != -1) {
+ if (height >= 0) {
mLastHeight = height;
setHeight(height);
}
@@ -1794,32 +1805,34 @@
}
/**
- * <p>Updates the position and the dimension of the popup window. Calling this
- * function also updates the window with the current popup state as described
- * for {@link #update()}.</p>
+ * Updates the position and the dimension of the popup window.
+ * <p>
+ * Calling this function also updates the window with the current popup
+ * state as described for {@link #update()}.
*
* @param anchor the popup's anchor view
- * @param width the new width, can be -1 to ignore
- * @param height the new height, can be -1 to ignore
+ * @param width the new width, must be >= 0 or -1 to ignore
+ * @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(View anchor, int width, int height) {
update(anchor, false, 0, 0, true, width, height);
}
/**
- * <p>Updates the position and the dimension of the popup window. Width and
- * height can be set to -1 to update location only. Calling this function
- * also updates the window with the current popup state as
- * described for {@link #update()}.</p>
- *
- * <p>If the view later scrolls to move <code>anchor</code> to a different
- * location, the popup will be moved correspondingly.</p>
+ * Updates the position and the dimension of the popup window.
+ * <p>
+ * Width and height can be set to -1 to update location only. Calling this
+ * function also updates the window with the current popup state as
+ * described for {@link #update()}.
+ * <p>
+ * If the view later scrolls to move {@code anchor} to a different
+ * location, the popup will be moved correspondingly.
*
* @param anchor the popup's anchor view
* @param xoff x offset from the view's left edge
* @param yoff y offset from the view's bottom edge
- * @param width the new width, can be -1 to ignore
- * @param height the new height, can be -1 to ignore
+ * @param width the new width, must be >= 0 or -1 to ignore
+ * @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(View anchor, int xoff, int yoff, int width, int height) {
update(anchor, true, xoff, yoff, true, width, height);
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 28b4db2..20aa972 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -641,7 +641,7 @@
mCircleRadius = Math.min(mXCenter, mYCenter);
mMinHypotenuseForInnerNumber = mCircleRadius - mTextInset[HOURS_INNER] - mSelectorRadius;
- mMaxHypotenuseForOuterNumber = mCircleRadius - mTextInset[HOURS] - mSelectorRadius;
+ mMaxHypotenuseForOuterNumber = mCircleRadius - mTextInset[HOURS] + mSelectorRadius;
mHalfwayHypotenusePoint = mCircleRadius - (mTextInset[HOURS] + mTextInset[HOURS_INNER]) / 2;
calculatePositionsHours();
@@ -1144,30 +1144,31 @@
private void adjustPicker(int step) {
final int stepSize;
- final int initialValue;
+ final int initialStep;
final int maxValue;
final int minValue;
if (mShowHours) {
- stepSize = DEGREES_FOR_ONE_HOUR;
- initialValue = getCurrentHour() % 12;
+ stepSize = 1;
+ final int currentHour24 = getCurrentHour();
if (mIs24HourMode) {
- maxValue = 23;
+ initialStep = currentHour24;
minValue = 0;
+ maxValue = 23;
} else {
- maxValue = 12;
+ initialStep = hour24To12(currentHour24);
minValue = 1;
+ maxValue = 12;
}
} else {
- stepSize = DEGREES_FOR_ONE_MINUTE;
- initialValue = getCurrentMinute();
-
- maxValue = 55;
+ stepSize = 5;
+ initialStep = getCurrentMinute() / stepSize;
minValue = 0;
+ maxValue = 55;
}
- final int steppedValue = snapOnly30s(initialValue * stepSize, step) / stepSize;
- final int clampedValue = MathUtils.constrain(steppedValue, minValue, maxValue);
+ final int nextValue = (initialStep + step) * stepSize;
+ final int clampedValue = MathUtils.constrain(nextValue, minValue, maxValue);
if (mShowHours) {
setCurrentHour(clampedValue);
} else {
diff --git a/core/java/android/widget/SimpleMonthAdapter.java b/core/java/android/widget/SimpleMonthAdapter.java
deleted file mode 100644
index c807d56..0000000
--- a/core/java/android/widget/SimpleMonthAdapter.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * 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.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.SimpleMonthView.OnDayClickListener;
-
-import java.util.Calendar;
-
-/**
- * An adapter for a list of {@link android.widget.SimpleMonthView} items.
- */
-class SimpleMonthAdapter extends BaseAdapter {
- private final Calendar mMinDate = Calendar.getInstance();
- private final Calendar mMaxDate = Calendar.getInstance();
-
- private final Context mContext;
-
- private Calendar mSelectedDay = Calendar.getInstance();
- private ColorStateList mCalendarTextColors = ColorStateList.valueOf(Color.BLACK);
- private ColorStateList mCalendarDayBackgroundColor = ColorStateList.valueOf(Color.MAGENTA);
- private OnDaySelectedListener mOnDaySelectedListener;
-
- private int mFirstDayOfWeek;
-
- public SimpleMonthAdapter(Context context) {
- mContext = context;
- }
-
- public void setRange(Calendar min, Calendar max) {
- mMinDate.setTimeInMillis(min.getTimeInMillis());
- mMaxDate.setTimeInMillis(max.getTimeInMillis());
-
- notifyDataSetInvalidated();
- }
-
- public void setFirstDayOfWeek(int firstDayOfWeek) {
- mFirstDayOfWeek = firstDayOfWeek;
-
- notifyDataSetInvalidated();
- }
-
- public int getFirstDayOfWeek() {
- return mFirstDayOfWeek;
- }
-
- /**
- * Updates the selected day and related parameters.
- *
- * @param day The day to highlight
- */
- public void setSelectedDay(Calendar day) {
- mSelectedDay = day;
-
- notifyDataSetChanged();
- }
-
- /**
- * Sets the listener to call when the user selects a day.
- *
- * @param listener The listener to call.
- */
- public void setOnDaySelectedListener(OnDaySelectedListener listener) {
- mOnDaySelectedListener = listener;
- }
-
- void setCalendarTextColor(ColorStateList colors) {
- mCalendarTextColors = colors;
- }
-
- void setCalendarDayBackgroundColor(ColorStateList dayBackgroundColor) {
- mCalendarDayBackgroundColor = dayBackgroundColor;
- }
-
- /**
- * 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);
- final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
- return diffMonth + 12 * diffYear + 1;
- }
-
- @Override
- public Object getItem(int position) {
- return null;
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final SimpleMonthView v;
- if (convertView != null) {
- v = (SimpleMonthView) convertView;
- } else {
- v = new SimpleMonthView(mContext);
-
- // Set up the new view
- final AbsListView.LayoutParams params = new AbsListView.LayoutParams(
- AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.MATCH_PARENT);
- v.setLayoutParams(params);
- v.setClickable(true);
- v.setOnDayClickListener(mOnDayClickListener);
-
- v.setMonthTextColor(mCalendarTextColors);
- v.setDayOfWeekTextColor(mCalendarTextColors);
- v.setDayTextColor(mCalendarTextColors);
-
- v.setDayBackgroundColor(mCalendarDayBackgroundColor);
- }
-
- final int minMonth = mMinDate.get(Calendar.MONTH);
- final int minYear = mMinDate.get(Calendar.YEAR);
- final int currentMonth = position + minMonth;
- final int month = currentMonth % 12;
- final int year = currentMonth / 12 + minYear;
- final int selectedDay;
- if (isSelectedDayInMonth(year, month)) {
- selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
- } else {
- selectedDay = -1;
- }
-
- // Invokes requestLayout() to ensure that the recycled view is set with the appropriate
- // height/number of weeks before being displayed.
- v.reuse();
-
- final int enabledDayRangeStart;
- if (minMonth == month && minYear == year) {
- enabledDayRangeStart = mMinDate.get(Calendar.DAY_OF_MONTH);
- } else {
- enabledDayRangeStart = 1;
- }
-
- final int enabledDayRangeEnd;
- if (mMaxDate.get(Calendar.MONTH) == month && mMaxDate.get(Calendar.YEAR) == year) {
- enabledDayRangeEnd = mMaxDate.get(Calendar.DAY_OF_MONTH);
- } else {
- enabledDayRangeEnd = 31;
- }
-
- v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
- enabledDayRangeStart, enabledDayRangeEnd);
- v.invalidate();
-
- return v;
- }
-
- private boolean isSelectedDayInMonth(int year, int month) {
- return mSelectedDay.get(Calendar.YEAR) == year && mSelectedDay.get(Calendar.MONTH) == month;
- }
-
- private boolean isCalendarInRange(Calendar value) {
- return value.compareTo(mMinDate) >= 0 && value.compareTo(mMaxDate) <= 0;
- }
-
- private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() {
- @Override
- public void onDayClick(SimpleMonthView view, Calendar day) {
- if (day != null && isCalendarInRange(day)) {
- setSelectedDay(day);
-
- if (mOnDaySelectedListener != null) {
- mOnDaySelectedListener.onDaySelected(SimpleMonthAdapter.this, day);
- }
- }
- }
- };
-
- public interface OnDaySelectedListener {
- public void onDaySelected(SimpleMonthAdapter view, Calendar day);
- }
-}
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 58ad515..4e5a39a 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -18,8 +18,8 @@
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
@@ -29,8 +29,6 @@
import android.os.Bundle;
import android.text.TextPaint;
import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.text.format.Time;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.StateSet;
@@ -38,13 +36,13 @@
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.R;
import com.android.internal.widget.ExploreByTouchHelper;
import java.text.SimpleDateFormat;
import java.util.Calendar;
-import java.util.Formatter;
import java.util.Locale;
/**
@@ -52,93 +50,80 @@
* within the specified month.
*/
class SimpleMonthView extends View {
- private static final int MIN_ROW_HEIGHT = 10;
+ private static final int DAYS_IN_WEEK = 7;
+ private static final int MAX_WEEKS_IN_MONTH = 6;
private static final int DEFAULT_SELECTED_DAY = -1;
private static final int DEFAULT_WEEK_START = Calendar.SUNDAY;
- private static final int DEFAULT_NUM_DAYS = 7;
- private static final int DEFAULT_NUM_ROWS = 6;
- private static final int MAX_NUM_ROWS = 6;
- private final Formatter mFormatter;
- private final StringBuilder mStringBuilder;
-
- private final int mMonthTextSize;
- private final int mDayOfWeekTextSize;
- private final int mDayTextSize;
-
- /** Height of the header containing the month and day of week labels. */
- private final int mMonthHeaderHeight;
+ private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
+ private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
private final TextPaint mMonthPaint = new TextPaint();
private final TextPaint mDayOfWeekPaint = new TextPaint();
private final TextPaint mDayPaint = new TextPaint();
-
- private final Paint mDayBackgroundPaint = new Paint();
-
- /** Single-letter (when available) formatter for the day of week label. */
- private SimpleDateFormat mDayFormatter = new SimpleDateFormat("EEEEE", Locale.getDefault());
-
- // affects the padding on the sides of this view
- private int mPadding = 0;
-
- private String mDayOfWeekTypeface;
- private String mMonthTypeface;
-
- private int mMonth;
- private int mYear;
-
- // 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 final int mRowHeight;
-
- // If this view contains the today
- private boolean mHasToday = false;
-
- // Which day is selected [0-6] or -1 if no day is selected
- private int mActivatedDay = -1;
-
- // Which day is today [0-6] or -1 if no day is today
- private int mToday = DEFAULT_SELECTED_DAY;
-
- // Which day of the week to start on [0-6]
- private int mWeekStart = DEFAULT_WEEK_START;
-
- // How many days to display
- private int mNumDays = DEFAULT_NUM_DAYS;
-
- // The number of days + a spot for week number if it is displayed
- private int mNumCells = mNumDays;
-
- private int mDayOfWeekStart = 0;
-
- // First enabled day
- private int mEnabledDayStart = 1;
-
- // Last enabled day
- private int mEnabledDayEnd = 31;
+ private final Paint mDaySelectorPaint = new Paint();
+ private final Paint mDayHighlightPaint = new Paint();
private final Calendar mCalendar = Calendar.getInstance();
private final Calendar mDayLabelCalendar = Calendar.getInstance();
private final MonthViewTouchHelper mTouchHelper;
- private int mNumRows = DEFAULT_NUM_ROWS;
+ private final SimpleDateFormat mTitleFormatter;
+ private final SimpleDateFormat mDayOfWeekFormatter;
- // Optional listener for handling day click actions
+ private CharSequence mTitle;
+
+ private int mMonth;
+ private int mYear;
+
+ private int mPaddedWidth;
+ private int mPaddedHeight;
+
+ private final int mMonthHeight;
+ private final int mDayOfWeekHeight;
+ private final int mDayHeight;
+ private final int mCellWidth;
+ private final int mDaySelectorRadius;
+
+ /** The day of month for the selected day, or -1 if no day is selected. */
+ private int mActivatedDay = -1;
+
+ /**
+ * The day of month for today, or -1 if the today is not in the current
+ * month.
+ */
+ private int mToday = DEFAULT_SELECTED_DAY;
+
+ /** The first day of the week (ex. Calendar.SUNDAY). */
+ private int mWeekStart = DEFAULT_WEEK_START;
+
+ /** The number of days (ex. 28) in the current month. */
+ private int mDaysInMonth;
+
+ /**
+ * The day of week (ex. Calendar.SUNDAY) for the first day of the current
+ * month.
+ */
+ private int mDayOfWeekStart;
+
+ /** The day of month for the first (inclusive) enabled day. */
+ private int mEnabledDayStart = 1;
+
+ /** The day of month for the last (inclusive) enabled day. */
+ private int mEnabledDayEnd = 31;
+
+ /** The number of week rows needed to display the current month. */
+ private int mNumWeeks = MAX_WEEKS_IN_MONTH;
+
+ /** Optional listener for handling day click actions. */
private OnDayClickListener mOnDayClickListener;
- // Whether to prevent setting the accessibility delegate
- private boolean mLockAccessibilityDelegate;
-
- private int mNormalTextColor;
- private int mDisabledTextColor;
- private int mSelectedDayColor;
-
private ColorStateList mDayTextColor;
+ private int mTouchedDay = -1;
+
public SimpleMonthView(Context context) {
this(context, null);
}
@@ -155,64 +140,123 @@
super(context, attrs, defStyleAttr, defStyleRes);
final Resources res = context.getResources();
- mDayOfWeekTypeface = res.getString(R.string.day_of_week_label_typeface);
- mMonthTypeface = res.getString(R.string.sans_serif);
-
- mStringBuilder = new StringBuilder(50);
- mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
-
- mDayTextSize = res.getDimensionPixelSize(R.dimen.datepicker_day_number_size);
- mMonthTextSize = res.getDimensionPixelSize(R.dimen.datepicker_month_label_size);
- mDayOfWeekTextSize = res.getDimensionPixelSize(
- R.dimen.datepicker_month_day_label_text_size);
- mMonthHeaderHeight = res.getDimensionPixelOffset(
- R.dimen.datepicker_month_list_item_header_height);
-
- mRowHeight = Math.max(MIN_ROW_HEIGHT,
- (res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height)
- - mMonthHeaderHeight) / MAX_NUM_ROWS);
+ mMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height);
+ mDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height);
+ mDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height);
+ mCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width);
+ mDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius);
// Set up accessibility components.
mTouchHelper = new MonthViewTouchHelper(this);
setAccessibilityDelegate(mTouchHelper);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- mLockAccessibilityDelegate = true;
- initPaints();
+ final Locale locale = res.getConfiguration().locale;
+ final String titleFormat = DateFormat.getBestDateTimePattern(locale, DEFAULT_TITLE_FORMAT);
+ mTitleFormatter = new SimpleDateFormat(titleFormat, locale);
+ mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale);
+
+ setClickable(true);
+ initPaints(res);
+ }
+
+ /**
+ * Applies the specified text appearance resource to a paint, returning the
+ * text color if one is set in the text appearance.
+ *
+ * @param p the paint to modify
+ * @param resId the resource ID of the text appearance
+ * @return the text color, if available
+ */
+ private ColorStateList applyTextAppearance(Paint p, int resId) {
+ final TypedArray ta = mContext.obtainStyledAttributes(null,
+ R.styleable.TextAppearance, 0, resId);
+
+ final String fontFamily = ta.getString(R.styleable.TextAppearance_fontFamily);
+ if (fontFamily != null) {
+ p.setTypeface(Typeface.create(fontFamily, 0));
+ }
+
+ p.setTextSize(ta.getDimensionPixelSize(
+ R.styleable.TextAppearance_textSize, (int) p.getTextSize()));
+
+ final ColorStateList textColor = ta.getColorStateList(R.styleable.TextAppearance_textColor);
+ if (textColor != null) {
+ final int enabledColor = textColor.getColorForState(ENABLED_STATE_SET, 0);
+ p.setColor(enabledColor);
+ }
+
+ ta.recycle();
+
+ return textColor;
+ }
+
+ public void setMonthTextAppearance(int resId) {
+ applyTextAppearance(mMonthPaint, resId);
+ invalidate();
+ }
+
+ public void setDayOfWeekTextAppearance(int resId) {
+ applyTextAppearance(mDayOfWeekPaint, resId);
+ invalidate();
+ }
+
+ public void setDayTextAppearance(int resId) {
+ final ColorStateList textColor = applyTextAppearance(mDayPaint, resId);
+ if (textColor != null) {
+ mDayTextColor = textColor;
+ }
+
+ invalidate();
+ }
+
+ public CharSequence getTitle() {
+ if (mTitle == null) {
+ mTitle = mTitleFormatter.format(mCalendar.getTime());
+ }
+ return mTitle;
}
/**
* Sets up the text and style properties for painting.
*/
- private void initPaints() {
+ private void initPaints(Resources res) {
+ final String monthTypeface = res.getString(R.string.date_picker_month_typeface);
+ final String dayOfWeekTypeface = res.getString(R.string.date_picker_day_of_week_typeface);
+ final String dayTypeface = res.getString(R.string.date_picker_day_typeface);
+
+ final int monthTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_month_text_size);
+ final int dayOfWeekTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_day_of_week_text_size);
+ final int dayTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_day_text_size);
+
mMonthPaint.setAntiAlias(true);
- mMonthPaint.setTextSize(mMonthTextSize);
- mMonthPaint.setTypeface(Typeface.create(mMonthTypeface, Typeface.BOLD));
+ mMonthPaint.setTextSize(monthTextSize);
+ mMonthPaint.setTypeface(Typeface.create(monthTypeface, 0));
mMonthPaint.setTextAlign(Align.CENTER);
mMonthPaint.setStyle(Style.FILL);
mDayOfWeekPaint.setAntiAlias(true);
- mDayOfWeekPaint.setTextSize(mDayOfWeekTextSize);
- mDayOfWeekPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.BOLD));
+ mDayOfWeekPaint.setTextSize(dayOfWeekTextSize);
+ mDayOfWeekPaint.setTypeface(Typeface.create(dayOfWeekTypeface, 0));
mDayOfWeekPaint.setTextAlign(Align.CENTER);
mDayOfWeekPaint.setStyle(Style.FILL);
- mDayBackgroundPaint.setAntiAlias(true);
- mDayBackgroundPaint.setStyle(Style.FILL);
+ mDaySelectorPaint.setAntiAlias(true);
+ mDaySelectorPaint.setStyle(Style.FILL);
+
+ mDayHighlightPaint.setAntiAlias(true);
+ mDayHighlightPaint.setStyle(Style.FILL);
mDayPaint.setAntiAlias(true);
- mDayPaint.setTextSize(mDayTextSize);
+ mDayPaint.setTextSize(dayTextSize);
+ mDayPaint.setTypeface(Typeface.create(dayTypeface, 0));
mDayPaint.setTextAlign(Align.CENTER);
mDayPaint.setStyle(Style.FILL);
}
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- mDayFormatter = new SimpleDateFormat("EEEEE", newConfig.locale);
- }
-
void setMonthTextColor(ColorStateList monthTextColor) {
final int enabledColor = monthTextColor.getColorForState(ENABLED_STATE_SET, 0);
mMonthPaint.setColor(enabledColor);
@@ -230,20 +274,18 @@
invalidate();
}
- void setDayBackgroundColor(ColorStateList dayBackgroundColor) {
+ void setDaySelectorColor(ColorStateList dayBackgroundColor) {
final int activatedColor = dayBackgroundColor.getColorForState(
StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
- mDayBackgroundPaint.setColor(activatedColor);
+ mDaySelectorPaint.setColor(activatedColor);
invalidate();
}
- @Override
- public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
- // Workaround for a JB MR1 issue where accessibility delegates on
- // top-level ListView items are overwritten.
- if (!mLockAccessibilityDelegate) {
- super.setAccessibilityDelegate(delegate);
- }
+ void setDayHighlightColor(ColorStateList dayHighlightColor) {
+ final int pressedColor = dayHighlightColor.getColorForState(
+ StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED), 0);
+ mDayHighlightPaint.setColor(pressedColor);
+ invalidate();
}
public void setOnDayClickListener(OnDayClickListener listener) {
@@ -253,30 +295,124 @@
@Override
public boolean dispatchHoverEvent(MotionEvent event) {
// First right-of-refusal goes the touch exploration helper.
- if (mTouchHelper.dispatchHoverEvent(event)) {
- return true;
- }
- return super.dispatchHoverEvent(event);
+ return mTouchHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
- case MotionEvent.ACTION_UP:
- final int day = getDayFromLocation(event.getX(), event.getY());
- if (day >= 0) {
- onDayClick(day);
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ final int touchedDay = getDayAtLocation(event.getX(), event.getY());
+ if (mTouchedDay != touchedDay) {
+ mTouchedDay = touchedDay;
+ invalidate();
}
break;
+
+ case MotionEvent.ACTION_UP:
+ final int clickedDay = getDayAtLocation(event.getX(), event.getY());
+ onDayClicked(clickedDay);
+ // Fall through.
+ case MotionEvent.ACTION_CANCEL:
+ // Reset touched day on stream end.
+ mTouchedDay = -1;
+ invalidate();
+ break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
- drawMonthTitle(canvas);
- drawWeekDayLabels(canvas);
+ final int paddingLeft = getPaddingLeft();
+ final int paddingTop = getPaddingTop();
+ canvas.translate(paddingLeft, paddingTop);
+
+ drawMonth(canvas);
+ drawDaysOfWeek(canvas);
drawDays(canvas);
+
+ canvas.translate(-paddingLeft, -paddingTop);
+ }
+
+ private void drawMonth(Canvas canvas) {
+ final float x = mPaddedWidth / 2f;
+
+ // Vertically centered within the month header height.
+ final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
+ final float y = (mMonthHeight - lineHeight) / 2f;
+
+ canvas.drawText(getTitle().toString(), x, y, mMonthPaint);
+ }
+
+ private void drawDaysOfWeek(Canvas canvas) {
+ final float cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+
+ // Vertically centered within the cell height.
+ final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
+ final float y = mMonthHeight + (mDayOfWeekHeight - lineHeight) / 2f;
+
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ final int calendarDay = (i + mWeekStart) % DAYS_IN_WEEK;
+ mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
+
+ final String dayLabel = mDayOfWeekFormatter.format(mDayLabelCalendar.getTime());
+ final float x = (2 * i + 1) * cellWidthHalf;
+ canvas.drawText(dayLabel, x, y, mDayOfWeekPaint);
+ }
+ }
+
+ /**
+ * Draws the month days.
+ */
+ private void drawDays(Canvas canvas) {
+ final int cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+
+ // Vertically centered within the cell height.
+ final float halfLineHeight = (mDayPaint.ascent() + mDayPaint.descent()) / 2;
+ float centerY = mMonthHeight + mDayOfWeekHeight + mDayHeight / 2f;
+
+ for (int day = 1, j = findDayOffset(); day <= mDaysInMonth; day++) {
+ final int x = (2 * j + 1) * cellWidthHalf;
+ int stateMask = 0;
+
+ if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
+ stateMask |= StateSet.VIEW_STATE_ENABLED;
+ }
+
+ final boolean isDayActivated = mActivatedDay == day;
+ if (isDayActivated) {
+ stateMask |= StateSet.VIEW_STATE_ACTIVATED;
+
+ // Adjust the circle to be centered on the row.
+ canvas.drawCircle(x, centerY, mDaySelectorRadius, mDaySelectorPaint);
+ } else if (mTouchedDay == day) {
+ stateMask |= StateSet.VIEW_STATE_PRESSED;
+
+ // Adjust the circle to be centered on the row.
+ canvas.drawCircle(x, centerY, mDaySelectorRadius, mDayHighlightPaint);
+ }
+
+ final boolean isDayToday = mToday == day;
+ final int dayTextColor;
+ if (isDayToday && !isDayActivated) {
+ dayTextColor = mDaySelectorPaint.getColor();
+ } else {
+ final int[] stateSet = StateSet.get(stateMask);
+ dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
+ }
+ mDayPaint.setColor(dayTextColor);
+
+ canvas.drawText("" + day, x, centerY - halfLineHeight, mDayPaint);
+
+ j++;
+
+ if (j == DAYS_IN_WEEK) {
+ j = 0;
+ centerY += mDayHeight;
+ }
+ }
}
private static boolean isValidDayOfWeek(int day) {
@@ -288,18 +424,52 @@
}
/**
- * Sets all the parameters for displaying this week. Parameters have a default value and
- * will only update if a new value is included, except for focus month, which will always
- * default to no focus month if no value is passed in. The only required parameter is the
- * week start.
+ * Sets the selected day.
*
- * @param selectedDay the selected day of the month, or -1 for no selection.
- * @param month the month.
- * @param year the year.
- * @param weekStart which day the week should start on. {@link Calendar#SUNDAY} through
- * {@link Calendar#SATURDAY}.
- * @param enabledDayStart the first enabled day.
- * @param enabledDayEnd the last enabled day.
+ * @param dayOfMonth the selected day of the month, or {@code -1} to clear
+ * the selection
+ */
+ public void setSelectedDay(int dayOfMonth) {
+ mActivatedDay = dayOfMonth;
+
+ // Invalidate cached accessibility information.
+ mTouchHelper.invalidateRoot();
+ invalidate();
+ }
+
+ /**
+ * Sets the first day of the week.
+ *
+ * @param weekStart which day the week should start on, valid values are
+ * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+ */
+ public void setFirstDayOfWeek(int weekStart) {
+ if (isValidDayOfWeek(weekStart)) {
+ mWeekStart = weekStart;
+ } else {
+ mWeekStart = mCalendar.getFirstDayOfWeek();
+ }
+
+ // Invalidate cached accessibility information.
+ mTouchHelper.invalidateRoot();
+ invalidate();
+ }
+
+ /**
+ * Sets all the parameters for displaying this week.
+ * <p>
+ * Parameters have a default value and will only update if a new value is
+ * included, except for focus month, which will always default to no focus
+ * month if no value is passed in. The only required parameter is the week
+ * start.
+ *
+ * @param selectedDay the selected day of the month, or -1 for no selection
+ * @param month the month
+ * @param year the year
+ * @param weekStart which day the week should start on, valid values are
+ * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+ * @param enabledDayStart the first enabled day
+ * @param enabledDayEnd the last enabled day
*/
void setMonthParams(int selectedDay, int month, int year, int weekStart, int enabledDayStart,
int enabledDayEnd) {
@@ -310,12 +480,6 @@
}
mYear = year;
- // Figure out what day today is
- final Time today = new Time(Time.getCurrentTimezone());
- today.setToNow();
- mHasToday = false;
- mToday = -1;
-
mCalendar.set(Calendar.MONTH, mMonth);
mCalendar.set(Calendar.YEAR, mYear);
mCalendar.set(Calendar.DAY_OF_MONTH, 1);
@@ -334,15 +498,20 @@
mEnabledDayEnd = enabledDayEnd;
}
- mNumCells = getDaysInMonth(mMonth, mYear);
- for (int i = 0; i < mNumCells; i++) {
+ // Figure out what day today is.
+ final Calendar today = Calendar.getInstance();
+ mToday = -1;
+ mDaysInMonth = getDaysInMonth(mMonth, mYear);
+ for (int i = 0; i < mDaysInMonth; i++) {
final int day = i + 1;
if (sameDay(day, today)) {
- mHasToday = true;
mToday = day;
}
}
- mNumRows = calculateNumRows();
+ mNumWeeks = calculateNumRows();
+
+ // Invalidate the old title.
+ mTitle = null;
// Invalidate cached accessibility information.
mTouchHelper.invalidateRoot();
@@ -371,154 +540,118 @@
}
public void reuse() {
- mNumRows = DEFAULT_NUM_ROWS;
+ mNumWeeks = MAX_WEEKS_IN_MONTH;
requestLayout();
}
private int calculateNumRows() {
- int offset = findDayOffset();
- int dividend = (offset + mNumCells) / mNumDays;
- int remainder = (offset + mNumCells) % mNumDays;
- return (dividend + (remainder > 0 ? 1 : 0));
+ final int offset = findDayOffset();
+ final int dividend = (offset + mDaysInMonth) / DAYS_IN_WEEK;
+ final int remainder = (offset + mDaysInMonth) % DAYS_IN_WEEK;
+ return dividend + (remainder > 0 ? 1 : 0);
}
- private boolean sameDay(int day, Time today) {
- return mYear == today.year &&
- mMonth == today.month &&
- day == today.monthDay;
+ private boolean sameDay(int day, Calendar today) {
+ return mYear == today.get(Calendar.YEAR) && mMonth == today.get(Calendar.MONTH)
+ && day == today.get(Calendar.DAY_OF_MONTH);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows
- + mMonthHeaderHeight);
+ final int preferredHeight = mDayHeight * mNumWeeks + mDayOfWeekHeight + mMonthHeight
+ + getPaddingTop() + getPaddingBottom();
+ final int preferredWidth = mCellWidth * DAYS_IN_WEEK
+ + getPaddingStart() + getPaddingEnd();
+ final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec);
+ final int resolvedHeight = resolveSize(preferredHeight, heightMeasureSpec);
+ setMeasuredDimension(resolvedWidth, resolvedHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- mWidth = w;
+ mPaddedWidth = w - getPaddingLeft() - getPaddingRight();
+ mPaddedHeight = w - getPaddingTop() - getPaddingBottom();
// Invalidate cached accessibility information.
mTouchHelper.invalidateRoot();
}
- private String getMonthAndYearString() {
- int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
- | DateUtils.FORMAT_NO_MONTH_DAY;
- mStringBuilder.setLength(0);
- long millis = mCalendar.getTimeInMillis();
- return DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags,
- Time.getCurrentTimezone()).toString();
- }
-
- private void drawMonthTitle(Canvas canvas) {
- final float x = (mWidth + 2 * mPadding) / 2f;
-
- // Centered on the upper half of the month header.
- final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
- final float y = mMonthHeaderHeight * 0.25f - lineHeight / 2f;
-
- canvas.drawText(getMonthAndYearString(), x, y, mMonthPaint);
- }
-
- private void drawWeekDayLabels(Canvas canvas) {
- final float dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
-
- // Centered on the lower half of the month header.
- final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
- final float y = mMonthHeaderHeight * 0.75f - lineHeight / 2f;
-
- for (int i = 0; i < mNumDays; i++) {
- final int calendarDay = (i + mWeekStart) % mNumDays;
- mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
-
- final String dayLabel = mDayFormatter.format(mDayLabelCalendar.getTime());
- final float x = (2 * i + 1) * dayWidthHalf + mPadding;
- canvas.drawText(dayLabel, x, y, mDayOfWeekPaint);
- }
- }
-
- /**
- * Draws the month days.
- */
- private void drawDays(Canvas canvas) {
- final int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
-
- // Centered within the row.
- final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
- float y = mMonthHeaderHeight + (mRowHeight - lineHeight) / 2f;
-
- for (int day = 1, j = findDayOffset(); day <= mNumCells; day++) {
- final int x = (2 * j + 1) * dayWidthHalf + mPadding;
- int stateMask = 0;
-
- if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
- stateMask |= StateSet.VIEW_STATE_ENABLED;
- }
-
- if (mActivatedDay == day) {
- stateMask |= StateSet.VIEW_STATE_ACTIVATED;
-
- // Adjust the circle to be centered the row.
- final float rowCenterY = y + lineHeight / 2;
- canvas.drawCircle(x, rowCenterY, mRowHeight / 2,
- mDayBackgroundPaint);
- }
-
- final int[] stateSet = StateSet.get(stateMask);
- final int dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
- mDayPaint.setColor(dayTextColor);
-
- final boolean isDayToday = mHasToday && mToday == day;
- mDayPaint.setFakeBoldText(isDayToday);
-
- canvas.drawText(String.format("%d", day), x, y, mDayPaint);
-
- j++;
-
- if (j == mNumDays) {
- j = 0;
- y += mRowHeight;
- }
- }
- }
-
private int findDayOffset() {
- return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart)
- - mWeekStart;
+ final int offset = mDayOfWeekStart - mWeekStart;
+ if (mDayOfWeekStart < mWeekStart) {
+ return offset + DAYS_IN_WEEK;
+ }
+ return offset;
}
/**
- * Calculates the day that the given x position is in, accounting for week
- * number. Returns the day or -1 if the position wasn't in a day.
+ * Calculates the day of the month at the specified touch position. Returns
+ * the day of the month or -1 if the position wasn't in a valid day.
*
- * @param x The x position of the touch event
- * @return The day number, or -1 if the position wasn't in a day
+ * @param x the x position of the touch event
+ * @param y the y position of the touch event
+ * @return the day of the month at (x, y) or -1 if the position wasn't in a
+ * valid day
*/
- private int getDayFromLocation(float x, float y) {
- int dayStart = mPadding;
- if (x < dayStart || x > mWidth - mPadding) {
+ private int getDayAtLocation(float x, float y) {
+ final int paddedX = (int) (x - getPaddingLeft() + 0.5f);
+ if (paddedX < 0 || paddedX >= mPaddedWidth) {
return -1;
}
- // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
- int row = (int) (y - mMonthHeaderHeight) / mRowHeight;
- int column = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding));
- int day = column - findDayOffset() + 1;
- day += row * mNumDays;
- if (day < 1 || day > mNumCells) {
+ final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+ final int paddedY = (int) (y - getPaddingTop() + 0.5f);
+ if (paddedY < headerHeight || paddedY >= mPaddedHeight) {
return -1;
}
+
+ final int row = (paddedY - headerHeight) / mDayHeight;
+ final int col = (paddedX * DAYS_IN_WEEK) / mPaddedWidth;
+ final int index = col + row * DAYS_IN_WEEK;
+ final int day = index + 1 - findDayOffset();
+ if (day < 1 || day > mDaysInMonth) {
+ return -1;
+ }
+
return day;
}
/**
+ * Calculates the bounds of the specified day.
+ *
+ * @param day the day of the month
+ * @param outBounds the rect to populate with bounds
+ */
+ private boolean getBoundsForDay(int day, Rect outBounds) {
+ if (day < 1 || day > mDaysInMonth) {
+ return false;
+ }
+
+ final int index = day - 1 + findDayOffset();
+ final int row = index / DAYS_IN_WEEK;
+ final int col = index % DAYS_IN_WEEK;
+
+ final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+ final int paddedY = row * mDayHeight + headerHeight;
+ final int paddedX = col * mPaddedWidth;
+
+ final int y = paddedY + getPaddingTop();
+ final int x = paddedX + getPaddingLeft();
+
+ final int cellHeight = mDayHeight;
+ final int cellWidth = mPaddedWidth / DAYS_IN_WEEK;
+ outBounds.set(x, y, (x + cellWidth), (y + cellHeight));
+
+ return true;
+ }
+
+ /**
* Called when the user clicks on a day. Handles callbacks to the
* {@link OnDayClickListener} if one is set.
*
- * @param day The day that was clicked
+ * @param day the day that was clicked
*/
- private void onDayClick(int day) {
+ private void onDayClicked(int day) {
if (mOnDayClickListener != null) {
Calendar date = Calendar.getInstance();
date.set(mYear, mMonth, day);
@@ -530,44 +663,6 @@
}
/**
- * @return The date that has accessibility focus, or {@code null} if no date
- * has focus
- */
- Calendar getAccessibilityFocus() {
- final int day = mTouchHelper.getFocusedVirtualView();
- Calendar date = null;
- if (day >= 0) {
- date = Calendar.getInstance();
- date.set(mYear, mMonth, day);
- }
- return date;
- }
-
- /**
- * Clears accessibility focus within the view. No-op if the view does not
- * contain accessibility focus.
- */
- public void clearAccessibilityFocus() {
- mTouchHelper.clearFocusedVirtualView();
- }
-
- /**
- * Attempts to restore accessibility focus to the specified date.
- *
- * @param day The date which should receive focus
- * @return {@code false} if the date is not valid for this month view, or
- * {@code true} if the date received focus
- */
- boolean restoreAccessibilityFocus(Calendar day) {
- if ((day.get(Calendar.YEAR) != mYear) || (day.get(Calendar.MONTH) != mMonth) ||
- (day.get(Calendar.DAY_OF_MONTH) > mNumCells)) {
- return false;
- }
- mTouchHelper.setFocusedVirtualView(day.get(Calendar.DAY_OF_MONTH));
- return true;
- }
-
- /**
* Provides a virtual view hierarchy for interfacing with an accessibility
* service.
*/
@@ -581,24 +676,9 @@
super(host);
}
- public void setFocusedVirtualView(int virtualViewId) {
- getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
- virtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- }
-
- public void clearFocusedVirtualView() {
- final int focusedVirtualView = getFocusedVirtualView();
- if (focusedVirtualView != ExploreByTouchHelper.INVALID_ID) {
- getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
- focusedVirtualView,
- AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
- null);
- }
- }
-
@Override
protected int getVirtualViewAt(float x, float y) {
- final int day = getDayFromLocation(x, y);
+ final int day = getDayAtLocation(x, y);
if (day >= 0) {
return day;
}
@@ -607,7 +687,7 @@
@Override
protected void getVisibleVirtualViews(IntArray virtualViewIds) {
- for (int day = 1; day <= mNumCells; day++) {
+ for (int day = 1; day <= mDaysInMonth; day++) {
virtualViewIds.add(day);
}
}
@@ -619,11 +699,20 @@
@Override
protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
- getItemBounds(virtualViewId, mTempRect);
+ final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect);
+
+ if (!hasBounds) {
+ // The day is invalid, kill the node.
+ mTempRect.setEmpty();
+ node.setContentDescription("");
+ node.setBoundsInParent(mTempRect);
+ node.setVisibleToUser(false);
+ return;
+ }
node.setContentDescription(getItemDescription(virtualViewId));
node.setBoundsInParent(mTempRect);
- node.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+ node.addAction(AccessibilityAction.ACTION_CLICK);
if (virtualViewId == mActivatedDay) {
node.setSelected(true);
@@ -636,7 +725,7 @@
Bundle arguments) {
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK:
- onDayClick(virtualViewId);
+ onDayClicked(virtualViewId);
return true;
}
@@ -644,26 +733,6 @@
}
/**
- * Calculates the bounding rectangle of a given time object.
- *
- * @param day The day to calculate bounds for
- * @param rect The rectangle in which to store the bounds
- */
- private void getItemBounds(int day, Rect rect) {
- final int offsetX = mPadding;
- final int offsetY = mMonthHeaderHeight;
- final int cellHeight = mRowHeight;
- final int cellWidth = ((mWidth - (2 * mPadding)) / mNumDays);
- final int index = ((day - 1) + findDayOffset());
- final int row = (index / mNumDays);
- final int column = (index % mNumDays);
- final int x = (offsetX + (column * cellWidth));
- final int y = (offsetY + (row * cellHeight));
-
- rect.set(x, y, (x + cellWidth), (y + cellHeight));
- }
-
- /**
* Generates a description for a given time object. Since this
* description will be spoken, the components are ordered by descending
* specificity as DAY MONTH YEAR.
diff --git a/core/java/android/widget/TextViewWithCircularIndicator.java b/core/java/android/widget/TextViewWithCircularIndicator.java
deleted file mode 100644
index d3c786c..0000000
--- a/core/java/android/widget/TextViewWithCircularIndicator.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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 android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.util.AttributeSet;
-
-import com.android.internal.R;
-
-class TextViewWithCircularIndicator extends TextView {
- private final Paint mCirclePaint = new Paint();
- private final String mItemIsSelectedText;
-
- public TextViewWithCircularIndicator(Context context) {
- this(context, null);
- }
-
- public TextViewWithCircularIndicator(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TextViewWithCircularIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public TextViewWithCircularIndicator(Context context, AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
-
- final Resources res = context.getResources();
- mItemIsSelectedText = res.getString(R.string.item_is_selected);
-
- init();
- }
-
- private void init() {
- mCirclePaint.setTypeface(Typeface.create(mCirclePaint.getTypeface(), Typeface.BOLD));
- mCirclePaint.setAntiAlias(true);
- mCirclePaint.setTextAlign(Paint.Align.CENTER);
- mCirclePaint.setStyle(Paint.Style.FILL);
- }
-
- public void setCircleColor(int color) {
- mCirclePaint.setColor(color);
- invalidate();
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- if (isActivated()) {
- final int width = getWidth();
- final int height = getHeight();
- final int radius = Math.min(width, height) / 2;
- canvas.drawCircle(width / 2, height / 2, radius, mCirclePaint);
- }
-
- super.onDraw(canvas);
- }
-
- @Override
- public CharSequence getContentDescription() {
- final CharSequence itemText = getText();
- if (isActivated()) {
- return String.format(mItemIsSelectedText, itemText);
- } else {
- return itemText;
- }
- }
-}
\ No newline at end of file
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
index 6f0465f..7bd502e 100644
--- a/core/java/android/widget/YearPickerView.java
+++ b/core/java/android/widget/YearPickerView.java
@@ -17,10 +17,9 @@
package android.widget;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.util.AttributeSet;
-import android.util.StateSet;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@@ -32,23 +31,14 @@
/**
* Displays a selectable list of years.
*/
-class YearPickerView extends ListView implements AdapterView.OnItemClickListener,
- OnDateChangedListener {
- private final Calendar mMinDate = Calendar.getInstance();
- private final Calendar mMaxDate = Calendar.getInstance();
-
+class YearPickerView extends ListView {
private final YearAdapter mAdapter;
private final int mViewSize;
private final int mChildSize;
- private DatePickerController mController;
+ private OnYearSelectedListener mOnYearSelectedListener;
- private int mSelectedPosition = -1;
- private int mYearActivatedColor;
-
- public YearPickerView(Context context) {
- this(context, null);
- }
+ private long mCurrentTimeMillis;
public YearPickerView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.listViewStyle);
@@ -69,104 +59,187 @@
mViewSize = res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height);
mChildSize = res.getDimensionPixelOffset(R.dimen.datepicker_year_label_height);
- setVerticalFadingEdgeEnabled(true);
- setFadingEdgeLength(mChildSize / 3);
+ setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final int year = mAdapter.getYearForPosition(position);
+ mAdapter.setSelection(year);
- final int paddingTop = res.getDimensionPixelSize(
- R.dimen.datepicker_year_picker_padding_top);
- setPadding(0, paddingTop, 0, 0);
+ if (mOnYearSelectedListener != null) {
+ mOnYearSelectedListener.onYearChanged(YearPickerView.this, year);
+ }
+ }
+ });
- setOnItemClickListener(this);
- setDividerHeight(0);
-
- mAdapter = new YearAdapter(getContext(), R.layout.year_label_text_view);
+ mAdapter = new YearAdapter(getContext());
setAdapter(mAdapter);
}
+ public void setOnYearSelectedListener(OnYearSelectedListener listener) {
+ mOnYearSelectedListener = listener;
+ }
+
+ public void setDate(long currentTimeMillis) {
+ mCurrentTimeMillis = currentTimeMillis;
+ }
+
+ /**
+ * Sets the currently selected year. Jumps immediately to the new year.
+ *
+ * @param year the target year
+ */
+ public void setYear(final int year) {
+ mAdapter.setSelection(year);
+
+ post(new Runnable() {
+ @Override
+ public void run() {
+ final int position = mAdapter.getPositionForYear(year);
+ if (position >= 0 && position < getCount()) {
+ setSelectionCentered(position);
+ }
+ }
+ });
+ }
+
+ public void setSelectionCentered(int position) {
+ final int offset = mViewSize / 2 - mChildSize / 2;
+ setSelectionFromTop(position, offset);
+ }
+
public void setRange(Calendar min, Calendar max) {
- mMinDate.setTimeInMillis(min.getTimeInMillis());
- mMaxDate.setTimeInMillis(max.getTimeInMillis());
-
- updateAdapterData();
- }
-
- public void init(DatePickerController controller) {
- mController = controller;
- mController.registerOnDateChangedListener(this);
-
- updateAdapterData();
-
- onDateChanged();
- }
-
- public void setYearBackgroundColor(ColorStateList yearBackgroundColor) {
- mYearActivatedColor = yearBackgroundColor.getColorForState(
- StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
- invalidate();
+ mAdapter.setRange(min, max);
}
public void setYearTextAppearance(int resId) {
mAdapter.setItemTextAppearance(resId);
}
- private void updateAdapterData() {
- mAdapter.clear();
-
- final int maxYear = mMaxDate.get(Calendar.YEAR);
- for (int year = mMinDate.get(Calendar.YEAR); year <= maxYear; year++) {
- mAdapter.add(year);
- }
+ public void setYearActivatedTextAppearance(int resId) {
+ mAdapter.setItemActivatedTextAppearance(resId);
}
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- mController.tryVibrate();
- if (position != mSelectedPosition) {
- mSelectedPosition = position;
- mAdapter.notifyDataSetChanged();
- }
- mController.onYearSelected(mAdapter.getItem(position));
- }
+ private static class YearAdapter extends BaseAdapter {
+ private static final int ITEM_LAYOUT = R.layout.year_label_text_view;
- private class YearAdapter extends ArrayAdapter<Integer> {
+ private final LayoutInflater mInflater;
+
+ private int mActivatedYear;
+ private int mMinYear;
+ private int mCount;
+
private int mItemTextAppearanceResId;
+ private int mItemActivatedTextAppearanceResId;
- public YearAdapter(Context context, int resource) {
- super(context, resource);
+ public YearAdapter(Context context) {
+ mInflater = LayoutInflater.from(context);
}
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final TextViewWithCircularIndicator v = (TextViewWithCircularIndicator)
- super.getView(position, convertView, parent);
- v.setTextAppearance(v.getContext(), mItemTextAppearanceResId);
- v.setCircleColor(mYearActivatedColor);
+ public void setRange(Calendar minDate, Calendar maxDate) {
+ final int minYear = minDate.get(Calendar.YEAR);
+ final int count = maxDate.get(Calendar.YEAR) - minYear + 1;
- final int year = getItem(position);
- final boolean selected = mController.getSelectedDay().get(Calendar.YEAR) == year;
- v.setActivated(selected);
+ if (mMinYear != minYear || mCount != count) {
+ mMinYear = minYear;
+ mCount = count;
+ notifyDataSetInvalidated();
+ }
+ }
- return v;
+ public boolean setSelection(int year) {
+ if (mActivatedYear != year) {
+ mActivatedYear = year;
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
}
public void setItemTextAppearance(int resId) {
mItemTextAppearanceResId = resId;
+ notifyDataSetChanged();
}
- }
- public void postSetSelectionCentered(final int position) {
- postSetSelectionFromTop(position, mViewSize / 2 - mChildSize / 2);
- }
+ public void setItemActivatedTextAppearance(int resId) {
+ mItemActivatedTextAppearanceResId = resId;
+ notifyDataSetChanged();
+ }
- public void postSetSelectionFromTop(final int position, final int offset) {
- post(new Runnable() {
+ @Override
+ public int getCount() {
+ return mCount;
+ }
- @Override
- public void run() {
- setSelectionFromTop(position, offset);
- requestLayout();
+ @Override
+ public Integer getItem(int position) {
+ return getYearForPosition(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return getYearForPosition(position);
+ }
+
+ public int getPositionForYear(int year) {
+ return year - mMinYear;
+ }
+
+ public int getYearForPosition(int position) {
+ return mMinYear + position;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
}
- });
+
+ final int year = getYearForPosition(position);
+ final boolean activated = mActivatedYear == year;
+
+ final int textAppearanceResId;
+ if (activated && mItemActivatedTextAppearanceResId != 0) {
+ textAppearanceResId = mItemActivatedTextAppearanceResId;
+ } else {
+ textAppearanceResId = mItemTextAppearanceResId;
+ }
+
+ final TextView v = (TextView) convertView;
+ v.setText("" + year);
+ v.setTextAppearance(v.getContext(), textAppearanceResId);
+ v.setActivated(activated);
+ return v;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return true;
+ }
}
public int getFirstPositionOffset() {
@@ -177,22 +250,28 @@
return firstChild.getTop();
}
- @Override
- public void onDateChanged() {
- updateAdapterData();
- mAdapter.notifyDataSetChanged();
- postSetSelectionCentered(
- mController.getSelectedDay().get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
- }
-
/** @hide */
@Override
public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
super.onInitializeAccessibilityEventInternal(event);
+ // There are a bunch of years, so don't bother.
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
event.setFromIndex(0);
event.setToIndex(0);
}
}
+
+ /**
+ * The callback used to indicate the user changed the year.
+ */
+ public interface OnYearSelectedListener {
+ /**
+ * Called upon a year change.
+ *
+ * @param view The view associated with this listener.
+ * @param year The year that was set.
+ */
+ void onYearChanged(YearPickerView view, int year);
+ }
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 02f675c..f479f4f 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -33,6 +33,7 @@
import android.content.pm.PackageParser.PackageParserException;
import android.os.Build;
import android.os.SELinux;
+import android.os.SystemProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Slog;
@@ -74,6 +75,7 @@
final long[] apkHandles;
final boolean multiArch;
+ final boolean extractNativeLibs;
public static Handle create(File packageFile) throws IOException {
try {
@@ -86,14 +88,16 @@
public static Handle create(Package pkg) throws IOException {
return create(pkg.getAllCodePaths(),
- (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0);
+ (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0,
+ (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0);
}
public static Handle create(PackageLite lite) throws IOException {
- return create(lite.getAllCodePaths(), lite.multiArch);
+ return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs);
}
- private static Handle create(List<String> codePaths, boolean multiArch) throws IOException {
+ private static Handle create(List<String> codePaths, boolean multiArch,
+ boolean extractNativeLibs) throws IOException {
final int size = codePaths.size();
final long[] apkHandles = new long[size];
for (int i = 0; i < size; i++) {
@@ -108,12 +112,13 @@
}
}
- return new Handle(apkHandles, multiArch);
+ return new Handle(apkHandles, multiArch, extractNativeLibs);
}
- Handle(long[] apkHandles, boolean multiArch) {
+ Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs) {
this.apkHandles = apkHandles;
this.multiArch = multiArch;
+ this.extractNativeLibs = extractNativeLibs;
mGuard.open("close");
}
@@ -146,8 +151,8 @@
private static native long nativeSumNativeBinaries(long handle, String cpuAbi);
- private native static int nativeCopyNativeBinaries(long handle,
- String sharedLibraryPath, String abiToCopy);
+ private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
+ String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge);
private static long sumNativeBinaries(Handle handle, String abi) {
long sum = 0;
@@ -167,7 +172,8 @@
*/
public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
for (long apkHandle : handle.apkHandles) {
- int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi);
+ int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
+ handle.extractNativeLibs, HAS_NATIVE_BRIDGE);
if (res != INSTALL_SUCCEEDED) {
return res;
}
@@ -218,7 +224,8 @@
/**
* Remove the native binaries of a given package. This deletes the files
*/
- public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir) {
+ public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot,
+ boolean deleteRootDir) {
if (DEBUG_NATIVE) {
Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
}
@@ -247,7 +254,8 @@
// asked to or this will prevent installation of future updates.
if (deleteRootDir) {
if (!nativeLibraryRoot.delete()) {
- Slog.w(TAG, "Could not delete native binary directory: " + nativeLibraryRoot.getPath());
+ Slog.w(TAG, "Could not delete native binary directory: " +
+ nativeLibraryRoot.getPath());
}
}
}
@@ -416,6 +424,9 @@
// We don't care about the other return values for now.
private static final int BITCODE_PRESENT = 1;
+ private static final boolean HAS_NATIVE_BRIDGE =
+ !"0".equals(SystemProperties.get("ro.dalvik.vm.native.bridge", "0"));
+
private static native int hasRenderscriptBitcode(long apkHandle);
public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 57fcf57..06bdb24 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -764,17 +764,55 @@
private int[] mCurrentProfileIds = new int[0];
private static void buildEnabledInputMethodsSettingString(
- StringBuilder builder, Pair<String, ArrayList<String>> pair) {
- String id = pair.first;
- ArrayList<String> subtypes = pair.second;
- builder.append(id);
+ StringBuilder builder, Pair<String, ArrayList<String>> ime) {
+ builder.append(ime.first);
// Inputmethod and subtypes are saved in the settings as follows:
// ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
- for (String subtypeId: subtypes) {
+ for (String subtypeId: ime.second) {
builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
}
}
+ public static String buildInputMethodsSettingString(
+ List<Pair<String, ArrayList<String>>> allImeSettingsMap) {
+ final StringBuilder b = new StringBuilder();
+ boolean needsSeparator = false;
+ for (Pair<String, ArrayList<String>> ime : allImeSettingsMap) {
+ if (needsSeparator) {
+ b.append(INPUT_METHOD_SEPARATER);
+ }
+ buildEnabledInputMethodsSettingString(b, ime);
+ needsSeparator = true;
+ }
+ return b.toString();
+ }
+
+ public static List<Pair<String, ArrayList<String>>> buildInputMethodsAndSubtypeList(
+ String enabledInputMethodsStr,
+ TextUtils.SimpleStringSplitter inputMethodSplitter,
+ TextUtils.SimpleStringSplitter subtypeSplitter) {
+ ArrayList<Pair<String, ArrayList<String>>> imsList =
+ new ArrayList<Pair<String, ArrayList<String>>>();
+ if (TextUtils.isEmpty(enabledInputMethodsStr)) {
+ return imsList;
+ }
+ inputMethodSplitter.setString(enabledInputMethodsStr);
+ while (inputMethodSplitter.hasNext()) {
+ String nextImsStr = inputMethodSplitter.next();
+ subtypeSplitter.setString(nextImsStr);
+ if (subtypeSplitter.hasNext()) {
+ ArrayList<String> subtypeHashes = new ArrayList<String>();
+ // The first element is ime id.
+ String imeId = subtypeSplitter.next();
+ while (subtypeSplitter.hasNext()) {
+ subtypeHashes.add(subtypeSplitter.next());
+ }
+ imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
+ }
+ }
+ return imsList;
+ }
+
public InputMethodSettings(
Resources res, ContentResolver resolver,
HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
@@ -875,27 +913,9 @@
}
public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
- ArrayList<Pair<String, ArrayList<String>>> imsList
- = new ArrayList<Pair<String, ArrayList<String>>>();
- final String enabledInputMethodsStr = getEnabledInputMethodsStr();
- if (TextUtils.isEmpty(enabledInputMethodsStr)) {
- return imsList;
- }
- mInputMethodSplitter.setString(enabledInputMethodsStr);
- while (mInputMethodSplitter.hasNext()) {
- String nextImsStr = mInputMethodSplitter.next();
- mSubtypeSplitter.setString(nextImsStr);
- if (mSubtypeSplitter.hasNext()) {
- ArrayList<String> subtypeHashes = new ArrayList<String>();
- // The first element is ime id.
- String imeId = mSubtypeSplitter.next();
- while (mSubtypeSplitter.hasNext()) {
- subtypeHashes.add(mSubtypeSplitter.next());
- }
- imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
- }
- }
- return imsList;
+ return buildInputMethodsAndSubtypeList(getEnabledInputMethodsStr(),
+ mInputMethodSplitter,
+ mSubtypeSplitter);
}
public void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 65a970a..eaa0dc7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -109,7 +109,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 120 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 121 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -310,6 +310,9 @@
boolean mPowerSaveModeEnabled;
StopwatchTimer mPowerSaveModeEnabledTimer;
+ boolean mDeviceIdling;
+ StopwatchTimer mDeviceIdlingTimer;
+
boolean mDeviceIdleModeEnabled;
StopwatchTimer mDeviceIdleModeEnabledTimer;
@@ -414,6 +417,7 @@
int mMinDischargeStepLevel;
final LevelStepTracker mDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
final LevelStepTracker mDailyDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS*2);
+ ArrayList<PackageChange> mDailyPackageChanges;
int mLastChargeStepLevel;
int mMaxChargeStepLevel;
@@ -3417,9 +3421,26 @@
}
public void noteDeviceIdleModeLocked(boolean enabled, boolean fromActive, boolean fromMotion) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ boolean nowIdling = enabled;
+ if (mDeviceIdling && !enabled && !fromActive && !fromMotion) {
+ // We don't go out of general idling mode until explicitly taken out of
+ // device idle through going active or significant motion.
+ nowIdling = true;
+ }
+ if (mDeviceIdling != nowIdling) {
+ mDeviceIdling = nowIdling;
+ int stepState = nowIdling ? STEP_LEVEL_MODE_DEVICE_IDLE : 0;
+ mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_DEVICE_IDLE) ^ stepState;
+ mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_DEVICE_IDLE) | stepState;
+ if (enabled) {
+ mDeviceIdlingTimer.startRunningLocked(elapsedRealtime);
+ } else {
+ mDeviceIdlingTimer.stopRunningLocked(elapsedRealtime);
+ }
+ }
if (mDeviceIdleModeEnabled != enabled) {
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- final long uptime = SystemClock.uptimeMillis();
mDeviceIdleModeEnabled = enabled;
if (fromMotion) {
addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_SIGNIFICANT_MOTION,
@@ -3449,7 +3470,11 @@
final long uptime = SystemClock.uptimeMillis();
addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_INSTALLED,
pkgName, versionCode);
- mNumConnectivityChange++;
+ PackageChange pc = new PackageChange();
+ pc.mPackageName = pkgName;
+ pc.mUpdate = true;
+ pc.mVersionCode = versionCode;
+ addPackageChange(pc);
}
public void notePackageUninstalledLocked(String pkgName) {
@@ -3457,7 +3482,17 @@
final long uptime = SystemClock.uptimeMillis();
addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_UNINSTALLED,
pkgName, 0);
- mNumConnectivityChange++;
+ PackageChange pc = new PackageChange();
+ pc.mPackageName = pkgName;
+ pc.mUpdate = true;
+ addPackageChange(pc);
+ }
+
+ private void addPackageChange(PackageChange pc) {
+ if (mDailyPackageChanges == null) {
+ mDailyPackageChanges = new ArrayList<>();
+ }
+ mDailyPackageChanges.add(pc);
}
public void notePhoneOnLocked() {
@@ -4262,6 +4297,14 @@
return mDeviceIdleModeEnabledTimer.getCountLocked(which);
}
+ @Override public long getDeviceIdlingTime(long elapsedRealtimeUs, int which) {
+ return mDeviceIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public int getDeviceIdlingCount(int which) {
+ return mDeviceIdlingTimer.getCountLocked(which);
+ }
+
@Override public int getNumConnectivityChange(int which) {
int val = mNumConnectivityChange;
if (which == STATS_CURRENT) {
@@ -6724,6 +6767,7 @@
mInteractiveTimer = new StopwatchTimer(null, -10, null, mOnBatteryTimeBase);
mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase);
mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase);
+ mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase);
mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null,
@@ -6849,6 +6893,11 @@
mDailyChargeStepTracker.mNumStepDurations,
mDailyChargeStepTracker.mStepDurations);
}
+ if (mDailyPackageChanges != null) {
+ hasData = true;
+ item.mPackageChanges = mDailyPackageChanges;
+ mDailyPackageChanges = null;
+ }
mDailyDischargeStepTracker.init();
mDailyChargeStepTracker.init();
updateDailyDeadlineLocked();
@@ -6899,6 +6948,21 @@
out.attribute(null, "end", Long.toString(dit.mEndTime));
writeDailyLevelSteps(out, "dis", dit.mDischargeSteps, sb);
writeDailyLevelSteps(out, "chg", dit.mChargeSteps, sb);
+ if (dit.mPackageChanges != null) {
+ for (int j=0; j<dit.mPackageChanges.size(); j++) {
+ PackageChange pc = dit.mPackageChanges.get(j);
+ if (pc.mUpdate) {
+ out.startTag(null, "upd");
+ out.attribute(null, "pkg", pc.mPackageName);
+ out.attribute(null, "ver", Integer.toString(pc.mVersionCode));
+ out.endTag(null, "upd");
+ } else {
+ out.startTag(null, "rem");
+ out.attribute(null, "pkg", pc.mPackageName);
+ out.endTag(null, "rem");
+ }
+ }
+ }
out.endTag(null, "item");
}
out.endTag(null, "daily-items");
@@ -7011,6 +7075,26 @@
readDailyItemTagDetailsLocked(parser, dit, false, "dis");
} else if (tagName.equals("chg")) {
readDailyItemTagDetailsLocked(parser, dit, true, "chg");
+ } else if (tagName.equals("upd")) {
+ if (dit.mPackageChanges == null) {
+ dit.mPackageChanges = new ArrayList<>();
+ }
+ PackageChange pc = new PackageChange();
+ pc.mUpdate = true;
+ pc.mPackageName = parser.getAttributeValue(null, "pkg");
+ String verStr = parser.getAttributeValue(null, "ver");
+ pc.mVersionCode = verStr != null ? Integer.parseInt(verStr) : 0;
+ dit.mPackageChanges.add(pc);
+ XmlUtils.skipCurrentTag(parser);
+ } else if (tagName.equals("rem")) {
+ if (dit.mPackageChanges == null) {
+ dit.mPackageChanges = new ArrayList<>();
+ }
+ PackageChange pc = new PackageChange();
+ pc.mUpdate = false;
+ pc.mPackageName = parser.getAttributeValue(null, "pkg");
+ dit.mPackageChanges.add(pc);
+ XmlUtils.skipCurrentTag(parser);
} else {
Slog.w(TAG, "Unknown element under <item>: "
+ parser.getName());
@@ -7295,6 +7379,7 @@
mInteractiveTimer.reset(false);
mPowerSaveModeEnabledTimer.reset(false);
mDeviceIdleModeEnabledTimer.reset(false);
+ mDeviceIdlingTimer.reset(false);
mPhoneOnTimer.reset(false);
mAudioOnTimer.reset(false);
mVideoOnTimer.reset(false);
@@ -8083,6 +8168,11 @@
return mDailyChargeStepTracker;
}
+ @Override
+ public ArrayList<PackageChange> getDailyPackageChanges() {
+ return mDailyPackageChanges;
+ }
+
long getBatteryUptimeLocked() {
return mOnBatteryTimeBase.getUptime(SystemClock.uptimeMillis() * 1000);
}
@@ -8583,6 +8673,20 @@
mChargeStepTracker.readFromParcel(in);
mDailyDischargeStepTracker.readFromParcel(in);
mDailyChargeStepTracker.readFromParcel(in);
+ int NPKG = in.readInt();
+ if (NPKG > 0) {
+ mDailyPackageChanges = new ArrayList<>(NPKG);
+ while (NPKG > 0) {
+ NPKG--;
+ PackageChange pc = new PackageChange();
+ pc.mPackageName = in.readString();
+ pc.mUpdate = in.readInt() != 0;
+ pc.mVersionCode = in.readInt();
+ mDailyPackageChanges.add(pc);
+ }
+ } else {
+ mDailyPackageChanges = null;
+ }
mDailyStartTime = in.readLong();
mNextMinDailyDeadline = in.readLong();
mNextMaxDailyDeadline = in.readLong();
@@ -8599,6 +8703,7 @@
mPhoneOn = false;
mPowerSaveModeEnabledTimer.readSummaryFromParcelLocked(in);
mDeviceIdleModeEnabledTimer.readSummaryFromParcelLocked(in);
+ mDeviceIdlingTimer.readSummaryFromParcelLocked(in);
mPhoneOnTimer.readSummaryFromParcelLocked(in);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
@@ -8890,6 +8995,18 @@
mChargeStepTracker.writeToParcel(out);
mDailyDischargeStepTracker.writeToParcel(out);
mDailyChargeStepTracker.writeToParcel(out);
+ if (mDailyPackageChanges != null) {
+ final int NPKG = mDailyPackageChanges.size();
+ out.writeInt(NPKG);
+ for (int i=0; i<NPKG; i++) {
+ PackageChange pc = mDailyPackageChanges.get(i);
+ out.writeString(pc.mPackageName);
+ out.writeInt(pc.mUpdate ? 1 : 0);
+ out.writeInt(pc.mVersionCode);
+ }
+ } else {
+ out.writeInt(0);
+ }
out.writeLong(mDailyStartTime);
out.writeLong(mNextMinDailyDeadline);
out.writeLong(mNextMaxDailyDeadline);
@@ -8901,6 +9018,7 @@
mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mPowerSaveModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mDeviceIdleModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -9201,6 +9319,7 @@
mPhoneOn = false;
mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in);
mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase, in);
+ mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase, in);
mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase, in);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i,
@@ -9367,6 +9486,7 @@
mInteractiveTimer.writeToParcel(out, uSecRealtime);
mPowerSaveModeEnabledTimer.writeToParcel(out, uSecRealtime);
mDeviceIdleModeEnabledTimer.writeToParcel(out, uSecRealtime);
+ mDeviceIdlingTimer.writeToParcel(out, uSecRealtime);
mPhoneOnTimer.writeToParcel(out, uSecRealtime);
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
mPhoneSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
@@ -9507,6 +9627,8 @@
mPowerSaveModeEnabledTimer.logState(pr, " ");
pr.println("*** Device idle mode timer:");
mDeviceIdleModeEnabledTimer.logState(pr, " ");
+ pr.println("*** Device idling timer:");
+ mDeviceIdlingTimer.logState(pr, " ");
pr.println("*** Phone timer:");
mPhoneOnTimer.logState(pr, " ");
for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
diff --git a/core/java/com/android/internal/util/ScreenShapeHelper.java b/core/java/com/android/internal/util/ScreenShapeHelper.java
new file mode 100644
index 0000000..1bcc7a0
--- /dev/null
+++ b/core/java/com/android/internal/util/ScreenShapeHelper.java
@@ -0,0 +1,48 @@
+package com.android.internal.util;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewRootImpl;
+
+import com.android.internal.R;
+
+/**
+ * @hide
+ */
+public class ScreenShapeHelper {
+ private static final boolean IS_EMULATOR = Build.HARDWARE.contains("goldfish");
+
+ /**
+ * Return the bottom pixel window outset of a window given its style attributes.
+ * @param displayMetrics Display metrics of the current device
+ * @param windowStyle Window style attributes for the window.
+ * @return An outset dimension in pixels or 0 if no outset should be applied.
+ */
+ public static int getWindowOutsetBottomPx(DisplayMetrics displayMetrics,
+ TypedArray windowStyle) {
+ if (IS_EMULATOR) {
+ return SystemProperties.getInt(ViewRootImpl.PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX, 0);
+ } else if (windowStyle.hasValue(R.styleable.Window_windowOutsetBottom)) {
+ TypedValue outsetBottom = new TypedValue();
+ windowStyle.getValue(R.styleable.Window_windowOutsetBottom, outsetBottom);
+ return (int) outsetBottom.getDimension(displayMetrics);
+ }
+ return 0;
+ }
+
+ /**
+ * Get whether a device has has a round screen.
+ */
+ public static boolean getWindowIsRound(Resources resources) {
+ if (IS_EMULATOR) {
+ return SystemProperties.getBoolean(ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false);
+ } else {
+ return resources.getBoolean(
+ com.android.internal.R.bool.config_windowIsRound);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/AccessibleDateAnimator.java b/core/java/com/android/internal/widget/AccessibleDateAnimator.java
deleted file mode 100644
index f97a5d1..0000000
--- a/core/java/com/android/internal/widget/AccessibleDateAnimator.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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 com.android.internal.widget;
-
-import android.content.Context;
-import android.text.format.DateUtils;
-import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ViewAnimator;
-
-/**
- * @hide
- */
-public class AccessibleDateAnimator extends ViewAnimator {
- private long mDateMillis;
-
- public AccessibleDateAnimator(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void setDateMillis(long dateMillis) {
- mDateMillis = dateMillis;
- }
-
- /**
- * Announce the currently-selected date when launched.
- */
- @Override
- public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
- if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
- // Clear the event's current text so that only the current date will be spoken.
- event.getText().clear();
- int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR |
- DateUtils.FORMAT_SHOW_WEEKDAY;
-
- String dateString = DateUtils.formatDateTime(getContext(), mDateMillis, flags);
- event.getText().add(dateString);
- return true;
- }
- return super.dispatchPopulateAccessibilityEventInternal(event);
- }
-}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index f916e6f..8018942 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -137,7 +137,7 @@
private int mRestoredCurItem = -1;
private Parcelable mRestoredAdapterState = null;
private ClassLoader mRestoredClassLoader = null;
- private Scroller mScroller;
+ private final Scroller mScroller;
private PagerObserver mObserver;
private int mPageMargin;
@@ -162,9 +162,9 @@
private boolean mIsBeingDragged;
private boolean mIsUnableToDrag;
- private int mDefaultGutterSize;
+ private final int mDefaultGutterSize;
private int mGutterSize;
- private int mTouchSlop;
+ private final int mTouchSlop;
/**
* Position of the last motion event.
*/
@@ -187,10 +187,10 @@
* Determines speed during touch scrolling
*/
private VelocityTracker mVelocityTracker;
- private int mMinimumVelocity;
- private int mMaximumVelocity;
- private int mFlingDistance;
- private int mCloseEnough;
+ private final int mMinimumVelocity;
+ private final int mMaximumVelocity;
+ private final int mFlingDistance;
+ private final int mCloseEnough;
// If the pager is at least this close to its final position, complete the scroll
// on touch down and let the user interact with the content inside instead of
@@ -200,8 +200,8 @@
private boolean mFakeDragging;
private long mFakeDragBeginTime;
- private EdgeEffect mLeftEdge;
- private EdgeEffect mRightEdge;
+ private final EdgeEffect mLeftEdge;
+ private final EdgeEffect mRightEdge;
private boolean mFirstLayout = true;
private boolean mNeedCalculatePageOffsets = false;
@@ -339,20 +339,24 @@
interface Decor {}
public ViewPager(Context context) {
- super(context);
- initViewPager();
+ this(context, null);
}
public ViewPager(Context context, AttributeSet attrs) {
- super(context, attrs);
- initViewPager();
+ this(context, attrs, 0);
}
- void initViewPager() {
+ public ViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public ViewPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
setWillNotDraw(false);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setFocusable(true);
- final Context context = getContext();
+
mScroller = new Scroller(context, sInterpolator);
final ViewConfiguration configuration = ViewConfiguration.get(context);
final float density = context.getResources().getDisplayMetrics().density;
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 441af15..34de6c5 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -537,7 +537,6 @@
*/
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
- int result = -1;
JavaVMInitArgs initArgs;
char propBuf[PROPERTY_VALUE_MAX];
char stackTraceFileBuf[sizeof("-Xstacktracefile:")-1 + PROPERTY_VALUE_MAX];
@@ -581,6 +580,7 @@
char localeOption[sizeof("-Duser.locale=") + PROPERTY_VALUE_MAX];
char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:")-1 + PROPERTY_VALUE_MAX];
char nativeBridgeLibrary[sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX];
+ char cpuAbiListBuf[sizeof("--cpu-abilist=") + PROPERTY_VALUE_MAX];
bool checkJni = false;
property_get("dalvik.vm.checkjni", propBuf, "");
@@ -699,7 +699,7 @@
if (!hasFile("/system/etc/preloaded-classes")) {
ALOGE("Missing preloaded-classes file, /system/etc/preloaded-classes not found: %s\n",
strerror(errno));
- goto bail;
+ return -1;
}
addOption("-Ximage-compiler-option");
addOption("--image-classes=/system/etc/preloaded-classes");
@@ -811,6 +811,19 @@
addOption(nativeBridgeLibrary);
}
+#if defined(__LP64__)
+ const char* cpu_abilist_property_name = "ro.product.cpu.abilist64";
+#else
+ const char* cpu_abilist_property_name = "ro.product.cpu.abilist32";
+#endif // defined(__LP64__)
+ property_get(cpu_abilist_property_name, propBuf, "");
+ if (propBuf[0] == '\0') {
+ ALOGE("%s is not expected to be empty", cpu_abilist_property_name);
+ return -1;
+ }
+ snprintf(cpuAbiListBuf, sizeof(cpuAbiListBuf), "--cpu-abilist=%s", propBuf);
+ addOption(cpuAbiListBuf);
+
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray();
initArgs.nOptions = mOptions.size();
@@ -825,13 +838,10 @@
*/
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
- goto bail;
+ return -1;
}
- result = 0;
-
-bail:
- return result;
+ return 0;
}
char* AndroidRuntime::toSlashClassName(const char* className)
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 3c1993e..9307ff9 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -33,6 +33,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <inttypes.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -173,7 +174,11 @@
static install_status_t
copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
{
- jstring* javaNativeLibPath = (jstring*) arg;
+ void** args = reinterpret_cast<void**>(arg);
+ jstring* javaNativeLibPath = (jstring*) args[0];
+ jboolean extractNativeLibs = *(jboolean*) args[1];
+ jboolean hasNativeBridge = *(jboolean*) args[2];
+
ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
size_t uncompLen;
@@ -181,13 +186,31 @@
long crc;
time_t modTime;
- if (!zipFile->getEntryInfo(zipEntry, NULL, &uncompLen, NULL, NULL, &when, &crc)) {
+ int method;
+ off64_t offset;
+
+ if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, NULL, &offset, &when, &crc)) {
ALOGD("Couldn't read zip entry info\n");
return INSTALL_FAILED_INVALID_APK;
- } else {
- struct tm t;
- ZipUtils::zipTimeToTimespec(when, &t);
- modTime = mktime(&t);
+ }
+
+ if (!extractNativeLibs) {
+ // check if library is uncompressed and page-aligned
+ if (method != ZipFileRO::kCompressStored) {
+ ALOGD("Library '%s' is compressed - will not be able to open it directly from apk.\n",
+ fileName);
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ if (offset % PAGE_SIZE != 0) {
+ ALOGD("Library '%s' is not page-aligned - will not be able to open it directly from"
+ " apk.\n", fileName);
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ if (!hasNativeBridge) {
+ return INSTALL_SUCCEEDED;
+ }
}
// Build local file path
@@ -208,6 +231,9 @@
}
// Only copy out the native file if it's different.
+ struct tm t;
+ ZipUtils::zipTimeToTimespec(when, &t);
+ modTime = mktime(&t);
struct stat64 st;
if (!isFileDifferent(localFileName, uncompLen, modTime, crc, &st)) {
return INSTALL_SUCCEEDED;
@@ -465,10 +491,12 @@
static jint
com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
- jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi)
+ jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
+ jboolean extractNativeLibs, jboolean hasNativeBridge)
{
+ void* args[] = { &javaNativeLibPath, &extractNativeLibs, &hasNativeBridge };
return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi,
- copyFileIfChanged, &javaNativeLibPath);
+ copyFileIfChanged, reinterpret_cast<void*>(args));
}
static jlong
@@ -548,7 +576,7 @@
"(J)V",
(void *)com_android_internal_content_NativeLibraryHelper_close},
{"nativeCopyNativeBinaries",
- "(JLjava/lang/String;Ljava/lang/String;)I",
+ "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
(void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
{"nativeSumNativeBinaries",
"(JLjava/lang/String;)J",
diff --git a/core/res/res/color/date_picker_header_text_material.xml b/core/res/res/color/date_picker_header_text_material.xml
index cda894b..baa8958 100644
--- a/core/res/res/color/date_picker_header_text_material.xml
+++ b/core/res/res/color/date_picker_header_text_material.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:state_selected="true"
+ android:state_activated="true"
android:color="?attr/textColorPrimaryInverse" />
<item
android:color="?attr/textColorSecondaryInverse" />
diff --git a/core/res/res/drawable/pointer_arrow_icon.xml b/core/res/res/drawable/pointer_arrow_icon.xml
index 8f7d658..72af0c1 100644
--- a/core/res/res/drawable/pointer_arrow_icon.xml
+++ b/core/res/res/drawable/pointer_arrow_icon.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:bitmap="@drawable/pointer_arrow"
- android:hotSpotX="6dp"
- android:hotSpotY="6dp" />
+ android:hotSpotX="5dp"
+ android:hotSpotY="5dp" />
diff --git a/core/res/res/layout-land/date_picker_holo.xml b/core/res/res/layout-land/date_picker_holo.xml
deleted file mode 100644
index 991888c..0000000
--- a/core/res/res/layout-land/date_picker_holo.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/datepicker_view_animator_height"
- android:gravity="center"
- android:orientation="horizontal"
- android:minWidth="@dimen/datepicker_dialog_width" >
-
- <include
- layout="@layout/date_picker_selected_date"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_weight="1" />
-
- <include layout="@layout/date_picker_view_animator" />
-
-</LinearLayout>
diff --git a/core/res/res/layout-land/date_picker_material.xml b/core/res/res/layout-land/date_picker_material.xml
new file mode 100644
index 0000000..1e711c5
--- /dev/null
+++ b/core/res/res/layout-land/date_picker_material.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <include
+ layout="@layout/date_picker_header_material"
+ android:layout_width="168dp"
+ android:layout_height="match_parent" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ <include
+ layout="@layout/date_picker_view_animator_material"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <ViewStub
+ android:id="@id/buttonPanel"
+ android:layout="@layout/alert_dialog_button_bar_material"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/day_picker_button_margin_top" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout-watch/progress_dialog_material.xml b/core/res/res/layout-watch/progress_dialog_material.xml
new file mode 100644
index 0000000..228f724
--- /dev/null
+++ b/core/res/res/layout-watch/progress_dialog_material.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:id="@+id/body"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:baselineAligned="false"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingTop="@dimen/dialog_padding_top_material"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:paddingBottom="@dimen/dialog_padding_top_material">
+
+ <ProgressBar
+ android:id="@id/progress"
+ style="?android:attr/progressBarStyleSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:max="10000"
+ android:layout_marginEnd="?attr/dialogPreferredPadding" />
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/alert_dialog_button_bar_material.xml b/core/res/res/layout/alert_dialog_button_bar_material.xml
index 891bcd5..1eea4e1 100644
--- a/core/res/res/layout/alert_dialog_button_bar_material.xml
+++ b/core/res/res/layout/alert_dialog_button_bar_material.xml
@@ -18,16 +18,16 @@
<com.android.internal.widget.ButtonBarLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/buttonPanel"
- style="?attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutDirection="locale"
android:orientation="horizontal"
android:paddingStart="12dp"
android:paddingEnd="12dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:gravity="bottom">
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:gravity="bottom"
+ style="?attr/buttonBarStyle">
<Button
android:id="@+id/button3"
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
new file mode 100644
index 0000000..bda7de9
--- /dev/null
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/date_picker_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="18dp"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:orientation="vertical"
+ tools:background="@color/accent_material_light"
+ tools:paddingStart="24dp"
+ tools:paddingEnd="24dp">
+
+ <!-- Top padding should stay on this view so that
+ the touch target is a bit larger. -->
+ <TextView
+ android:id="@+id/date_picker_header_year"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel"
+ tools:text="2015"
+ tools:textSize="@dimen/date_picker_year_label_size"
+ tools:textColor="@color/white" />
+
+ <TextView
+ android:id="@+id/date_picker_header_date"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
+ android:maxLines="2"
+ android:ellipsize="none"
+ tools:text="Thu, Sep 30"
+ tools:textSize="@dimen/date_picker_date_label_size"
+ tools:textColor="@color/white" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/date_picker_holo.xml b/core/res/res/layout/date_picker_material.xml
similarity index 72%
rename from core/res/res/layout/date_picker_holo.xml
rename to core/res/res/layout/date_picker_material.xml
index 72030ea..a1c97ff 100644
--- a/core/res/res/layout/date_picker_holo.xml
+++ b/core/res/res/layout/date_picker_material.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2011 The Android Open Source Project
+ 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.
@@ -14,17 +14,21 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/datepicker_component_width"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center"
android:orientation="vertical">
<include
- layout="@layout/date_picker_selected_date"
+ layout="@layout/date_picker_header_material"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- <include layout="@layout/date_picker_view_animator" />
+ <include
+ layout="@layout/date_picker_view_animator_material"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
</LinearLayout>
diff --git a/core/res/res/layout/date_picker_selected_date.xml b/core/res/res/layout/date_picker_selected_date.xml
deleted file mode 100644
index 9becb81..0000000
--- a/core/res/res/layout/date_picker_selected_date.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/day_picker_selector_layout"
- android:layout_width="@dimen/datepicker_component_width"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/date_picker_header"
- android:layout_width="match_parent"
- android:layout_height="@dimen/datepicker_header_height"
- android:gravity="center"
- android:importantForAccessibility="no" />
-
- <LinearLayout
- android:id="@+id/date_picker_month_day_year_layout"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:orientation="vertical"
- android:gravity="center">
-
- <LinearLayout
- android:id="@+id/date_picker_month_and_day_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clickable="true"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/date_picker_month"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:duplicateParentState="true"
- android:gravity="center" />
-
- <TextView
- android:id="@+id/date_picker_day"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="-23dp"
- android:layout_marginBottom="-20dp"
- android:duplicateParentState="true"
- android:gravity="center" />
- </LinearLayout>
-
- <TextView
- android:id="@+id/date_picker_year"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center" />
- </LinearLayout>
-
-</LinearLayout>
diff --git a/core/res/res/layout/date_picker_view_animator.xml b/core/res/res/layout/date_picker_view_animator.xml
deleted file mode 100644
index 9085ed5..0000000
--- a/core/res/res/layout/date_picker_view_animator.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<com.android.internal.widget.AccessibleDateAnimator
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/animator"
- android:layout_width="@dimen/datepicker_component_width"
- android:layout_height="@dimen/datepicker_view_animator_height"
- android:gravity="center" />
\ No newline at end of file
diff --git a/core/res/res/layout/date_picker_view_animator_material.xml b/core/res/res/layout/date_picker_view_animator_material.xml
new file mode 100644
index 0000000..98ef1dd
--- /dev/null
+++ b/core/res/res/layout/date_picker_view_animator_material.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ViewAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/animator"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:gravity="center">
+
+ <android.widget.DayPickerView
+ android:id="@+id/date_picker_day_picker"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/day_picker_padding_horizontal"
+ android:paddingEnd="@dimen/day_picker_padding_horizontal"
+ android:paddingTop="@dimen/day_picker_padding_top" />
+
+ <android.widget.YearPickerView
+ android:id="@+id/date_picker_year_picker"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</ViewAnimator>
diff --git a/core/res/res/layout/year_label_text_view.xml b/core/res/res/layout/year_label_text_view.xml
index e5bd068..6240c4b 100644
--- a/core/res/res/layout/year_label_text_view.xml
+++ b/core/res/res/layout/year_label_text_view.xml
@@ -13,10 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<android.widget.TextViewWithCircularIndicator
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/month_text_view"
- android:layout_width="match_parent"
- android:layout_height="@dimen/datepicker_year_label_height"
- android:layout_gravity="center"
- android:gravity="center" />
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/month_text_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?attr/listPreferredItemHeightSmall"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:gravity="center" />
diff --git a/core/res/res/values-land/dimens_material.xml b/core/res/res/values-land/dimens_material.xml
index 379ccf6..202f4a4 100644
--- a/core/res/res/values-land/dimens_material.xml
+++ b/core/res/res/values-land/dimens_material.xml
@@ -48,4 +48,14 @@
<dimen name="timepicker_text_inset_inner">46dp</dimen>
<dimen name="timepicker_text_size_normal">14sp</dimen>
<dimen name="timepicker_text_size_inner">12sp</dimen>
+
+ <!-- Used by Material-style SimpleMonthView -->
+ <dimen name="date_picker_month_height">40dp</dimen>
+ <dimen name="date_picker_day_of_week_height">14dp</dimen>
+ <dimen name="date_picker_day_height">32dp</dimen>
+ <dimen name="date_picker_day_width">46dp</dimen>
+ <dimen name="date_picker_day_selector_radius">16dp</dimen>
+ <dimen name="day_picker_padding_horizontal">18dp</dimen>
+ <dimen name="day_picker_padding_top">0dp</dimen>
+ <dimen name="day_picker_button_margin_top">-8dp</dimen>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 15797dd..79a6bde 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1036,7 +1036,7 @@
<attr name="colorSwitchThumbNormal" format="color" />
<!-- @hide The background used by framework controls. -->
- <attr name="controlBackground" format="color" />
+ <attr name="controlBackground" format="reference" />
<!-- The color applied to the edge effect on scrolling containers. -->
<attr name="colorEdgeEffect" format="color" />
@@ -2633,6 +2633,70 @@
<enum name="paddedBounds" value="3" />
</attr>
+ <!-- Defines the drawable to draw over the content. This can be used as an overlay.
+ The foreground drawable participates in the padding of the content if the gravity
+ is set to fill. -->
+ <attr name="foreground" format="reference|color" />
+ <!-- Defines the gravity to apply to the foreground drawable. The gravity defaults
+ to fill. -->
+ <attr name="foregroundGravity">
+ <!-- Push object to the top of its container, not changing its size. -->
+ <flag name="top" value="0x30" />
+ <!-- Push object to the bottom of its container, not changing its size. -->
+ <flag name="bottom" value="0x50" />
+ <!-- Push object to the left of its container, not changing its size. -->
+ <flag name="left" value="0x03" />
+ <!-- Push object to the right of its container, not changing its size. -->
+ <flag name="right" value="0x05" />
+ <!-- Place object in the vertical center of its container, not changing its size. -->
+ <flag name="center_vertical" value="0x10" />
+ <!-- Grow the vertical size of the object if needed so it completely fills its container. -->
+ <flag name="fill_vertical" value="0x70" />
+ <!-- Place object in the horizontal center of its container, not changing its size. -->
+ <flag name="center_horizontal" value="0x01" />
+ <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->
+ <flag name="fill_horizontal" value="0x07" />
+ <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->
+ <flag name="center" value="0x11" />
+ <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->
+ <flag name="fill" value="0x77" />
+ <!-- Additional option that can be set to have the top and/or bottom edges of
+ the child clipped to its container's bounds.
+ The clip will be based on the vertical gravity: a top gravity will clip the bottom
+ edge, a bottom gravity will clip the top edge, and neither will clip both edges. -->
+ <flag name="clip_vertical" value="0x80" />
+ <!-- Additional option that can be set to have the left and/or right edges of
+ the child clipped to its container's bounds.
+ The clip will be based on the horizontal gravity: a left gravity will clip the right
+ edge, a right gravity will clip the left edge, and neither will clip both edges. -->
+ <flag name="clip_horizontal" value="0x08" />
+ </attr>
+ <!-- Defines whether the foreground drawable should be drawn inside the padding.
+ This property is turned on by default. -->
+ <attr name="foregroundInsidePadding" format="boolean" />
+ <!-- Tint to apply to the foreground. -->
+ <attr name="foregroundTint" format="color" />
+ <!-- Blending mode used to apply the foreground tint. -->
+ <attr name="foregroundTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
+
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -3366,72 +3430,9 @@
<attr name="padding" />
</declare-styleable>
<declare-styleable name="FrameLayout">
- <!-- Defines the drawable to draw over the content. This can be used as an overlay.
- The foreground drawable participates in the padding of the content if the gravity
- is set to fill. -->
- <attr name="foreground" format="reference|color" />
- <!-- Defines the gravity to apply to the foreground drawable. The gravity defaults
- to fill. -->
- <attr name="foregroundGravity">
- <!-- Push object to the top of its container, not changing its size. -->
- <flag name="top" value="0x30" />
- <!-- Push object to the bottom of its container, not changing its size. -->
- <flag name="bottom" value="0x50" />
- <!-- Push object to the left of its container, not changing its size. -->
- <flag name="left" value="0x03" />
- <!-- Push object to the right of its container, not changing its size. -->
- <flag name="right" value="0x05" />
- <!-- Place object in the vertical center of its container, not changing its size. -->
- <flag name="center_vertical" value="0x10" />
- <!-- Grow the vertical size of the object if needed so it completely fills its container. -->
- <flag name="fill_vertical" value="0x70" />
- <!-- Place object in the horizontal center of its container, not changing its size. -->
- <flag name="center_horizontal" value="0x01" />
- <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->
- <flag name="fill_horizontal" value="0x07" />
- <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->
- <flag name="center" value="0x11" />
- <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->
- <flag name="fill" value="0x77" />
- <!-- Additional option that can be set to have the top and/or bottom edges of
- the child clipped to its container's bounds.
- The clip will be based on the vertical gravity: a top gravity will clip the bottom
- edge, a bottom gravity will clip the top edge, and neither will clip both edges. -->
- <flag name="clip_vertical" value="0x80" />
- <!-- Additional option that can be set to have the left and/or right edges of
- the child clipped to its container's bounds.
- The clip will be based on the horizontal gravity: a left gravity will clip the right
- edge, a right gravity will clip the left edge, and neither will clip both edges. -->
- <flag name="clip_horizontal" value="0x08" />
- </attr>
- <!-- Defines whether the foreground drawable should be drawn inside the padding.
- This property is turned on by default. -->
- <attr name="foregroundInsidePadding" format="boolean" />
<!-- Determines whether to measure all children or just those in
the VISIBLE or INVISIBLE state when measuring. Defaults to false. -->
<attr name="measureAllChildren" format="boolean" />
- <!-- Tint to apply to the foreground. -->
- <attr name="foregroundTint" format="color" />
- <!-- Blending mode used to apply the foreground tint. -->
- <attr name="foregroundTintMode">
- <!-- The tint is drawn on top of the drawable.
- [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
- <enum name="src_over" value="3" />
- <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
- color channels are thrown out. [Sa * Da, Sc * Da] -->
- <enum name="src_in" value="5" />
- <!-- The tint is drawn above the drawable, but with the drawable’s alpha
- channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
- <enum name="src_atop" value="9" />
- <!-- Multiplies the color and alpha channels of the drawable with those of
- the tint. [Sa * Da, Sc * Dc] -->
- <enum name="multiply" value="14" />
- <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
- <enum name="screen" value="15" />
- <!-- Combines the tint and drawable color and alpha channels, clamping the
- result to valid color values. Saturate(S + D) -->
- <enum name="add" value="16" />
- </attr>
</declare-styleable>
<declare-styleable name="ExpandableListView">
<!-- Indicator shown beside the group View. This can be a stateful Drawable. -->
@@ -4443,46 +4444,42 @@
</declare-styleable>
<declare-styleable name="DatePicker">
- <!-- The first year (inclusive), for example "1940".
- {@deprecated Use minDate instead.} -->
+ <!-- The first year (inclusive), for example "1940". {@deprecated Use minDate instead.} -->
<attr name="startYear" format="integer" />
- <!-- The last year (inclusive), for example "2010".
- {@deprecated Use maxDate instead.} -->
+ <!-- The last year (inclusive), for example "2010". {@deprecated Use maxDate instead.} -->
<attr name="endYear" format="integer" />
- <!-- Whether the spinners are shown. -->
- <attr name="spinnersShown" format="boolean" />
- <!-- Whether the calendar view is shown. -->
- <attr name="calendarViewShown" format="boolean" />
+
+ <!-- The first day of week according to {@link java.util.Calendar}. -->
+ <attr name="firstDayOfWeek" />
<!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
<attr name="minDate" format="string" />
<!-- The maximal date shown by this calendar view in mm/dd/yyyy format. -->
<attr name="maxDate" format="string" />
- <!-- The first day of week according to {@link java.util.Calendar}. -->
- <attr name="firstDayOfWeek" />
+
+ <!-- Whether the spinners are shown. Only valid for "spinner" mode. -->
+ <attr name="spinnersShown" format="boolean" />
+ <!-- Whether the calendar view is shown. Only valid for "spinner" mode. -->
+ <attr name="calendarViewShown" format="boolean" />
+
<!-- @hide The layout of the date picker. -->
<attr name="internalLayout" format="reference" />
<!-- @hide The layout of the legacy DatePicker. -->
<attr name="legacyLayout" />
- <!-- The background color for the date selector 's day of week. -->
- <attr name="dayOfWeekBackground" format="color|reference" />
- <!-- The text color for the date selector's day of week. -->
- <attr name="dayOfWeekTextAppearance" format="reference" />
- <!-- The month's text appearance in the date selector. -->
- <attr name="headerMonthTextAppearance" format="reference" />
- <!-- The day of month's text appearance in the date selector. -->
- <attr name="headerDayOfMonthTextAppearance" format="reference" />
- <!-- The year's text appearance in the date selector. -->
- <attr name="headerYearTextAppearance" format="reference" />
- <!-- The background for the date selector. -->
+
+ <!-- The text color for the selected date header text, ex. "2014" or
+ "Tue, Mar 18". This should be a color state list where the
+ activated state will be used when the year picker or day picker is
+ active.-->
+ <attr name="headerTextColor" format="color" />
+ <!-- The background for the selected date header. -->
<attr name="headerBackground" />
+
<!-- The list year's text appearance in the list. -->
<attr name="yearListItemTextAppearance" format="reference" />
- <!-- The list year's selected circle color in the list. -->
- <attr name="yearListSelectorColor" format="color" />
+ <!-- @hide The list year's text appearance in the list when activated. -->
+ <attr name="yearListItemActivatedTextAppearance" format="reference" />
<!-- The text color list of the calendar. -->
<attr name="calendarTextColor" format="color" />
- <!-- @hide The activated background color for the calendar. -->
- <attr name="calendarDayBackgroundColor" format="color" />
<!-- Defines the look of the widget. Prior to the L release, the only choice was
spinner. As of L, with the Material theme selected, the default layout is calendar,
but this attribute can be used to force spinner to be used instead. -->
@@ -4492,6 +4489,19 @@
<!-- Date picker with calendar to select the date. -->
<enum name="calendar" value="2" />
</attr>
+
+ <!-- @deprecated The text appearance for the month (ex. May) in the selected date header. -->
+ <attr name="headerMonthTextAppearance" format="reference" />
+ <!-- @deprecated The text appearance for the day of month (ex. 28) in the selected date header. -->
+ <attr name="headerDayOfMonthTextAppearance" format="reference" />
+ <!-- The text appearance for the year (ex. 2014) in the selected date header. -->
+ <attr name="headerYearTextAppearance" format="reference" />
+ <!-- @deprecated The background color for the header's day of week. -->
+ <attr name="dayOfWeekBackground" format="color" />
+ <!-- @deprecated The text color for the header's day of week. -->
+ <attr name="dayOfWeekTextAppearance" format="reference" />
+ <!-- @deprecated The list year's selected circle color in the list. -->
+ <attr name="yearListSelectorColor" format="color" />
</declare-styleable>
<declare-styleable name="TwoLineListItem">
@@ -4709,35 +4719,42 @@
<declare-styleable name="CalendarView">
<!-- The first day of week according to {@link java.util.Calendar}. -->
<attr name="firstDayOfWeek" format="integer" />
- <!-- Whether do show week numbers. -->
- <attr name="showWeekNumber" format="boolean" />
<!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
<attr name="minDate" />
<!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
<attr name="maxDate" />
- <!-- The number of weeks to be shown. -->
- <attr name="shownWeekCount" format="integer"/>
- <!-- The background color for the selected week. -->
- <attr name="selectedWeekBackgroundColor" format="color|reference" />
- <!-- The color for the dates of the focused month. -->
- <attr name="focusedMonthDateColor" format="color|reference" />
- <!-- The color for the dates of an unfocused month. -->
- <attr name="unfocusedMonthDateColor" format="color|reference" />
- <!-- The color for the week numbers. -->
- <attr name="weekNumberColor" format="color|reference" />
- <!-- The color for the separator line between weeks. -->
- <attr name="weekSeparatorLineColor" format="color|reference" />
- <!-- Drawable for the vertical bar shown at the beginning and at the end of the selected date. -->
- <attr name="selectedDateVerticalBar" format="reference" />
- <!-- The text appearance for the week day abbreviation of the calendar header. -->
+ <!-- The text appearance for the month and year in the calendar header. -->
+ <attr name="monthTextAppearance" format="reference" />
+ <!-- The text appearance for the week day abbreviation in the calendar header. -->
<attr name="weekDayTextAppearance" format="reference" />
- <!-- The text appearance for the calendar dates. -->
+ <!-- The text appearance for the day numbers in the calendar grid. -->
<attr name="dateTextAppearance" format="reference" />
- <!-- The number of weeks to be shown. -->
+ <!-- @hide The background color used for the day selection indicator. -->
+ <attr name="daySelectorColor" format="color" />
+ <!-- @hide The background color used for the day highlight indicator. -->
+ <attr name="dayHighlightColor" format="color" />
+ <!-- @hide Which style of calendar delegate to use. -->
<attr name="calendarViewMode">
<enum name="holo" value="0" />
<enum name="material" value="1" />
</attr>
+
+ <!-- @deprecated Whether do show week numbers. -->
+ <attr name="showWeekNumber" format="boolean" />
+ <!-- @deprecated The number of weeks to be shown. -->
+ <attr name="shownWeekCount" format="integer"/>
+ <!-- @deprecated The background color for the selected week. -->
+ <attr name="selectedWeekBackgroundColor" format="color|reference" />
+ <!-- @deprecated The color for the dates of the focused month. -->
+ <attr name="focusedMonthDateColor" format="color|reference" />
+ <!-- @deprecated The color for the dates of an unfocused month. -->
+ <attr name="unfocusedMonthDateColor" format="color|reference" />
+ <!-- @deprecated The color for the week numbers. -->
+ <attr name="weekNumberColor" format="color|reference" />
+ <!-- @deprecated The color for the separator line between weeks. -->
+ <attr name="weekSeparatorLineColor" format="color|reference" />
+ <!-- @deprecated Drawable for the vertical bar shown at the beginning and at the end of the selected date. -->
+ <attr name="selectedDateVerticalBar" format="reference" />
</declare-styleable>
<declare-styleable name="NumberPicker">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c0b2cbe..aefb67c 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1039,6 +1039,10 @@
activity. -->
<attr name="resizeableActivity" format="boolean" />
+ <!-- When set installer will extract native libraries. If set to false
+ libraries in the apk must be stored and page-aligned. -->
+ <attr name="extractNativeLibs" format="boolean"/>
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
@@ -1169,8 +1173,8 @@
@hide -->
<attr name="usesCleartextTraffic" />
<attr name="multiArch" />
+ <attr name="extractNativeLibs" />
</declare-styleable>
-
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
features in your package (or other packages). See the
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index da68c92..1cb39f0 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -16,16 +16,19 @@
<!-- Colors specific to Material themes. -->
<resources>
- <color name="background_material_dark">#ff303030</color>
- <color name="background_material_light">#fffafafa</color>
- <color name="background_floating_material_dark">#ff424242</color>
- <color name="background_floating_material_light">#ffffffff</color>
+ <color name="foreground_material_dark">@color/white</color>
+ <color name="foreground_material_light">@color/black</color>
- <color name="primary_material_dark">#ff212121</color>
- <color name="primary_material_light">#fff5f5f5</color>
- <color name="primary_dark_material_dark">#ff000000</color>
- <color name="primary_dark_material_light">#ff757575</color>
- <color name="primary_dark_material_light_light_status_bar">#ffe0e0e0</color>
+ <color name="background_material_dark">@color/material_grey_850</color>
+ <color name="background_material_light">@color/material_grey_50</color>
+ <color name="background_floating_material_dark">@color/material_grey_800</color>
+ <color name="background_floating_material_light">@color/white</color>
+
+ <color name="primary_material_dark">@color/material_grey_900</color>
+ <color name="primary_material_light">@color/material_grey_100</color>
+ <color name="primary_dark_material_dark">@color/black</color>
+ <color name="primary_dark_material_light">@color/material_grey_600</color>
+ <color name="primary_dark_material_light_light_status_bar">@color/material_grey_300</color>
<color name="accent_material_light">@color/material_deep_teal_500</color>
<color name="accent_material_dark">@color/material_deep_teal_200</color>
@@ -38,19 +41,20 @@
<color name="switch_thumb_disabled_material_dark">#ff616161</color>
<color name="switch_thumb_disabled_material_light">#ffbdbdbd</color>
- <color name="foreground_material_dark">@color/white</color>
- <color name="foreground_material_light">@color/black</color>
-
- <color name="link_text_material_dark">@color/material_deep_teal_200</color>
<color name="link_text_material_light">@color/material_deep_teal_500</color>
+ <color name="link_text_material_dark">@color/material_deep_teal_200</color>
<!-- Text & foreground colors -->
<eat-comment />
+ <!-- 87% black -->
<color name="primary_text_default_material_light">#de000000</color>
+ <!-- 54% black -->
<color name="secondary_text_default_material_light">#8a000000</color>
+ <!-- 100% white -->
<color name="primary_text_default_material_dark">#ffffffff</color>
+ <!-- 70% white -->
<color name="secondary_text_default_material_dark">#b3ffffff</color>
<item name="hint_alpha_material_dark" format="float" type="dimen">0.50</item>
@@ -66,6 +70,14 @@
<!-- Primary & accent colors -->
<eat-comment />
+ <color name="material_grey_900">#ff212121</color>
+ <color name="material_grey_850">#ff303030</color>
+ <color name="material_grey_800">#ff424242</color>
+ <color name="material_grey_600">#ff757575</color>
+ <color name="material_grey_300">#ffe0e0e0</color>
+ <color name="material_grey_100">#fff5f5f5</color>
+ <color name="material_grey_50">#fffafafa</color>
+
<color name="material_deep_teal_200">#ff80cbc4</color>
<color name="material_deep_teal_500">#ff009688</color>
@@ -98,16 +110,16 @@
<color name="datepicker_default_disabled_text_color_material_light">#80999999</color>
<color name="datepicker_default_disabled_text_color_material_dark">#80999999</color>
- <color name="datepicker_default_selected_text_color_material_light">#33b5e5</color>
- <color name="datepicker_default_selected_text_color_material_dark">#33b5e5</color>
+ <color name="datepicker_default_selected_text_color_material_light">#ff33b5e5</color>
+ <color name="datepicker_default_selected_text_color_material_dark">#ff33b5e5</color>
- <color name="datepicker_default_pressed_text_color_material_light">#0099cc</color>
- <color name="datepicker_default_pressed_text_color_material_dark">#0099cc</color>
+ <color name="datepicker_default_pressed_text_color_material_light">#ff0099cc</color>
+ <color name="datepicker_default_pressed_text_color_material_dark">#ff0099cc</color>
<color name="datepicker_default_circle_background_color_material_light">@color/material_deep_teal_500</color>
<color name="datepicker_default_circle_background_color_material_dark">@color/material_deep_teal_200</color>
- <color name="datepicker_default_view_animator_color_material_light">#f2f2f2</color>
+ <color name="datepicker_default_view_animator_color_material_light">#fff2f2f2</color>
<color name="datepicker_default_view_animator_color_material_dark">#ff303030</color>
</resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 8d2afde..6fd39f6 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -143,9 +143,7 @@
<dimen name="timepicker_text_size_inner">12sp</dimen>
<!-- Material date picker dimensions. -->
- <dimen name="datepicker_year_picker_padding_top">8dp</dimen>
<dimen name="datepicker_year_label_height">64dp</dimen>
- <dimen name="datepicker_year_label_text_size">22dp</dimen>
<dimen name="datepicker_component_width">260dp</dimen>
<dimen name="datepicker_dialog_width">520dp</dimen>
<dimen name="datepicker_selected_date_day_size">88dp</dimen>
@@ -154,10 +152,24 @@
<dimen name="datepicker_header_height">30dp</dimen>
<dimen name="datepicker_header_text_size">14dp</dimen>
+ <dimen name="datepicker_list_year_label_size">16sp</dimen>
+ <dimen name="datepicker_list_year_activated_label_size">26sp</dimen>
+
+ <dimen name="date_picker_year_label_size">16sp</dimen>
+ <dimen name="date_picker_date_label_size">34dp</dimen>
+
<!-- Used by Material-style SimpleMonthView -->
- <dimen name="datepicker_day_number_size">12sp</dimen>
- <dimen name="datepicker_month_label_size">14sp</dimen>
- <dimen name="datepicker_month_day_label_text_size">12sp</dimen>
- <dimen name="datepicker_month_list_item_header_height">48dp</dimen>
+ <dimen name="date_picker_month_text_size">14sp</dimen>
+ <dimen name="date_picker_day_of_week_text_size">12sp</dimen>
+ <dimen name="date_picker_day_text_size">12sp</dimen>
+ <dimen name="date_picker_month_height">56dp</dimen>
+ <dimen name="date_picker_day_of_week_height">36dp</dimen>
+ <dimen name="date_picker_day_height">40dp</dimen>
+ <dimen name="date_picker_day_width">44dp</dimen>
+ <dimen name="date_picker_day_selector_radius">20dp</dimen>
+ <dimen name="day_picker_padding_horizontal">20dp</dimen>
+ <dimen name="day_picker_padding_top">6dp</dimen>
+ <dimen name="day_picker_button_margin_top">0dp</dimen>
+
<dimen name="datepicker_view_animator_height">226dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e507b3d..5e4b039 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2651,4 +2651,5 @@
<public type="attr" name="colorBackgroundFloating" />
+ <public type="attr" name="extractNativeLibs" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 19cae03..7672e93 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5042,18 +5042,6 @@
<!-- Accessibility announcement for minute circular picker [CHAR LIMIT=NONE] -->
<string name="select_minutes">Select minutes</string>
- <!--
- Content description for the month and day selector in the date picker, which displays
- a selectable grid of days laid out by month.
- [CHAR LIMIT=50]
- -->
- <string name="day_picker_description">Month grid of days</string>
- <!--
- Content description for the year selector in the date picker, which displays
- a scrolling, vertical list of years.
- [CHAR LIMIT=50]
- -->
- <string name="year_picker_description">Year list</string>
<!-- Accessibility announcement for the day picker [CHAR LIMIT=NONE] -->
<string name="select_day">Select month and day</string>
<!-- Accessibility announcement for the year picker [CHAR LIMIT=NONE] -->
@@ -5079,7 +5067,11 @@
<string name="sans_serif">sans-serif</string>
<!-- DO NOT TRANSLATE -->
- <string name="day_of_week_label_typeface">sans-serif</string>
+ <string name="date_picker_month_typeface">sans-serif-medium</string>
+ <!-- DO NOT TRANSLATE -->
+ <string name="date_picker_day_of_week_typeface">sans-serif-medium</string>
+ <!-- DO NOT TRANSLATE -->
+ <string name="date_picker_day_typeface">sans-serif-medium</string>
<!-- Notify use that they are in Lock-to-app -->
<string name="lock_to_app_toast">To unpin this screen, touch and hold Back and Overview at the same time.</string>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index a8ab18d..f1f7462 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -372,11 +372,13 @@
<style name="TextAppearance.Material.WindowTitle" parent="TextAppearance.Material.Title" />
<style name="TextAppearance.Material.DialogWindowTitle" parent="TextAppearance.Material.Title" />
- <style name="TextAppearance.Material.CalendarViewWeekDayView" parent="TextAppearance.Material.Small">
- <item name="textStyle">bold</item>
- <item name="textColor">#505050</item>
+ <style name="TextAppearance.Material.Widget.Calendar.Day" parent="TextAppearance.Material.Caption">
+ <item name="textColor">?attr/textColorPrimaryActivated</item>
</style>
+ <style name="TextAppearance.Material.Widget.Calendar.DayOfWeek" parent="TextAppearance.Material.Caption" />
+ <style name="TextAppearance.Material.Widget.Calendar.Month" parent="TextAppearance.Material.Body2" />
+
<style name="TextAppearance.Material.TimePicker.TimeLabel" parent="TextAppearance.Material">
<item name="textSize">@dimen/timepicker_time_label_size</item>
<item name="textColor">@color/time_picker_header_text_material</item>
@@ -406,14 +408,27 @@
</style>
<style name="TextAppearance.Material.DatePicker.YearLabel" parent="TextAppearance.Material">
- <item name="includeFontPadding">false</item>
<item name="textColor">@color/date_picker_header_text_material</item>
- <item name="textSize">@dimen/datepicker_selected_date_year_size</item>
+ <item name="textSize">@dimen/date_picker_year_label_size</item>
+ <item name="fontFamily">sans-serif-medium</item>
+ </style>
+
+ <style name="TextAppearance.Material.DatePicker.DateLabel" parent="TextAppearance.Material">
+ <item name="textColor">@color/date_picker_header_text_material</item>
+ <item name="textSize">@dimen/date_picker_date_label_size</item>
+ <item name="fontFamily">sans-serif-medium</item>
</style>
<style name="TextAppearance.Material.DatePicker.List.YearLabel" parent="TextAppearance.Material">
- <item name="textColor">?attr/textColorSecondaryActivated</item>
- <item name="textSize">@dimen/datepicker_year_label_text_size</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ <item name="textSize">@dimen/datepicker_list_year_label_size</item>
+ <item name="fontFamily">sans-serif</item>
+ </style>
+
+ <style name="TextAppearance.Material.DatePicker.List.YearLabel.Activated">
+ <item name="textColor">?attr/colorControlActivated</item>
+ <item name="textSize">@dimen/datepicker_list_year_activated_label_size</item>
+ <item name="fontFamily">sans-serif-medium</item>
</style>
<style name="TextAppearance.Material.Notification">
@@ -487,7 +502,8 @@
<!-- Alert dialog button bar button -->
<style name="Widget.Material.Button.ButtonBar.AlertDialog" parent="Widget.Material.Button.Borderless.Colored">
<item name="minWidth">64dp</item>
- <item name="maxLines">2</item>
+ <item name="singleLine">true</item>
+ <item name="ellipsize">none</item>
<item name="minHeight">@dimen/alert_dialog_button_bar_height</item>
</style>
@@ -615,14 +631,13 @@
</style>
<style name="Widget.Material.CalendarView" parent="Widget.CalendarView">
- <item name="selectedWeekBackgroundColor">#330099FF</item>
- <item name="focusedMonthDateColor">#FFFFFFFF</item>
- <item name="unfocusedMonthDateColor">#66FFFFFF</item>
- <item name="weekNumberColor">#33FFFFFF</item>
- <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>
+
+ <item name="monthTextAppearance">@style/TextAppearance.Material.Widget.Calendar.Month</item>
+ <item name="weekDayTextAppearance">@style/TextAppearance.Material.Widget.Calendar.DayOfWeek</item>
+ <item name="dateTextAppearance">@style/TextAppearance.Material.Widget.Calendar.Day</item>
+ <item name="daySelectorColor">?attr/colorControlActivated</item>
+ <item name="dayHighlightColor">?attr/colorControlHighlight</item>
</style>
<style name="Widget.Material.ImageButton" parent="Widget.ImageButton">
@@ -647,11 +662,11 @@
<item name="internalLayout">@layout/time_picker_material</item>
<item name="headerTimeTextAppearance">@style/TextAppearance.Material.TimePicker.TimeLabel</item>
<item name="headerAmPmTextAppearance">@style/TextAppearance.Material.TimePicker.AmPmLabel</item>
- <item name="headerBackground">@drawable/time_picker_header_material</item>
+ <item name="headerBackground">#ff555555</item>
<item name="numbersTextColor">?attr/textColorPrimaryActivated</item>
<item name="numbersInnerTextColor">?attr/textColorSecondaryActivated</item>
- <item name="numbersBackgroundColor">#10ffffff</item>
<item name="numbersSelectorColor">?attr/colorControlActivated</item>
+ <item name="numbersBackgroundColor">#ff555555</item>
<item name="amPmTextColor">?attr/textColorSecondary</item>
</style>
@@ -660,17 +675,10 @@
<item name="legacyLayout">@layout/date_picker_legacy_holo</item>
<item name="calendarViewShown">true</item>
<!-- Attributes for new-style DatePicker. -->
- <item name="internalLayout">@layout/date_picker_holo</item>
- <item name="dayOfWeekBackground">#10000000</item>
- <item name="dayOfWeekTextAppearance">@style/TextAppearance.Material.DatePicker.DayOfWeekLabel</item>
- <item name="headerMonthTextAppearance">@style/TextAppearance.Material.DatePicker.MonthLabel</item>
- <item name="headerDayOfMonthTextAppearance">@style/TextAppearance.Material.DatePicker.DayOfMonthLabel</item>
- <item name="headerYearTextAppearance">@style/TextAppearance.Material.DatePicker.YearLabel</item>
- <item name="headerBackground">?attr/colorAccent</item>
+ <item name="internalLayout">@layout/date_picker_material</item>
<item name="yearListItemTextAppearance">@style/TextAppearance.Material.DatePicker.List.YearLabel</item>
- <item name="yearListSelectorColor">?attr/colorControlActivated</item>
- <item name="calendarTextColor">?attr/textColorSecondaryActivated</item>
- <item name="calendarDayBackgroundColor">?attr/colorControlActivated</item>
+ <item name="yearListItemActivatedTextAppearance">@style/TextAppearance.Material.DatePicker.List.YearLabel.Activated</item>
+ <item name="headerBackground">#ff555555</item>
</style>
<style name="Widget.Material.ActivityChooserView" parent="Widget.ActivityChooserView">
@@ -1021,24 +1029,18 @@
<style name="Widget.Material.Light.GestureOverlayView" parent="Widget.Material.GestureOverlayView"/>
<style name="Widget.Material.Light.GridView" parent="Widget.Material.GridView"/>
<style name="Widget.Material.Light.ImageButton" parent="Widget.Material.ImageButton"/>
-
- <style name="Widget.Material.Light.CalendarView" parent="Widget.CalendarView">
- <item name="selectedWeekBackgroundColor">#330066ff</item>
- <item name="focusedMonthDateColor">#FF000000</item>
- <item name="unfocusedMonthDateColor">#7F08002B</item>
- <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.CalendarView" parent="Widget.Material.CalendarView" />
<style name="Widget.Material.Light.NumberPicker" parent="Widget.Material.NumberPicker"/>
<style name="Widget.Material.Light.TimePicker" parent="Widget.Material.TimePicker">
- <item name="numbersBackgroundColor">#10000000</item>
+ <item name="headerBackground">?attr/colorAccent</item>
+ <item name="numbersBackgroundColor">#ffeeeeee</item>
</style>
- <style name="Widget.Material.Light.DatePicker" parent="Widget.Material.DatePicker" />
+ <style name="Widget.Material.Light.DatePicker" parent="Widget.Material.DatePicker">
+ <item name="headerBackground">?attr/colorAccent</item>
+ </style>
+
<style name="Widget.Material.Light.ActivityChooserView" parent="Widget.Material.ActivityChooserView" />
<style name="Widget.Material.Light.ImageWell" parent="Widget.Material.ImageWell"/>
<style name="Widget.Material.Light.ListView" parent="Widget.Material.ListView"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 203b017..c4e9e8e 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1988,7 +1988,7 @@
<java-symbol type="layout" name="time_picker_material" />
<java-symbol type="layout" name="time_picker_header_material" />
<java-symbol type="layout" name="year_label_text_view" />
- <java-symbol type="layout" name="date_picker_holo" />
+ <java-symbol type="layout" name="date_picker_material" />
<java-symbol type="id" name="time_header" />
<java-symbol type="id" name="hours" />
@@ -1999,11 +1999,8 @@
<java-symbol type="id" name="radial_picker" />
<java-symbol type="id" name="separator" />
<java-symbol type="id" name="date_picker_header" />
- <java-symbol type="id" name="date_picker_month_and_day_layout" />
- <java-symbol type="id" name="day_picker_selector_layout" />
- <java-symbol type="id" name="date_picker_month" />
- <java-symbol type="id" name="date_picker_day" />
- <java-symbol type="id" name="date_picker_year" />
+ <java-symbol type="id" name="date_picker_header_year" />
+ <java-symbol type="id" name="date_picker_header_date" />
<java-symbol type="id" name="animator" />
<java-symbol type="string" name="done_label" />
@@ -2039,19 +2036,24 @@
<java-symbol type="string" name="muted_by" />
<java-symbol type="string" name="item_is_selected" />
- <java-symbol type="string" name="day_of_week_label_typeface" />
<java-symbol type="string" name="select_day" />
- <java-symbol type="string" name="day_picker_description" />
<java-symbol type="string" name="select_year" />
- <java-symbol type="string" name="year_picker_description" />
- <java-symbol type="dimen" name="datepicker_day_number_size" />
- <java-symbol type="dimen" name="datepicker_month_label_size" />
- <java-symbol type="dimen" name="datepicker_month_day_label_text_size" />
- <java-symbol type="dimen" name="datepicker_month_list_item_header_height" />
+ <java-symbol type="string" name="date_picker_month_typeface" />
+ <java-symbol type="string" name="date_picker_day_of_week_typeface" />
+ <java-symbol type="string" name="date_picker_day_typeface" />
+ <java-symbol type="dimen" name="date_picker_month_text_size" />
+ <java-symbol type="dimen" name="date_picker_day_of_week_text_size" />
+ <java-symbol type="dimen" name="date_picker_day_text_size" />
+ <java-symbol type="dimen" name="date_picker_month_height" />
+ <java-symbol type="dimen" name="date_picker_day_height" />
+ <java-symbol type="dimen" name="date_picker_day_width" />
+ <java-symbol type="dimen" name="date_picker_day_selector_radius" />
+ <java-symbol type="id" name="date_picker_day_picker" />
+ <java-symbol type="id" name="date_picker_year_picker" />
+
<java-symbol type="dimen" name="datepicker_view_animator_height" />
<java-symbol type="dimen" name="datepicker_year_label_height" />
- <java-symbol type="dimen" name="datepicker_year_picker_padding_top" />
<java-symbol type="array" name="config_clockTickVibePattern" />
<java-symbol type="array" name="config_calendarDateVibePattern" />
@@ -2115,7 +2117,6 @@
<java-symbol type="id" name="transitionTransform" />
<java-symbol type="id" name="parentMatrix" />
<java-symbol type="bool" name="config_auto_attach_data_on_creation" />
- <java-symbol type="id" name="date_picker_month_day_year_layout" />
<java-symbol type="attr" name="closeItemLayout" />
<java-symbol type="layout" name="resolver_different_item_header" />
<java-symbol type="array" name="config_default_vm_number" />
@@ -2167,4 +2168,10 @@
<java-symbol type="integer" name="config_screen_magnification_multi_tap_adjustment" />
<java-symbol type="dimen" name="config_screen_magnification_scaling_threshold" />
<java-symbol type="dimen" name="timepicker_selector_stroke"/>
+
+ <java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.Month" />
+ <java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.DayOfWeek" />
+ <java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.Day" />
+ <java-symbol type="dimen" name="day_picker_padding_top"/>
+ <java-symbol type="dimen" name="date_picker_day_of_week_height"/>
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 38cfecd..9931d00 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -857,6 +857,7 @@
<!-- Theme overlay that overrides window properties to display as a dialog. -->
<style name="ThemeOverlay.Material.Dialog">
<item name="colorBackgroundCacheHint">@null</item>
+ <item name="colorBackground">?attr/colorBackgroundFloating</item>
<item name="windowFrame">@null</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
@@ -1271,7 +1272,6 @@
<style name="Theme.Material.Settings" parent="Theme.Material.DayNight.DarkActionBar">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
- <item name="colorAccent">@color/material_deep_teal_500</item>
<item name="presentationTheme">@style/Theme.Material.Settings.Dialog.Presentation</item>
<item name="searchDialogTheme">@style/Theme.Material.Settings.SearchBar</item>
@@ -1281,7 +1281,6 @@
<style name="Theme.Material.Settings.BaseDialog" parent="Theme.Material.DayNight.BaseDialog">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
- <item name="colorAccent">@color/material_deep_teal_500</item>
</style>
<style name="Theme.Material.Settings.Dialog" parent="Theme.Material.Settings.BaseDialog" />
@@ -1289,7 +1288,6 @@
<style name="Theme.Material.Settings.Dialog.BaseAlert" parent="Theme.Material.DayNight.Dialog.BaseAlert">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
- <item name="colorAccent">@color/material_deep_teal_500</item>
</style>
<style name="Theme.Material.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.BaseAlert" />
@@ -1297,7 +1295,6 @@
<style name="Theme.Material.Settings.Dialog.Presentation" parent="Theme.Material.DayNight.Dialog.Presentation">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
- <item name="colorAccent">@color/material_deep_teal_500</item>
</style>
<style name="Theme.Material.Settings.SearchBar" parent="Theme.Material.DayNight.SearchBar">
@@ -1309,6 +1306,5 @@
<style name="Theme.Material.Settings.CompactMenu" parent="Theme.Material.DayNight.CompactMenu">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
- <item name="colorAccent">@color/material_deep_teal_500</item>
</style>
</resources>
diff --git a/core/tests/coretests/apks/install_jni_lib/Android.mk b/core/tests/coretests/apks/install_jni_lib/Android.mk
index b61ea8e..7322e8d 100644
--- a/core/tests/coretests/apks/install_jni_lib/Android.mk
+++ b/core/tests/coretests/apks/install_jni_lib/Android.mk
@@ -23,6 +23,14 @@
libnativehelper
LOCAL_MODULE := libframeworks_coretests_jni
+
+# this does not prevent build system
+# from installing library to /system/lib
LOCAL_MODULE_TAGS := tests
+# .. we want to avoid that... so we put it somewhere
+# bionic linker cant find it without outside help (nativetests):
+LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
index 957fc4a..e0b616c 100644
--- a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
+++ b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
@@ -27,8 +27,8 @@
{ "checkFunction", "()I", (void*) checkFunction },
};
-int register_com_android_framework_coretests_JNITests(JNIEnv* env) {
- return jniRegisterNativeMethods(env, "com/android/framework/coretests/JNITests", sMethods,
+int register_com_android_frameworks_coretests_JNITests(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/frameworks/coretests/JNITests", sMethods,
NELEM(sMethods));
}
@@ -46,7 +46,7 @@
return JNI_ERR;
}
- if ((status = android::register_com_android_framework_coretests_JNITests(e)) < 0) {
+ if ((status = android::register_com_android_frameworks_coretests_JNITests(e)) < 0) {
return JNI_ERR;
}
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
new file mode 100644
index 0000000..5fa2405
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := install_jni_lib_open_from_apk
+
+LOCAL_JNI_SHARED_LIBRARIES_ZIP_OPTIONS := -0
+LOCAL_PAGE_ALIGN_JNI_SHARED_LIBRARIES := true
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml b/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml
new file mode 100644
index 0000000..190f894
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_jni_lib_open_from_apk">
+
+ <application android:hasCode="true" android:label="@string/app_name" android:extractNativeLibs="false">
+ <activity android:name="com.android.frameworks.coretests.OpenFromApkActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/core/res/res/drawable/time_picker_header_material.xml b/core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml
similarity index 71%
rename from core/res/res/drawable/time_picker_header_material.xml
rename to core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml
index ef2068a..8c2a0bf 100644
--- a/core/res/res/drawable/time_picker_header_material.xml
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
limitations under the License.
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?attr/colorControlHighlight">
- <item android:drawable="?attr/colorAccent" />
-</ripple>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name">Load From Apk Test</string>
+</resources>
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java
new file mode 100644
index 0000000..4f9176c
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java
@@ -0,0 +1,26 @@
+/*
+ * 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 com.android.frameworks.coretests;
+
+public class JNITests {
+ static {
+ System.loadLibrary("frameworks_coretests_jni");
+ }
+
+ public static native int checkFunction();
+}
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java
new file mode 100644
index 0000000..524cad7c
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java
@@ -0,0 +1,38 @@
+/*
+ * 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 com.android.frameworks.coretests;
+
+import android.app.Activity;
+import android.widget.TextView;
+import android.os.Bundle;
+
+public class OpenFromApkActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ TextView tv = new TextView(this);
+
+ int i = JNITests.checkFunction();
+
+ tv.setText("All is well: i=" + i);
+
+ setContentView(tv);
+ }
+
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 6659769..c517201 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -58,11 +58,11 @@
<group gid="log" />
</permission>
- <permission name="android.permission.READ_EXTERNAL_STORAGE" >
+ <permission name="android.permission.READ_EXTERNAL_STORAGE" perUser="true" >
<group gid="sdcard_r" />
</permission>
- <permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
+ <permission name="android.permission.WRITE_EXTERNAL_STORAGE" perUser="true" >
<group gid="sdcard_r" />
<group gid="sdcard_rw" />
</permission>
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 17e5b0b..db489f6 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -16,6 +16,8 @@
import android.animation.Animator;
import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.animation.Animator.AnimatorListener;
import android.annotation.NonNull;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -37,6 +39,7 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
/**
* This class uses {@link android.animation.ObjectAnimator} and
@@ -312,6 +315,15 @@
eventType = parser.next();
}
+ setupAnimatorSet();
+ }
+
+ private void setupAnimatorSet() {
+ if (mAnimatedVectorState.mTempAnimators != null) {
+ mAnimatedVectorState.mAnimatorSet.playTogether(mAnimatedVectorState.mTempAnimators);
+ mAnimatedVectorState.mTempAnimators.clear();
+ mAnimatedVectorState.mTempAnimators = null;
+ }
}
@Override
@@ -330,10 +342,44 @@
}
}
+ /**
+ * Adds a listener to the set of listeners that are sent events through the life of an
+ * animation.
+ *
+ * @param listener the listener to be added to the current set of listeners for this animation.
+ */
+ public void addListener(AnimatorListener listener) {
+ mAnimatedVectorState.mAnimatorSet.addListener(listener);
+ }
+
+ /**
+ * Removes a listener from the set listening to this animation.
+ *
+ * @param listener the listener to be removed from the current set of listeners for this
+ * animation.
+ */
+ public void removeListener(AnimatorListener listener) {
+ mAnimatedVectorState.mAnimatorSet.removeListener(listener);
+ }
+
+ /**
+ * Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently
+ * listening for events on this <code>AnimatedVectorDrawable</code> object.
+ *
+ * @return List<AnimatorListener> The set of listeners.
+ */
+ public List<AnimatorListener> getListeners() {
+ return mAnimatedVectorState.mAnimatorSet.getListeners();
+ }
+
private static class AnimatedVectorDrawableState extends ConstantState {
int mChangingConfigurations;
VectorDrawable mVectorDrawable;
- ArrayList<Animator> mAnimators;
+ // Always have a valid animatorSet to handle all the listeners call.
+ AnimatorSet mAnimatorSet = new AnimatorSet();
+ // When parsing the XML, we build individual animator and store in this array. At the end,
+ // we add this array into the mAnimatorSet.
+ private ArrayList<Animator> mTempAnimators;
ArrayMap<Animator, String> mTargetNameMap;
public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy,
@@ -353,18 +399,23 @@
mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds());
mVectorDrawable.setAllowCaching(false);
}
- if (copy.mAnimators != null) {
- final int numAnimators = copy.mAnimators.size();
- mAnimators = new ArrayList<Animator>(numAnimators);
+ if (copy.mAnimatorSet != null) {
+ final int numAnimators = copy.mTargetNameMap.size();
+ // Deep copy a animator set, and then setup the target map again.
+ mAnimatorSet = copy.mAnimatorSet.clone();
mTargetNameMap = new ArrayMap<Animator, String>(numAnimators);
+ // Since the new AnimatorSet is cloned from the old one, the order must be the
+ // same inside the array.
+ ArrayList<Animator> oldAnim = copy.mAnimatorSet.getChildAnimations();
+ ArrayList<Animator> newAnim = mAnimatorSet.getChildAnimations();
+
for (int i = 0; i < numAnimators; ++i) {
- Animator anim = copy.mAnimators.get(i);
- Animator animClone = anim.clone();
- String targetName = copy.mTargetNameMap.get(anim);
- Object targetObject = mVectorDrawable.getTargetByName(targetName);
- animClone.setTarget(targetObject);
- mAnimators.add(animClone);
- mTargetNameMap.put(animClone, targetName);
+ // Target name must be the same for new and old
+ String targetName = copy.mTargetNameMap.get(oldAnim.get(i));
+
+ Object newTargetObject = mVectorDrawable.getTargetByName(targetName);
+ newAnim.get(i).setTarget(newTargetObject);
+ mTargetNameMap.put(newAnim.get(i), targetName);
}
}
} else {
@@ -397,11 +448,11 @@
private void setupAnimatorsForTarget(String name, Animator animator) {
Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name);
animator.setTarget(target);
- if (mAnimatedVectorState.mAnimators == null) {
- mAnimatedVectorState.mAnimators = new ArrayList<Animator>();
+ if (mAnimatedVectorState.mTempAnimators == null) {
+ mAnimatedVectorState.mTempAnimators = new ArrayList<Animator>();
mAnimatedVectorState.mTargetNameMap = new ArrayMap<Animator, String>();
}
- mAnimatedVectorState.mAnimators.add(animator);
+ mAnimatedVectorState.mTempAnimators.add(animator);
mAnimatedVectorState.mTargetNameMap.put(animator, name);
if (DBG_ANIMATION_VECTOR_DRAWABLE) {
Log.v(LOGTAG, "add animator for target " + name + " " + animator);
@@ -410,27 +461,11 @@
@Override
public boolean isRunning() {
- final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
- final int size = animators.size();
- for (int i = 0; i < size; i++) {
- final Animator animator = animators.get(i);
- if (animator.isRunning()) {
- return true;
- }
- }
- return false;
+ return mAnimatedVectorState.mAnimatorSet.isRunning();
}
private boolean isStarted() {
- final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
- final int size = animators.size();
- for (int i = 0; i < size; i++) {
- final Animator animator = animators.get(i);
- if (animator.isStarted()) {
- return true;
- }
- }
- return false;
+ return mAnimatedVectorState.mAnimatorSet.isStarted();
}
@Override
@@ -439,24 +474,13 @@
if (isStarted()) {
return;
}
- // Otherwise, kick off every animator.
- final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
- final int size = animators.size();
- for (int i = 0; i < size; i++) {
- final Animator animator = animators.get(i);
- animator.start();
- }
+ mAnimatedVectorState.mAnimatorSet.start();
invalidateSelf();
}
@Override
public void stop() {
- final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
- final int size = animators.size();
- for (int i = 0; i < size; i++) {
- final Animator animator = animators.get(i);
- animator.end();
- }
+ mAnimatedVectorState.mAnimatorSet.end();
}
/**
@@ -473,27 +497,14 @@
Log.w(LOGTAG, "AnimatedVectorDrawable can't reverse()");
return;
}
- final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
- final int size = animators.size();
- for (int i = 0; i < size; i++) {
- final Animator animator = animators.get(i);
- animator.reverse();
- }
+ mAnimatedVectorState.mAnimatorSet.reverse();
}
/**
* @hide
*/
public boolean canReverse() {
- final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
- final int size = animators.size();
- for (int i = 0; i < size; i++) {
- final Animator animator = animators.get(i);
- if (!animator.canReverse()) {
- return false;
- }
- }
- return true;
+ return mAnimatedVectorState.mAnimatorSet.canReverse();
}
private final Callback mCallback = new Callback() {
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index c5e53da..39a33ce 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -805,7 +805,6 @@
// is no need for deep copying.
private final Path mPath;
private final Path mRenderPath;
- private static final Matrix IDENTITY_MATRIX = new Matrix();
private final Matrix mFinalPathMatrix = new Matrix();
private Paint mStrokePaint;
@@ -948,7 +947,7 @@
public void draw(Canvas canvas, int w, int h, ColorFilter filter) {
// Travese the tree in pre-order to draw.
- drawGroupTree(mRootGroup, IDENTITY_MATRIX, canvas, w, h, filter);
+ drawGroupTree(mRootGroup, Matrix.IDENTITY_MATRIX, canvas, w, h, filter);
}
private void drawPath(VGroup vGroup, VPath vPath, Canvas canvas, int w, int h,
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index f935bb1..f0b07a6 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -36,6 +36,7 @@
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
+import java.security.spec.RSAKeyGenParameterSpec;
import android.util.Log;
import android.util.Base64;
@@ -713,6 +714,8 @@
args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null);
args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
+ args.addBlob(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
+ RSAKeyGenParameterSpec.F4.toByteArray());
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
int result = mKeyStore.generateKey(name, args, 0, outCharacteristics);
@@ -750,6 +753,8 @@
args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, new byte[] {0x01, 0x02, 0x03});
args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
+ args.addBlob(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
+ RSAKeyGenParameterSpec.F4.toByteArray());
KeyCharacteristics outCharacteristics = new KeyCharacteristics();
int result = mKeyStore.generateKey(name, args, 0, outCharacteristics);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ea4216c..0091790 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -418,6 +418,7 @@
}
void RenderProxy::dumpGraphicsMemory(int fd) {
+ if (!RenderThread::hasInstance()) return;
SETUP_TASK(dumpGraphicsMemory);
args->fd = fd;
args->thread = &RenderThread::getInstance();
diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h
index dcf5449..d4cfeeb 100644
--- a/libs/hwui/thread/Signal.h
+++ b/libs/hwui/thread/Signal.h
@@ -30,8 +30,10 @@
~Signal() { }
void signal() {
- Mutex::Autolock l(mLock);
- mSignaled = true;
+ {
+ Mutex::Autolock l(mLock);
+ mSignaled = true;
+ }
mCondition.signal(mType);
}
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index c69b2fd..f0ed0bb 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -109,8 +109,11 @@
return false;
}
- Mutex::Autolock l(mLock);
- ssize_t index = mTasks.add(task);
+ ssize_t index;
+ {
+ Mutex::Autolock l(mLock);
+ index = mTasks.add(task);
+ }
mSignal.signal();
return index >= 0;
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 6b37a34..069f7ff 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -82,6 +82,10 @@
* encrypted content, the samples returned from the extractor remain encrypted, they
* are only decrypted when the samples are delivered to the decoder.
* <p>
+ * MediaDrm methods throw {@link java.lang.IllegalStateException}
+ * when a method is called on a MediaDrm object that is in an invalid or inoperable
+ * state. This is typically due to incorrect application API usage, but may also
+ * be due to an unrecoverable failure in the DRM plugin or security hardware.
* <a name="Callbacks"></a>
* <h3>Callbacks</h3>
* <p>Applications should register for informational events in order
@@ -383,11 +387,27 @@
public static final int KEY_TYPE_RELEASE = 3;
/**
+ * Key request type is initial license request
+ */
+ public static final int REQUEST_TYPE_INITIAL = 0;
+
+ /**
+ * Key request type is license renewal
+ */
+ public static final int REQUEST_TYPE_RENEWAL = 1;
+
+ /**
+ * Key request type is license release
+ */
+ public static final int REQUEST_TYPE_RELEASE = 2;
+
+ /**
* Contains the opaque data an app uses to request keys from a license server
*/
public final static class KeyRequest {
private byte[] mData;
private String mDefaultUrl;
+ private int mRequestType;
KeyRequest() {}
@@ -402,6 +422,11 @@
* server URL from other sources.
*/
public String getDefaultUrl() { return mDefaultUrl; }
+
+ /**
+ * Get the type of the request
+ */
+ public int getRequestType() { return mRequestType; }
};
/**
@@ -460,7 +485,6 @@
* reprovisioning is required
* @throws DeniedByServerException if the response indicates that the
* server rejected the request
- * @throws ResourceBusyException if required resources are in use
*/
public native byte[] provideKeyResponse(byte[] scope, byte[] response)
throws NotProvisionedException, DeniedByServerException;
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index db6b38b..88d979e 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -32,7 +32,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.util.AndroidRuntimeException;
import android.util.Log;
@@ -112,7 +111,24 @@
* resumes.</p>
*/
public class SoundPool {
- private final SoundPoolDelegate mImpl;
+ static { System.loadLibrary("soundpool"); }
+
+ // SoundPool messages
+ //
+ // must match SoundPool.h
+ private static final int SAMPLE_LOADED = 1;
+
+ private final static String TAG = "SoundPool";
+ private final static boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private long mNativeContext; // accessed by native methods
+
+ private EventHandler mEventHandler;
+ private SoundPool.OnLoadCompleteListener mOnLoadCompleteListener;
+
+ private final Object mLock;
+ private final AudioAttributes mAttributes;
+ private final IAppOpsService mAppOps;
/**
* Constructor. Constructs a SoundPool object with the following
@@ -135,10 +151,374 @@
}
private SoundPool(int maxStreams, AudioAttributes attributes) {
- if (SystemProperties.getBoolean("config.disable_media", false)) {
- mImpl = new SoundPoolStub();
+ // do native setup
+ if (native_setup(new WeakReference<SoundPool>(this), maxStreams, attributes) != 0) {
+ throw new RuntimeException("Native setup failed");
+ }
+ mLock = new Object();
+ mAttributes = attributes;
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOps = IAppOpsService.Stub.asInterface(b);
+ }
+
+ /**
+ * Release the SoundPool resources.
+ *
+ * Release all memory and native resources used by the SoundPool
+ * object. The SoundPool can no longer be used and the reference
+ * should be set to null.
+ */
+ public native final void release();
+
+ protected void finalize() { release(); }
+
+ /**
+ * Load the sound from the specified path.
+ *
+ * @param path the path to the audio file
+ * @param priority the priority of the sound. Currently has no effect. Use
+ * a value of 1 for future compatibility.
+ * @return a sound ID. This value can be used to play or unload the sound.
+ */
+ public int load(String path, int priority) {
+ int id = 0;
+ try {
+ File f = new File(path);
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(f,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ if (fd != null) {
+ id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
+ fd.close();
+ }
+ } catch (java.io.IOException e) {
+ Log.e(TAG, "error loading " + path);
+ }
+ return id;
+ }
+
+ /**
+ * Load the sound from the specified APK resource.
+ *
+ * Note that the extension is dropped. For example, if you want to load
+ * a sound from the raw resource file "explosion.mp3", you would specify
+ * "R.raw.explosion" as the resource ID. Note that this means you cannot
+ * have both an "explosion.wav" and an "explosion.mp3" in the res/raw
+ * directory.
+ *
+ * @param context the application context
+ * @param resId the resource ID
+ * @param priority the priority of the sound. Currently has no effect. Use
+ * a value of 1 for future compatibility.
+ * @return a sound ID. This value can be used to play or unload the sound.
+ */
+ public int load(Context context, int resId, int priority) {
+ AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
+ int id = 0;
+ if (afd != null) {
+ id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority);
+ try {
+ afd.close();
+ } catch (java.io.IOException ex) {
+ //Log.d(TAG, "close failed:", ex);
+ }
+ }
+ return id;
+ }
+
+ /**
+ * Load the sound from an asset file descriptor.
+ *
+ * @param afd an asset file descriptor
+ * @param priority the priority of the sound. Currently has no effect. Use
+ * a value of 1 for future compatibility.
+ * @return a sound ID. This value can be used to play or unload the sound.
+ */
+ public int load(AssetFileDescriptor afd, int priority) {
+ if (afd != null) {
+ long len = afd.getLength();
+ if (len < 0) {
+ throw new AndroidRuntimeException("no length for fd");
+ }
+ return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority);
} else {
- mImpl = new SoundPoolImpl(this, maxStreams, attributes);
+ return 0;
+ }
+ }
+
+ /**
+ * Load the sound from a FileDescriptor.
+ *
+ * This version is useful if you store multiple sounds in a single
+ * binary. The offset specifies the offset from the start of the file
+ * and the length specifies the length of the sound within the file.
+ *
+ * @param fd a FileDescriptor object
+ * @param offset offset to the start of the sound
+ * @param length length of the sound
+ * @param priority the priority of the sound. Currently has no effect. Use
+ * a value of 1 for future compatibility.
+ * @return a sound ID. This value can be used to play or unload the sound.
+ */
+ public int load(FileDescriptor fd, long offset, long length, int priority) {
+ return _load(fd, offset, length, priority);
+ }
+
+ /**
+ * Unload a sound from a sound ID.
+ *
+ * Unloads the sound specified by the soundID. This is the value
+ * returned by the load() function. Returns true if the sound is
+ * successfully unloaded, false if the sound was already unloaded.
+ *
+ * @param soundID a soundID returned by the load() function
+ * @return true if just unloaded, false if previously unloaded
+ */
+ public native final boolean unload(int soundID);
+
+ /**
+ * Play a sound from a sound ID.
+ *
+ * Play the sound specified by the soundID. This is the value
+ * returned by the load() function. Returns a non-zero streamID
+ * if successful, zero if it fails. The streamID can be used to
+ * further control playback. Note that calling play() may cause
+ * another sound to stop playing if the maximum number of active
+ * streams is exceeded. A loop value of -1 means loop forever,
+ * a value of 0 means don't loop, other values indicate the
+ * number of repeats, e.g. a value of 1 plays the audio twice.
+ * The playback rate allows the application to vary the playback
+ * rate (pitch) of the sound. A value of 1.0 means play back at
+ * the original frequency. A value of 2.0 means play back twice
+ * as fast, and a value of 0.5 means playback at half speed.
+ *
+ * @param soundID a soundID returned by the load() function
+ * @param leftVolume left volume value (range = 0.0 to 1.0)
+ * @param rightVolume right volume value (range = 0.0 to 1.0)
+ * @param priority stream priority (0 = lowest priority)
+ * @param loop loop mode (0 = no loop, -1 = loop forever)
+ * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
+ * @return non-zero streamID if successful, zero if failed
+ */
+ public final int play(int soundID, float leftVolume, float rightVolume,
+ int priority, int loop, float rate) {
+ if (isRestricted()) {
+ leftVolume = rightVolume = 0;
+ }
+ return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
+ }
+
+ /**
+ * Pause a playback stream.
+ *
+ * Pause the stream specified by the streamID. This is the
+ * value returned by the play() function. If the stream is
+ * playing, it will be paused. If the stream is not playing
+ * (e.g. is stopped or was previously paused), calling this
+ * function will have no effect.
+ *
+ * @param streamID a streamID returned by the play() function
+ */
+ public native final void pause(int streamID);
+
+ /**
+ * Resume a playback stream.
+ *
+ * Resume the stream specified by the streamID. This
+ * is the value returned by the play() function. If the stream
+ * is paused, this will resume playback. If the stream was not
+ * previously paused, calling this function will have no effect.
+ *
+ * @param streamID a streamID returned by the play() function
+ */
+ public native final void resume(int streamID);
+
+ /**
+ * Pause all active streams.
+ *
+ * Pause all streams that are currently playing. This function
+ * iterates through all the active streams and pauses any that
+ * are playing. It also sets a flag so that any streams that
+ * are playing can be resumed by calling autoResume().
+ */
+ public native final void autoPause();
+
+ /**
+ * Resume all previously active streams.
+ *
+ * Automatically resumes all streams that were paused in previous
+ * calls to autoPause().
+ */
+ public native final void autoResume();
+
+ /**
+ * Stop a playback stream.
+ *
+ * Stop the stream specified by the streamID. This
+ * is the value returned by the play() function. If the stream
+ * is playing, it will be stopped. It also releases any native
+ * resources associated with this stream. If the stream is not
+ * playing, it will have no effect.
+ *
+ * @param streamID a streamID returned by the play() function
+ */
+ public native final void stop(int streamID);
+
+ /**
+ * Set stream volume.
+ *
+ * Sets the volume on the stream specified by the streamID.
+ * This is the value returned by the play() function. The
+ * value must be in the range of 0.0 to 1.0. If the stream does
+ * not exist, it will have no effect.
+ *
+ * @param streamID a streamID returned by the play() function
+ * @param leftVolume left volume value (range = 0.0 to 1.0)
+ * @param rightVolume right volume value (range = 0.0 to 1.0)
+ */
+ public final void setVolume(int streamID, float leftVolume, float rightVolume) {
+ if (isRestricted()) {
+ return;
+ }
+ _setVolume(streamID, leftVolume, rightVolume);
+ }
+
+ /**
+ * Similar, except set volume of all channels to same value.
+ * @hide
+ */
+ public void setVolume(int streamID, float volume) {
+ setVolume(streamID, volume, volume);
+ }
+
+ /**
+ * Change stream priority.
+ *
+ * Change the priority of the stream specified by the streamID.
+ * This is the value returned by the play() function. Affects the
+ * order in which streams are re-used to play new sounds. If the
+ * stream does not exist, it will have no effect.
+ *
+ * @param streamID a streamID returned by the play() function
+ */
+ public native final void setPriority(int streamID, int priority);
+
+ /**
+ * Set loop mode.
+ *
+ * Change the loop mode. A loop value of -1 means loop forever,
+ * a value of 0 means don't loop, other values indicate the
+ * number of repeats, e.g. a value of 1 plays the audio twice.
+ * If the stream does not exist, it will have no effect.
+ *
+ * @param streamID a streamID returned by the play() function
+ * @param loop loop mode (0 = no loop, -1 = loop forever)
+ */
+ public native final void setLoop(int streamID, int loop);
+
+ /**
+ * Change playback rate.
+ *
+ * The playback rate allows the application to vary the playback
+ * rate (pitch) of the sound. A value of 1.0 means playback at
+ * the original frequency. A value of 2.0 means playback twice
+ * as fast, and a value of 0.5 means playback at half speed.
+ * If the stream does not exist, it will have no effect.
+ *
+ * @param streamID a streamID returned by the play() function
+ * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
+ */
+ public native final void setRate(int streamID, float rate);
+
+ public interface OnLoadCompleteListener {
+ /**
+ * Called when a sound has completed loading.
+ *
+ * @param soundPool SoundPool object from the load() method
+ * @param sampleId the sample ID of the sound loaded.
+ * @param status the status of the load operation (0 = success)
+ */
+ public void onLoadComplete(SoundPool soundPool, int sampleId, int status);
+ }
+
+ /**
+ * Sets the callback hook for the OnLoadCompleteListener.
+ */
+ public void setOnLoadCompleteListener(OnLoadCompleteListener listener) {
+ synchronized(mLock) {
+ if (listener != null) {
+ // setup message handler
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mEventHandler = new EventHandler(looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mEventHandler = new EventHandler(looper);
+ } else {
+ mEventHandler = null;
+ }
+ } else {
+ mEventHandler = null;
+ }
+ mOnLoadCompleteListener = listener;
+ }
+ }
+
+ private boolean isRestricted() {
+ if ((mAttributes.getFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
+ return false;
+ }
+ try {
+ final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+ mAttributes.getUsage(),
+ Process.myUid(), ActivityThread.currentPackageName());
+ return mode != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ private native final int _load(FileDescriptor fd, long offset, long length, int priority);
+
+ private native final int native_setup(Object weakRef, int maxStreams,
+ Object/*AudioAttributes*/ attributes);
+
+ private native final int _play(int soundID, float leftVolume, float rightVolume,
+ int priority, int loop, float rate);
+
+ private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
+
+ // post event from native code to message handler
+ @SuppressWarnings("unchecked")
+ private static void postEventFromNative(Object ref, int msg, int arg1, int arg2, Object obj) {
+ SoundPool soundPool = ((WeakReference<SoundPool>) ref).get();
+ if (soundPool == null)
+ return;
+
+ if (soundPool.mEventHandler != null) {
+ Message m = soundPool.mEventHandler.obtainMessage(msg, arg1, arg2, obj);
+ soundPool.mEventHandler.sendMessage(m);
+ }
+ }
+
+ private final class EventHandler extends Handler {
+ public EventHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case SAMPLE_LOADED:
+ if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
+ synchronized(mLock) {
+ if (mOnLoadCompleteListener != null) {
+ mOnLoadCompleteListener.onLoadComplete(SoundPool.this, msg.arg1, msg.arg2);
+ }
+ }
+ break;
+ default:
+ Log.e(TAG, "Unknown message type " + msg.what);
+ return;
+ }
}
}
@@ -197,613 +577,4 @@
return new SoundPool(mMaxStreams, mAudioAttributes);
}
}
-
- /**
- * Load the sound from the specified path.
- *
- * @param path the path to the audio file
- * @param priority the priority of the sound. Currently has no effect. Use
- * a value of 1 for future compatibility.
- * @return a sound ID. This value can be used to play or unload the sound.
- */
- public int load(String path, int priority) {
- return mImpl.load(path, priority);
- }
-
- /**
- * Load the sound from the specified APK resource.
- *
- * Note that the extension is dropped. For example, if you want to load
- * a sound from the raw resource file "explosion.mp3", you would specify
- * "R.raw.explosion" as the resource ID. Note that this means you cannot
- * have both an "explosion.wav" and an "explosion.mp3" in the res/raw
- * directory.
- *
- * @param context the application context
- * @param resId the resource ID
- * @param priority the priority of the sound. Currently has no effect. Use
- * a value of 1 for future compatibility.
- * @return a sound ID. This value can be used to play or unload the sound.
- */
- public int load(Context context, int resId, int priority) {
- return mImpl.load(context, resId, priority);
- }
-
- /**
- * Load the sound from an asset file descriptor.
- *
- * @param afd an asset file descriptor
- * @param priority the priority of the sound. Currently has no effect. Use
- * a value of 1 for future compatibility.
- * @return a sound ID. This value can be used to play or unload the sound.
- */
- public int load(AssetFileDescriptor afd, int priority) {
- return mImpl.load(afd, priority);
- }
-
- /**
- * Load the sound from a FileDescriptor.
- *
- * This version is useful if you store multiple sounds in a single
- * binary. The offset specifies the offset from the start of the file
- * and the length specifies the length of the sound within the file.
- *
- * @param fd a FileDescriptor object
- * @param offset offset to the start of the sound
- * @param length length of the sound
- * @param priority the priority of the sound. Currently has no effect. Use
- * a value of 1 for future compatibility.
- * @return a sound ID. This value can be used to play or unload the sound.
- */
- public int load(FileDescriptor fd, long offset, long length, int priority) {
- return mImpl.load(fd, offset, length, priority);
- }
-
- /**
- * Unload a sound from a sound ID.
- *
- * Unloads the sound specified by the soundID. This is the value
- * returned by the load() function. Returns true if the sound is
- * successfully unloaded, false if the sound was already unloaded.
- *
- * @param soundID a soundID returned by the load() function
- * @return true if just unloaded, false if previously unloaded
- */
- public final boolean unload(int soundID) {
- return mImpl.unload(soundID);
- }
-
- /**
- * Play a sound from a sound ID.
- *
- * Play the sound specified by the soundID. This is the value
- * returned by the load() function. Returns a non-zero streamID
- * if successful, zero if it fails. The streamID can be used to
- * further control playback. Note that calling play() may cause
- * another sound to stop playing if the maximum number of active
- * streams is exceeded. A loop value of -1 means loop forever,
- * a value of 0 means don't loop, other values indicate the
- * number of repeats, e.g. a value of 1 plays the audio twice.
- * The playback rate allows the application to vary the playback
- * rate (pitch) of the sound. A value of 1.0 means play back at
- * the original frequency. A value of 2.0 means play back twice
- * as fast, and a value of 0.5 means playback at half speed.
- *
- * @param soundID a soundID returned by the load() function
- * @param leftVolume left volume value (range = 0.0 to 1.0)
- * @param rightVolume right volume value (range = 0.0 to 1.0)
- * @param priority stream priority (0 = lowest priority)
- * @param loop loop mode (0 = no loop, -1 = loop forever)
- * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
- * @return non-zero streamID if successful, zero if failed
- */
- public final int play(int soundID, float leftVolume, float rightVolume,
- int priority, int loop, float rate) {
- return mImpl.play(
- soundID, leftVolume, rightVolume, priority, loop, rate);
- }
-
- /**
- * Pause a playback stream.
- *
- * Pause the stream specified by the streamID. This is the
- * value returned by the play() function. If the stream is
- * playing, it will be paused. If the stream is not playing
- * (e.g. is stopped or was previously paused), calling this
- * function will have no effect.
- *
- * @param streamID a streamID returned by the play() function
- */
- public final void pause(int streamID) {
- mImpl.pause(streamID);
- }
-
- /**
- * Resume a playback stream.
- *
- * Resume the stream specified by the streamID. This
- * is the value returned by the play() function. If the stream
- * is paused, this will resume playback. If the stream was not
- * previously paused, calling this function will have no effect.
- *
- * @param streamID a streamID returned by the play() function
- */
- public final void resume(int streamID) {
- mImpl.resume(streamID);
- }
-
- /**
- * Pause all active streams.
- *
- * Pause all streams that are currently playing. This function
- * iterates through all the active streams and pauses any that
- * are playing. It also sets a flag so that any streams that
- * are playing can be resumed by calling autoResume().
- */
- public final void autoPause() {
- mImpl.autoPause();
- }
-
- /**
- * Resume all previously active streams.
- *
- * Automatically resumes all streams that were paused in previous
- * calls to autoPause().
- */
- public final void autoResume() {
- mImpl.autoResume();
- }
-
- /**
- * Stop a playback stream.
- *
- * Stop the stream specified by the streamID. This
- * is the value returned by the play() function. If the stream
- * is playing, it will be stopped. It also releases any native
- * resources associated with this stream. If the stream is not
- * playing, it will have no effect.
- *
- * @param streamID a streamID returned by the play() function
- */
- public final void stop(int streamID) {
- mImpl.stop(streamID);
- }
-
- /**
- * Set stream volume.
- *
- * Sets the volume on the stream specified by the streamID.
- * This is the value returned by the play() function. The
- * value must be in the range of 0.0 to 1.0. If the stream does
- * not exist, it will have no effect.
- *
- * @param streamID a streamID returned by the play() function
- * @param leftVolume left volume value (range = 0.0 to 1.0)
- * @param rightVolume right volume value (range = 0.0 to 1.0)
- */
- public final void setVolume(int streamID,
- float leftVolume, float rightVolume) {
- mImpl.setVolume(streamID, leftVolume, rightVolume);
- }
-
- /**
- * Similar, except set volume of all channels to same value.
- * @hide
- */
- public void setVolume(int streamID, float volume) {
- setVolume(streamID, volume, volume);
- }
-
- /**
- * Change stream priority.
- *
- * Change the priority of the stream specified by the streamID.
- * This is the value returned by the play() function. Affects the
- * order in which streams are re-used to play new sounds. If the
- * stream does not exist, it will have no effect.
- *
- * @param streamID a streamID returned by the play() function
- */
- public final void setPriority(int streamID, int priority) {
- mImpl.setPriority(streamID, priority);
- }
-
- /**
- * Set loop mode.
- *
- * Change the loop mode. A loop value of -1 means loop forever,
- * a value of 0 means don't loop, other values indicate the
- * number of repeats, e.g. a value of 1 plays the audio twice.
- * If the stream does not exist, it will have no effect.
- *
- * @param streamID a streamID returned by the play() function
- * @param loop loop mode (0 = no loop, -1 = loop forever)
- */
- public final void setLoop(int streamID, int loop) {
- mImpl.setLoop(streamID, loop);
- }
-
- /**
- * Change playback rate.
- *
- * The playback rate allows the application to vary the playback
- * rate (pitch) of the sound. A value of 1.0 means playback at
- * the original frequency. A value of 2.0 means playback twice
- * as fast, and a value of 0.5 means playback at half speed.
- * If the stream does not exist, it will have no effect.
- *
- * @param streamID a streamID returned by the play() function
- * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
- */
- public final void setRate(int streamID, float rate) {
- mImpl.setRate(streamID, rate);
- }
-
- public interface OnLoadCompleteListener {
- /**
- * Called when a sound has completed loading.
- *
- * @param soundPool SoundPool object from the load() method
- * @param sampleId the sample ID of the sound loaded.
- * @param status the status of the load operation (0 = success)
- */
- public void onLoadComplete(SoundPool soundPool, int sampleId, int status);
- }
-
- /**
- * Sets the callback hook for the OnLoadCompleteListener.
- */
- public void setOnLoadCompleteListener(OnLoadCompleteListener listener) {
- mImpl.setOnLoadCompleteListener(listener);
- }
-
- /**
- * Release the SoundPool resources.
- *
- * Release all memory and native resources used by the SoundPool
- * object. The SoundPool can no longer be used and the reference
- * should be set to null.
- */
- public final void release() {
- mImpl.release();
- }
-
- /**
- * Interface for SoundPool implementations.
- * SoundPool is statically referenced and unconditionally called from all
- * over the framework, so we can't simply omit the class or make it throw
- * runtime exceptions, as doing so would break the framework. Instead we
- * now select either a real or no-op impl object based on whether media is
- * enabled.
- *
- * @hide
- */
- /* package */ interface SoundPoolDelegate {
- public int load(String path, int priority);
- public int load(Context context, int resId, int priority);
- public int load(AssetFileDescriptor afd, int priority);
- public int load(
- FileDescriptor fd, long offset, long length, int priority);
- public boolean unload(int soundID);
- public int play(
- int soundID, float leftVolume, float rightVolume,
- int priority, int loop, float rate);
- public void pause(int streamID);
- public void resume(int streamID);
- public void autoPause();
- public void autoResume();
- public void stop(int streamID);
- public void setVolume(int streamID, float leftVolume, float rightVolume);
- public void setVolume(int streamID, float volume);
- public void setPriority(int streamID, int priority);
- public void setLoop(int streamID, int loop);
- public void setRate(int streamID, float rate);
- public void setOnLoadCompleteListener(OnLoadCompleteListener listener);
- public void release();
- }
-
-
- /**
- * Real implementation of the delegate interface. This was formerly the
- * body of SoundPool itself.
- */
- /* package */ static class SoundPoolImpl implements SoundPoolDelegate {
- static { System.loadLibrary("soundpool"); }
-
- private final static String TAG = "SoundPool";
- private final static boolean DEBUG = false;
-
- private long mNativeContext; // accessed by native methods
-
- private EventHandler mEventHandler;
- private SoundPool.OnLoadCompleteListener mOnLoadCompleteListener;
- private SoundPool mProxy;
-
- private final Object mLock;
- private final AudioAttributes mAttributes;
- private final IAppOpsService mAppOps;
-
- // SoundPool messages
- //
- // must match SoundPool.h
- private static final int SAMPLE_LOADED = 1;
-
- public SoundPoolImpl(SoundPool proxy, int maxStreams, AudioAttributes attr) {
-
- // do native setup
- if (native_setup(new WeakReference(this), maxStreams, attr) != 0) {
- throw new RuntimeException("Native setup failed");
- }
- mLock = new Object();
- mProxy = proxy;
- mAttributes = attr;
- IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
- mAppOps = IAppOpsService.Stub.asInterface(b);
- }
-
- public int load(String path, int priority)
- {
- int id = 0;
- try {
- File f = new File(path);
- ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
- if (fd != null) {
- id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
- fd.close();
- }
- } catch (java.io.IOException e) {
- Log.e(TAG, "error loading " + path);
- }
- return id;
- }
-
- @Override
- public int load(Context context, int resId, int priority) {
- AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
- int id = 0;
- if (afd != null) {
- id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority);
- try {
- afd.close();
- } catch (java.io.IOException ex) {
- //Log.d(TAG, "close failed:", ex);
- }
- }
- return id;
- }
-
- @Override
- public int load(AssetFileDescriptor afd, int priority) {
- if (afd != null) {
- long len = afd.getLength();
- if (len < 0) {
- throw new AndroidRuntimeException("no length for fd");
- }
- return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority);
- } else {
- return 0;
- }
- }
-
- @Override
- public int load(FileDescriptor fd, long offset, long length, int priority) {
- return _load(fd, offset, length, priority);
- }
-
- private native final int _load(FileDescriptor fd, long offset, long length, int priority);
-
- @Override
- public native final boolean unload(int soundID);
-
- @Override
- public final int play(int soundID, float leftVolume, float rightVolume,
- int priority, int loop, float rate) {
- if (isRestricted()) {
- leftVolume = rightVolume = 0;
- }
- return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
- }
-
- public native final int _play(int soundID, float leftVolume, float rightVolume,
- int priority, int loop, float rate);
-
- private boolean isRestricted() {
- if ((mAttributes.getFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
- return false;
- }
- try {
- final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
- mAttributes.getUsage(),
- Process.myUid(), ActivityThread.currentPackageName());
- return mode != AppOpsManager.MODE_ALLOWED;
- } catch (RemoteException e) {
- return false;
- }
- }
-
- @Override
- public native final void pause(int streamID);
-
- @Override
- public native final void resume(int streamID);
-
- @Override
- public native final void autoPause();
-
- @Override
- public native final void autoResume();
-
- @Override
- public native final void stop(int streamID);
-
- @Override
- public final void setVolume(int streamID, float leftVolume, float rightVolume) {
- if (isRestricted()) {
- return;
- }
- _setVolume(streamID, leftVolume, rightVolume);
- }
-
- private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
-
- @Override
- public void setVolume(int streamID, float volume) {
- setVolume(streamID, volume, volume);
- }
-
- @Override
- public native final void setPriority(int streamID, int priority);
-
- @Override
- public native final void setLoop(int streamID, int loop);
-
- @Override
- public native final void setRate(int streamID, float rate);
-
- @Override
- public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)
- {
- synchronized(mLock) {
- if (listener != null) {
- // setup message handler
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mEventHandler = new EventHandler(mProxy, looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mEventHandler = new EventHandler(mProxy, looper);
- } else {
- mEventHandler = null;
- }
- } else {
- mEventHandler = null;
- }
- mOnLoadCompleteListener = listener;
- }
- }
-
- private class EventHandler extends Handler
- {
- private SoundPool mSoundPool;
-
- public EventHandler(SoundPool soundPool, Looper looper) {
- super(looper);
- mSoundPool = soundPool;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case SAMPLE_LOADED:
- if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
- synchronized(mLock) {
- if (mOnLoadCompleteListener != null) {
- mOnLoadCompleteListener.onLoadComplete(mSoundPool, msg.arg1, msg.arg2);
- }
- }
- break;
- default:
- Log.e(TAG, "Unknown message type " + msg.what);
- return;
- }
- }
- }
-
- // post event from native code to message handler
- private static void postEventFromNative(Object weakRef, int msg, int arg1, int arg2, Object obj)
- {
- SoundPoolImpl soundPoolImpl = (SoundPoolImpl)((WeakReference)weakRef).get();
- if (soundPoolImpl == null)
- return;
-
- if (soundPoolImpl.mEventHandler != null) {
- Message m = soundPoolImpl.mEventHandler.obtainMessage(msg, arg1, arg2, obj);
- soundPoolImpl.mEventHandler.sendMessage(m);
- }
- }
-
- public native final void release();
-
- private native final int native_setup(Object weakRef, int maxStreams,
- Object/*AudioAttributes*/ attributes);
-
- protected void finalize() { release(); }
- }
-
- /**
- * No-op implementation of SoundPool.
- * Used when media is disabled by the system.
- * @hide
- */
- /* package */ static class SoundPoolStub implements SoundPoolDelegate {
- public SoundPoolStub() { }
-
- public int load(String path, int priority) {
- return 0;
- }
-
- @Override
- public int load(Context context, int resId, int priority) {
- return 0;
- }
-
- @Override
- public int load(AssetFileDescriptor afd, int priority) {
- return 0;
- }
-
- @Override
- public int load(FileDescriptor fd, long offset, long length, int priority) {
- return 0;
- }
-
- @Override
- public final boolean unload(int soundID) {
- return true;
- }
-
- @Override
- public final int play(int soundID, float leftVolume, float rightVolume,
- int priority, int loop, float rate) {
- return 0;
- }
-
- @Override
- public final void pause(int streamID) { }
-
- @Override
- public final void resume(int streamID) { }
-
- @Override
- public final void autoPause() { }
-
- @Override
- public final void autoResume() { }
-
- @Override
- public final void stop(int streamID) { }
-
- @Override
- public final void setVolume(int streamID,
- float leftVolume, float rightVolume) { }
-
- @Override
- public void setVolume(int streamID, float volume) {
- }
-
- @Override
- public final void setPriority(int streamID, int priority) { }
-
- @Override
- public final void setLoop(int streamID, int loop) { }
-
- @Override
- public final void setRate(int streamID, float rate) { }
-
- @Override
- public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener) {
- }
-
- @Override
- public final void release() { }
- }
}
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 438e767..936762c 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -1054,6 +1054,50 @@
public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
/**
+ * Internal integer flag used by individual TV input services.
+ * <p>
+ * This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+
+ /**
+ * Internal integer flag used by individual TV input services.
+ * <p>
+ * This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+
+ /**
+ * Internal integer flag used by individual TV input services.
+ * <p>
+ * This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+
+ /**
+ * Internal integer flag used by individual TV input services.
+ * <p>
+ * This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+
+ /**
* The version number of this row entry used by TV input services.
* <p>
* This is best used by sync adapters to identify the rows to update. The number can be
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index cf1b441..b887855 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1223,6 +1223,8 @@
args.arg2 = mProxySession;
args.arg3 = mProxySessionCallback;
args.arg4 = session.getToken();
+ session.tune(TvContract.buildChannelUriForPassthroughInput(
+ getHardwareInputId()));
} else {
args.arg1 = null;
args.arg2 = null;
@@ -1232,7 +1234,6 @@
}
mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED, args)
.sendToTarget();
- session.tune(TvContract.buildChannelUriForPassthroughInput(getHardwareInputId()));
}
@Override
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 1433c79..d10df3e 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -435,7 +435,7 @@
Image_unlockIfLocked(env, image);
// Set timestamp
- ALOGV("timestamp to be queued: %lld", timestampNs);
+ ALOGV("timestamp to be queued: %" PRId64, timestampNs);
res = native_window_set_buffers_timestamp(anw.get(), timestampNs);
if (res != OK) {
jniThrowRuntimeException(env, "Set timestamp failed");
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 8302a34..96d7133 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -59,6 +59,7 @@
struct RequestFields {
jfieldID data;
jfieldID defaultUrl;
+ jfieldID requestType;
};
struct ArrayListFields {
@@ -101,6 +102,12 @@
jint kKeyTypeRelease;
} gKeyTypes;
+struct KeyRequestTypes {
+ jint kKeyRequestTypeInitial;
+ jint kKeyRequestTypeRenewal;
+ jint kKeyRequestTypeRelease;
+} gKeyRequestTypes;
+
struct CertificateTypes {
jint kCertificateTypeNone;
jint kCertificateTypeX509;
@@ -182,7 +189,7 @@
jint jeventType;
// translate DrmPlugin event types into their java equivalents
- switch(eventType) {
+ switch (eventType) {
case DrmPlugin::kDrmPluginEventProvisionRequired:
jeventType = gEventTypes.kEventProvisionRequired;
break;
@@ -236,7 +243,7 @@
const char *drmMessage = NULL;
- switch(err) {
+ switch (err) {
case ERROR_DRM_UNKNOWN:
drmMessage = "General DRM error";
break;
@@ -587,6 +594,13 @@
GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_RELEASE", "I");
gKeyTypes.kKeyTypeRelease = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_INITIAL", "I");
+ gKeyRequestTypes.kKeyRequestTypeInitial = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RENEWAL", "I");
+ gKeyRequestTypes.kKeyRequestTypeRenewal = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RELEASE", "I");
+ gKeyRequestTypes.kKeyRequestTypeRelease = env->GetStaticIntField(clazz, field);
+
GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_NONE", "I");
gCertificateTypes.kCertificateTypeNone = env->GetStaticIntField(clazz, field);
GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_X509", "I");
@@ -595,6 +609,7 @@
FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B");
GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
+ GET_FIELD_ID(gFields.keyRequest.requestType, clazz, "mRequestType", "I");
FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
GET_FIELD_ID(gFields.provisionRequest.data, clazz, "mData", "[B");
@@ -786,9 +801,10 @@
Vector<uint8_t> request;
String8 defaultUrl;
+ DrmPlugin::KeyRequestType keyRequestType;
status_t err = drm->getKeyRequest(sessionId, initData, mimeType,
- keyType, optParams, request, defaultUrl);
+ keyType, optParams, request, defaultUrl, &keyRequestType);
if (throwExceptionAsNecessary(env, err, "Failed to get key request")) {
return NULL;
@@ -807,6 +823,25 @@
jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl);
+
+ switch (keyRequestType) {
+ case DrmPlugin::kKeyRequestType_Initial:
+ env->SetIntField(keyObj, gFields.keyRequest.requestType,
+ gKeyRequestTypes.kKeyRequestTypeInitial);
+ break;
+ case DrmPlugin::kKeyRequestType_Renewal:
+ env->SetIntField(keyObj, gFields.keyRequest.requestType,
+ gKeyRequestTypes.kKeyRequestTypeRenewal);
+ break;
+ case DrmPlugin::kKeyRequestType_Release:
+ env->SetIntField(keyObj, gFields.keyRequest.requestType,
+ gKeyRequestTypes.kKeyRequestTypeRelease);
+ break;
+ case DrmPlugin::kKeyRequestType_Unknown:
+ throwStateException(env, "DRM plugin failure: unknown key request type",
+ ERROR_DRM_UNKNOWN);
+ break;
+ }
}
return keyObj;
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 71ab013..2476056 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- android_media_SoundPool_SoundPoolImpl.cpp \
+ android_media_SoundPool.cpp \
SoundPool.cpp \
SoundPoolThread.cpp
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
similarity index 74%
rename from media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
rename to media/jni/soundpool/android_media_SoundPool.cpp
index b2333f8..fc4cf05 100644
--- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -47,10 +47,10 @@
// ----------------------------------------------------------------------------
static jint
-android_media_SoundPool_SoundPoolImpl_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
+android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
jlong offset, jlong length, jint priority)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_load_FD");
+ ALOGV("android_media_SoundPool_load_FD");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return 0;
return (jint) ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor),
@@ -58,104 +58,104 @@
}
static jboolean
-android_media_SoundPool_SoundPoolImpl_unload(JNIEnv *env, jobject thiz, jint sampleID) {
- ALOGV("android_media_SoundPool_SoundPoolImpl_unload\n");
+android_media_SoundPool_unload(JNIEnv *env, jobject thiz, jint sampleID) {
+ ALOGV("android_media_SoundPool_unload\n");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return JNI_FALSE;
return ap->unload(sampleID) ? JNI_TRUE : JNI_FALSE;
}
static jint
-android_media_SoundPool_SoundPoolImpl_play(JNIEnv *env, jobject thiz, jint sampleID,
+android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
jfloat rate)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_play\n");
+ ALOGV("android_media_SoundPool_play\n");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return 0;
return (jint) ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
}
static void
-android_media_SoundPool_SoundPoolImpl_pause(JNIEnv *env, jobject thiz, jint channelID)
+android_media_SoundPool_pause(JNIEnv *env, jobject thiz, jint channelID)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_pause");
+ ALOGV("android_media_SoundPool_pause");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->pause(channelID);
}
static void
-android_media_SoundPool_SoundPoolImpl_resume(JNIEnv *env, jobject thiz, jint channelID)
+android_media_SoundPool_resume(JNIEnv *env, jobject thiz, jint channelID)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_resume");
+ ALOGV("android_media_SoundPool_resume");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->resume(channelID);
}
static void
-android_media_SoundPool_SoundPoolImpl_autoPause(JNIEnv *env, jobject thiz)
+android_media_SoundPool_autoPause(JNIEnv *env, jobject thiz)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_autoPause");
+ ALOGV("android_media_SoundPool_autoPause");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->autoPause();
}
static void
-android_media_SoundPool_SoundPoolImpl_autoResume(JNIEnv *env, jobject thiz)
+android_media_SoundPool_autoResume(JNIEnv *env, jobject thiz)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_autoResume");
+ ALOGV("android_media_SoundPool_autoResume");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->autoResume();
}
static void
-android_media_SoundPool_SoundPoolImpl_stop(JNIEnv *env, jobject thiz, jint channelID)
+android_media_SoundPool_stop(JNIEnv *env, jobject thiz, jint channelID)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_stop");
+ ALOGV("android_media_SoundPool_stop");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->stop(channelID);
}
static void
-android_media_SoundPool_SoundPoolImpl_setVolume(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_setVolume(JNIEnv *env, jobject thiz, jint channelID,
jfloat leftVolume, jfloat rightVolume)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_setVolume");
+ ALOGV("android_media_SoundPool_setVolume");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setVolume(channelID, (float) leftVolume, (float) rightVolume);
}
static void
-android_media_SoundPool_SoundPoolImpl_setPriority(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_setPriority(JNIEnv *env, jobject thiz, jint channelID,
jint priority)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_setPriority");
+ ALOGV("android_media_SoundPool_setPriority");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setPriority(channelID, (int) priority);
}
static void
-android_media_SoundPool_SoundPoolImpl_setLoop(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_setLoop(JNIEnv *env, jobject thiz, jint channelID,
int loop)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_setLoop");
+ ALOGV("android_media_SoundPool_setLoop");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setLoop(channelID, loop);
}
static void
-android_media_SoundPool_SoundPoolImpl_setRate(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_setRate(JNIEnv *env, jobject thiz, jint channelID,
jfloat rate)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_setRate");
+ ALOGV("android_media_SoundPool_setRate");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setRate(channelID, (float) rate);
@@ -169,7 +169,7 @@
}
static jint
-android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,
+android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,
jint maxChannels, jobject jaa)
{
if (jaa == 0) {
@@ -191,7 +191,7 @@
(audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
- ALOGV("android_media_SoundPool_SoundPoolImpl_native_setup");
+ ALOGV("android_media_SoundPool_native_setup");
SoundPool *ap = new SoundPool(maxChannels, paa);
if (ap == NULL) {
return -1;
@@ -211,9 +211,9 @@
}
static void
-android_media_SoundPool_SoundPoolImpl_release(JNIEnv *env, jobject thiz)
+android_media_SoundPool_release(JNIEnv *env, jobject thiz)
{
- ALOGV("android_media_SoundPool_SoundPoolImpl_release");
+ ALOGV("android_media_SoundPool_release");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap != NULL) {
@@ -236,63 +236,63 @@
static JNINativeMethod gMethods[] = {
{ "_load",
"(Ljava/io/FileDescriptor;JJI)I",
- (void *)android_media_SoundPool_SoundPoolImpl_load_FD
+ (void *)android_media_SoundPool_load_FD
},
{ "unload",
"(I)Z",
- (void *)android_media_SoundPool_SoundPoolImpl_unload
+ (void *)android_media_SoundPool_unload
},
{ "_play",
"(IFFIIF)I",
- (void *)android_media_SoundPool_SoundPoolImpl_play
+ (void *)android_media_SoundPool_play
},
{ "pause",
"(I)V",
- (void *)android_media_SoundPool_SoundPoolImpl_pause
+ (void *)android_media_SoundPool_pause
},
{ "resume",
"(I)V",
- (void *)android_media_SoundPool_SoundPoolImpl_resume
+ (void *)android_media_SoundPool_resume
},
{ "autoPause",
"()V",
- (void *)android_media_SoundPool_SoundPoolImpl_autoPause
+ (void *)android_media_SoundPool_autoPause
},
{ "autoResume",
"()V",
- (void *)android_media_SoundPool_SoundPoolImpl_autoResume
+ (void *)android_media_SoundPool_autoResume
},
{ "stop",
"(I)V",
- (void *)android_media_SoundPool_SoundPoolImpl_stop
+ (void *)android_media_SoundPool_stop
},
{ "_setVolume",
"(IFF)V",
- (void *)android_media_SoundPool_SoundPoolImpl_setVolume
+ (void *)android_media_SoundPool_setVolume
},
{ "setPriority",
"(II)V",
- (void *)android_media_SoundPool_SoundPoolImpl_setPriority
+ (void *)android_media_SoundPool_setPriority
},
{ "setLoop",
"(II)V",
- (void *)android_media_SoundPool_SoundPoolImpl_setLoop
+ (void *)android_media_SoundPool_setLoop
},
{ "setRate",
"(IF)V",
- (void *)android_media_SoundPool_SoundPoolImpl_setRate
+ (void *)android_media_SoundPool_setRate
},
{ "native_setup",
"(Ljava/lang/Object;ILjava/lang/Object;)I",
- (void*)android_media_SoundPool_SoundPoolImpl_native_setup
+ (void*)android_media_SoundPool_native_setup
},
{ "release",
"()V",
- (void*)android_media_SoundPool_SoundPoolImpl_release
+ (void*)android_media_SoundPool_release
}
};
-static const char* const kClassPathName = "android/media/SoundPool$SoundPoolImpl";
+static const char* const kClassPathName = "android/media/SoundPool";
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
@@ -314,14 +314,14 @@
fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.mNativeContext == NULL) {
- ALOGE("Can't find SoundPoolImpl.mNativeContext");
+ ALOGE("Can't find SoundPool.mNativeContext");
return result;
}
fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.mPostEvent == NULL) {
- ALOGE("Can't find android/media/SoundPoolImpl.postEventFromNative");
+ ALOGE("Can't find android/media/SoundPool.postEventFromNative");
return result;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 30786f0..952b220 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -62,9 +62,10 @@
*/
private static final ArraySet<String> sBroadcastOnRestore;
static {
- sBroadcastOnRestore = new ArraySet<String>(2);
+ sBroadcastOnRestore = new ArraySet<String>(3);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+ sBroadcastOnRestore.add(Settings.Secure.ENABLED_INPUT_METHODS);
}
private interface SettingsLookup {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 158e133..7a58c87 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -106,6 +106,9 @@
<uses-permission android:name="android.permission.TRUST_LISTENER" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
+ <!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
+ <uses-permission android:name="android.permission.SET_WALLPAPER"/>
+
<!-- Recents -->
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_bottom.xml b/packages/SystemUI/res/drawable/vector_drawable_place_bottom.xml
new file mode 100644
index 0000000..14f1981
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_bottom.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM4.0,10.0l16.0,0.0l0.0,10.0L4.0,20.0L4.0,10.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml b/packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml
new file mode 100644
index 0000000..aee0b7f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml
@@ -0,0 +1,33 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0.0,8.0l4.0,0.0 0.0,-4.0 4.0,0.0 0.0,-4.0 -8.0,0.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4.0,16.0l-4.0,0.0 0.0,8.0 8.0,0.0 0.0,-4.0 -4.0,0.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.0,0.0l0.0,4.0 4.0,0.0 0.0,4.0 4.0,0.0 0.0,-8.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.0,20.0l-4.0,0.0 0.0,4.0 8.0,0.0 0.0,-8.0 -4.0,0.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_left.xml b/packages/SystemUI/res/drawable/vector_drawable_place_left.xml
new file mode 100644
index 0000000..078f83c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_left.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24.0,0.0L0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0zM14.0,4.0l0.0,16.0L4.0,20.0L4.0,4.0L14.0,4.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_right.xml b/packages/SystemUI/res/drawable/vector_drawable_place_right.xml
new file mode 100644
index 0000000..86730db
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_right.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0L0.0,24.0zM10.0,20.0L10.0,4.0l10.0,0.0l0.0,16.0L10.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_top.xml b/packages/SystemUI/res/drawable/vector_drawable_place_top.xml
new file mode 100644
index 0000000..92e01af
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_top.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24.0,24.0L24.0,0.0L0.0,0.0l0.0,24.0L24.0,24.0zM20.0,14.0L4.0,14.0L4.0,4.0l16.0,0.0L20.0,14.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml b/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml
deleted file mode 100644
index 36e54a0..0000000
--- a/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="16dp"
- android:orientation="vertical"
- android:descendantFocusability="beforeDescendants"
- android:focusableInTouchMode="true">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <EditText
- android:id="@+id/inset_left"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:hint="Left"
- android:singleLine="true"
- android:imeOptions="actionNext"
- android:inputType="number"
- android:selectAllOnFocus="true" />
- <EditText
- android:id="@+id/inset_top"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:hint="Top"
- android:singleLine="true"
- android:imeOptions="actionNext"
- android:inputType="number"
- android:selectAllOnFocus="true" />
- <EditText
- android:id="@+id/inset_right"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:hint="Right"
- android:singleLine="true"
- android:imeOptions="actionNext"
- android:inputType="number"
- android:selectAllOnFocus="true" />
- <EditText
- android:id="@+id/inset_bottom"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:hint="Bottom"
- android:singleLine="true"
- android:imeOptions="actionDone"
- android:inputType="number"
- android:selectAllOnFocus="true" />
- </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout/recents_task_resize_dialog.xml
new file mode 100644
index 0000000..a8c6ee9
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_task_resize_dialog.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:orientation="vertical"
+ android:descendantFocusability="beforeDescendants"
+ android:focusableInTouchMode="true">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/place_left"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_left" />
+ <Button
+ android:id="@+id/place_right"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_right" />
+ <Button
+ android:id="@+id/place_top"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_top" />
+ <Button
+ android:id="@+id/place_bottom"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_bottom" />
+ <Button
+ android:id="@+id/place_full"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:background="@drawable/vector_drawable_place_fullscreen" />
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 66d494b..b696787 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -216,6 +216,10 @@
<string name="phone_label">open phone</string>
<!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
<string name="camera_label">open camera</string>
+ <!-- Caption for "Recents resize" developer debug feature. [CHAR LIMIT=NONE] -->
+ <string name="recents_caption_resize">Select new task layout</string>
+ <!-- Button name for "Cancel". [CHAR LIMIT=NONE] -->
+ <string name="cancel">Cancel</string>
<!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_compatibility_zoom_button">Compatibility zoom button.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 5c1a317..cb78deb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles;
import android.app.ActivityManager;
-import android.os.SystemClock;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
@@ -27,16 +26,11 @@
public class FlashlightTile extends QSTile<QSTile.BooleanState> implements
FlashlightController.FlashlightListener {
- /** Grace period for which we consider the flashlight
- * still available because it was recently on. */
- private static final long RECENTLY_ON_DURATION_MILLIS = 500;
-
private final AnimationIcon mEnable
= new AnimationIcon(R.drawable.ic_signal_flashlight_enable_animation);
private final AnimationIcon mDisable
= new AnimationIcon(R.drawable.ic_signal_flashlight_disable_animation);
private final FlashlightController mFlashlightController;
- private long mWasLastOn;
public FlashlightTile(Host host) {
super(host);
@@ -69,33 +63,17 @@
return;
}
boolean newState = !mState.value;
- mFlashlightController.setFlashlight(newState);
refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE);
+ mFlashlightController.setFlashlight(newState);
}
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- if (state.value) {
- mWasLastOn = SystemClock.uptimeMillis();
- }
-
+ state.visible = mFlashlightController.isAvailable();
+ state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
if (arg instanceof UserBoolean) {
state.value = ((UserBoolean) arg).value;
}
-
- if (!state.value && mWasLastOn != 0) {
- if (SystemClock.uptimeMillis() > mWasLastOn + RECENTLY_ON_DURATION_MILLIS) {
- mWasLastOn = 0;
- } else {
- mHandler.removeCallbacks(mRecentlyOnTimeout);
- mHandler.postAtTime(mRecentlyOnTimeout, mWasLastOn + RECENTLY_ON_DURATION_MILLIS);
- }
- }
-
- // Always show the tile when the flashlight is or was recently on. This is needed because
- // the camera is not available while it is being used for the flashlight.
- state.visible = mWasLastOn != 0 || mFlashlightController.isAvailable();
- state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
final AnimationIcon icon = state.value ? mEnable : mDisable;
icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated);
state.icon = icon;
@@ -115,8 +93,8 @@
}
@Override
- public void onFlashlightOff() {
- refreshState(UserBoolean.BACKGROUND_FALSE);
+ public void onFlashlightChanged(boolean enabled) {
+ refreshState(enabled ? UserBoolean.BACKGROUND_TRUE : UserBoolean.BACKGROUND_FALSE);
}
@Override
@@ -128,11 +106,4 @@
public void onFlashlightAvailabilityChanged(boolean available) {
refreshState();
}
-
- private Runnable mRecentlyOnTimeout = new Runnable() {
- @Override
- public void run() {
- refreshState();
- }
- };
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 7f1e876..2d1fab0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -385,7 +385,7 @@
}
// Return early if there are no tasks in the focused stack
- if (focusedStack.getTaskCount() == 0) return;
+ if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
// Return early if there is no running task (can't determine affiliated tasks in this case)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index c259c9d..f014f09 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -592,14 +592,14 @@
private RecentsResizeTaskDialog getResizeTaskDebugDialog() {
if (mResizeTaskDebugDialog == null) {
- mResizeTaskDebugDialog = new RecentsResizeTaskDialog(getFragmentManager());
+ mResizeTaskDebugDialog = new RecentsResizeTaskDialog(getFragmentManager(), this);
}
return mResizeTaskDebugDialog;
}
@Override
public void onTaskResize(Task t) {
- getResizeTaskDebugDialog().showResizeTaskDialog(t);
+ getResizeTaskDebugDialog().showResizeTaskDialog(t, mRecentsView);
}
/**** RecentsView.RecentsViewCallbacks Implementation ****/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 9263543..abeb2b0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -292,7 +292,7 @@
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0;
lockToAppEnabled = ssp.getSystemSetting(context,
Settings.System.LOCK_TO_APP_ENABLED) != 0;
- multiStackEnabled = "1".equals(ssp.getSystemProperty("overview.enableMultiStack"));
+ multiStackEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
}
/** Called when the configuration has changed, and we want to reset any configuration specific
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index d67eceb..7c11894 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -16,31 +16,23 @@
package com.android.systemui.recents;
-import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.FragmentManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.os.Bundle;
-import android.util.MutableInt;
-import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.EditText;
-import android.widget.Toast;
+import android.widget.Button;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
-
-import java.util.List;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.views.RecentsView;
/**
* A helper for the dialogs that show when task debugging is on.
@@ -49,80 +41,137 @@
static final String TAG = "RecentsResizeTaskDialog";
+ // The various window arrangements we can handle.
+ private static final int PLACE_LEFT = 1;
+ private static final int PLACE_RIGHT = 2;
+ private static final int PLACE_TOP = 3;
+ private static final int PLACE_BOTTOM = 4;
+ private static final int PLACE_FULL = 5;
+
// The task we want to resize.
- Task mTaskToResize;
- FragmentManager mFragmentManager;
- View mResizeTaskDialogContent;
+ private Task mTaskToResize;
+ private Task mNextTaskToResize;
+ private FragmentManager mFragmentManager;
+ private View mResizeTaskDialogContent;
+ private RecentsActivity mRecentsActivity;
+ private RecentsView mRecentsView;
+ private SystemServicesProxy mSsp;
- public RecentsResizeTaskDialog() {}
-
- public RecentsResizeTaskDialog(FragmentManager mgr) {
+ public RecentsResizeTaskDialog(FragmentManager mgr, RecentsActivity activity) {
mFragmentManager = mgr;
+ mRecentsActivity = activity;
+ mSsp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
}
/** Shows the resize-task dialog. */
- void showResizeTaskDialog(Task t) {
- mTaskToResize = t;
+ void showResizeTaskDialog(Task mainTask, RecentsView rv) {
+ mTaskToResize = mainTask;
+ mRecentsView = rv;
+ mNextTaskToResize = mRecentsView.getNextTaskOrTopTask(mainTask);
+
show(mFragmentManager, TAG);
}
/** Creates a new resize-task dialog. */
private void createResizeTaskDialog(final Context context, LayoutInflater inflater,
- AlertDialog.Builder builder, final SystemServicesProxy ssp) {
- builder.setTitle("Resize Task - Enter new dimensions");
+ AlertDialog.Builder builder) {
+ builder.setTitle(R.string.recents_caption_resize);
mResizeTaskDialogContent =
- inflater.inflate(R.layout.recents_multistack_stack_size_dialog, null, false);
- Rect bounds = ssp.getTaskBounds(mTaskToResize.key.stackId);
- setDimensionInEditText(mResizeTaskDialogContent, R.id.inset_left, bounds.left);
- setDimensionInEditText(mResizeTaskDialogContent, R.id.inset_top, bounds.top);
- setDimensionInEditText(mResizeTaskDialogContent, R.id.inset_right, bounds.right);
- setDimensionInEditText(mResizeTaskDialogContent, R.id.inset_bottom, bounds.bottom);
+ inflater.inflate(R.layout.recents_task_resize_dialog, null, false);
+
+ ((Button)mResizeTaskDialogContent.findViewById(R.id.place_left)).setOnClickListener(
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ placeTasks(PLACE_LEFT);
+ }
+ });
+ ((Button)mResizeTaskDialogContent.findViewById(R.id.place_right)).setOnClickListener(
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ placeTasks(PLACE_RIGHT);
+ }
+ });
+ ((Button)mResizeTaskDialogContent.findViewById(R.id.place_top)).setOnClickListener(
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ placeTasks(PLACE_TOP);
+ }
+ });
+ ((Button)mResizeTaskDialogContent.findViewById(R.id.place_bottom)).setOnClickListener(
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ placeTasks(PLACE_BOTTOM);
+ }
+ });
+ ((Button)mResizeTaskDialogContent.findViewById(R.id.place_full)).setOnClickListener(
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ placeTasks(PLACE_FULL);
+ }
+ });
+
+ builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ });
+
builder.setView(mResizeTaskDialogContent);
- builder.setPositiveButton("Resize Task", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- int left = getDimensionFromEditText(mResizeTaskDialogContent, R.id.inset_left);
- int top = getDimensionFromEditText(mResizeTaskDialogContent, R.id.inset_top);
- int right = getDimensionFromEditText(mResizeTaskDialogContent, R.id.inset_right);
- int bottom = getDimensionFromEditText(mResizeTaskDialogContent, R.id.inset_bottom);
- if (bottom <= top || right <= left) {
- Toast.makeText(context, "Invalid dimensions", Toast.LENGTH_SHORT).show();
- dismiss();
- return;
- }
- ssp.resizeTask(mTaskToResize.key.id, new Rect(left, top, right, bottom));
- dismiss();
- }
- });
- builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismiss();
- }
- });
}
- /** Helper to get an integer value from an edit text. */
- private int getDimensionFromEditText(View container, int id) {
- String text = ((EditText) container.findViewById(id)).getText().toString();
- if (text.trim().length() != 0) {
- return Integer.parseInt(text.trim());
+ /** Helper function to place window(s) on the display according to an arrangement request. */
+ private void placeTasks(int arrangement) {
+ Rect focusedBounds = mSsp.getWindowRect();
+ Rect otherBounds = new Rect(focusedBounds);
+
+ switch (arrangement) {
+ case PLACE_LEFT:
+ focusedBounds.right = focusedBounds.centerX();
+ otherBounds.left = focusedBounds.right;
+ break;
+ case PLACE_RIGHT:
+ otherBounds.right = otherBounds.centerX();
+ focusedBounds.left = otherBounds.right;
+ break;
+ case PLACE_TOP:
+ focusedBounds.bottom = focusedBounds.centerY();
+ otherBounds.top = focusedBounds.bottom;
+ break;
+ case PLACE_BOTTOM:
+ otherBounds.bottom = otherBounds.centerY();
+ focusedBounds.top = otherBounds.bottom;
+ break;
+ case PLACE_FULL:
+ // Null the rectangle to avoid the other task to show up.
+ otherBounds = new Rect();
+ break;
}
- return 0;
- }
- /** Helper to set an integer value to an edit text. */
- private void setDimensionInEditText(View container, int id, int value) {
- ((EditText) container.findViewById(id)).setText("" + value);
+ // Resize all other tasks to go to the other side.
+ if (mNextTaskToResize != null && !otherBounds.isEmpty()) {
+ mSsp.resizeTask(mNextTaskToResize.key.id, otherBounds);
+ }
+ mSsp.resizeTask(mTaskToResize.key.id, focusedBounds);
+
+ // Get rid of the dialog.
+ dismiss();
+ mRecentsActivity.dismissRecentsToHomeRaw(false);
+
+ // Show tasks - beginning with the other first so that the focus ends on the selected one.
+ // TODO: Remove this once issue b/19893373 is resolved.
+ if (mNextTaskToResize != null && !otherBounds.isEmpty()) {
+ mRecentsView.launchTask(mNextTaskToResize);
+ }
+ mRecentsView.launchTask(mTaskToResize);
}
@Override
public Dialog onCreateDialog(Bundle args) {
final Context context = this.getActivity();
- final SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
LayoutInflater inflater = getActivity().getLayoutInflater();
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- createResizeTaskDialog(context, inflater, builder, ssp);
+ createResizeTaskDialog(context, inflater, builder);
return builder.create();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index a473a29..d60df9c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -259,7 +259,7 @@
public Rect getTaskBounds(int stackId) {
ActivityManager.StackInfo info = getAllStackInfos().get(stackId);
if (info != null)
- return getAllStackInfos().get(stackId).bounds;
+ return info.bounds;
return new Rect();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 448a7a9..abed7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -149,6 +149,31 @@
return mTaskStackViews;
}
+ /** Gets the next task in the stack - or if the last - the top task */
+ public Task getNextTaskOrTopTask(Task taskToSearch) {
+ Task returnTask = null;
+ boolean found = false;
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = stackCount - 1; i >= 0; --i) {
+ TaskStack stack = stackViews.get(i).getStack();
+ ArrayList<Task> taskList = stack.getTasks();
+ // Iterate the stack views and try and find the focused task
+ for (int j = taskList.size() - 1; j >= 0; --j) {
+ Task task = taskList.get(j);
+ // Return the next task in the line.
+ if (found)
+ return task;
+ // Remember the first possible task as the top task.
+ if (returnTask == null)
+ returnTask = task;
+ if (task == taskToSearch)
+ found = true;
+ }
+ }
+ return returnTask;
+ }
+
/** Launches the focused task from the first stack if possible */
public boolean launchFocusedTask() {
// Get the first stack view
@@ -172,6 +197,28 @@
return false;
}
+ /** Launches a given task. */
+ public boolean launchTask(Task task) {
+ // Get the first stack view
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ TaskStack stack = stackView.getStack();
+ // Iterate the stack views and try and find the given task.
+ List<TaskView> taskViews = stackView.getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int j = 0; j < taskViewCount; j++) {
+ TaskView tv = taskViews.get(j);
+ if (tv.getTask() == task) {
+ onTaskViewClicked(stackView, tv, stack, task, false);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/** Launches the task that Recents was launched from, if possible */
public boolean launchPreviousTask() {
// Get the first stack view
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index ca08319..42399a3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -26,14 +26,15 @@
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
@@ -43,7 +44,9 @@
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
@@ -51,6 +54,7 @@
public class TaskViewHeader extends FrameLayout {
RecentsConfiguration mConfig;
+ private SystemServicesProxy mSsp;
// Header views
ImageView mMoveTaskButton;
@@ -91,6 +95,7 @@
public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
+ mSsp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
setWillNotDraw(false);
setClipToOutline(true);
setOutlineProvider(new ViewOutlineProvider() {
@@ -124,9 +129,6 @@
mActivityDescription = (TextView) findViewById(R.id.activity_description);
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
- if (mConfig.multiStackEnabled) {
- mMoveTaskButton.setVisibility(View.VISIBLE);
- }
// Hide the backgrounds if they are ripple drawables
if (!Constants.DebugFlags.App.EnableTaskFiltering) {
@@ -209,6 +211,32 @@
mLightDismissDrawable : mDarkDismissDrawable);
mDismissButton.setContentDescription(String.format(mDismissContentDescription,
t.activityLabel));
+ mMoveTaskButton.setVisibility((mConfig.multiStackEnabled) ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ /** Updates the resize task bar button. */
+ void updateResizeTaskBarIcon(Task t) {
+ Rect display = mSsp.getWindowRect();
+ Rect taskRect = mSsp.getTaskBounds(t.key.stackId);
+ int resId = R.drawable.star;
+ if (display.equals(taskRect)) {
+ resId = R.drawable.vector_drawable_place_fullscreen;
+ } else {
+ boolean top = display.top == taskRect.top;
+ boolean bottom = display.bottom == taskRect.bottom;
+ boolean left = display.left == taskRect.left;
+ boolean right = display.right == taskRect.right;
+ if (top && bottom && left) {
+ resId = R.drawable.vector_drawable_place_left;
+ } else if (top && bottom && right) {
+ resId = R.drawable.vector_drawable_place_right;
+ } else if (top && left && right) {
+ resId = R.drawable.vector_drawable_place_top;
+ } else if (bottom && left && right) {
+ resId = R.drawable.vector_drawable_place_bottom;
+ }
+ }
+ mMoveTaskButton.setImageResource(resId);
}
/** Unbinds the bar view from the task */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index bba7682..a55e026 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -132,6 +132,8 @@
/** Reset the transform on a view. */
public static void reset(View v) {
+ // Cancel any running animations
+ v.animate().cancel();
v.setTranslationX(0f);
v.setTranslationY(0f);
v.setTranslationZ(0f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 0c21b20..a247c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -336,7 +336,6 @@
}
public void launchCamera() {
- mFlashlightController.killFlashlight();
Intent intent = getCameraIntent();
boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity(
mContext, intent, mLockPatternUtils.getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index c9ba8f6..cd1914c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -17,20 +17,14 @@
package com.android.systemui.statusbar.policy;
import android.content.Context;
-import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CaptureRequest;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
+import android.text.TextUtils;
import android.util.Log;
-import android.util.Size;
-import android.view.Surface;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -44,7 +38,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int DISPATCH_ERROR = 0;
- private static final int DISPATCH_OFF = 1;
+ private static final int DISPATCH_CHANGED = 1;
private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
private final CameraManager mCameraManager;
@@ -57,52 +51,50 @@
/** Lock on {@code this} when accessing */
private boolean mFlashlightEnabled;
- private String mCameraId;
- private boolean mCameraAvailable;
- private CameraDevice mCameraDevice;
- private CaptureRequest mFlashlightRequest;
- private CameraCaptureSession mSession;
- private SurfaceTexture mSurfaceTexture;
- private Surface mSurface;
+ private final String mCameraId;
+ private boolean mTorchAvailable;
public FlashlightController(Context mContext) {
mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
- initialize();
- }
- public void initialize() {
+ String cameraId = null;
try {
- mCameraId = getCameraId();
+ cameraId = getCameraId();
} catch (Throwable e) {
Log.e(TAG, "Couldn't initialize.", e);
return;
+ } finally {
+ mCameraId = cameraId;
}
if (mCameraId != null) {
ensureHandler();
- mCameraManager.registerAvailabilityCallback(mAvailabilityCallback, mHandler);
+ mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
}
}
- public synchronized void setFlashlight(boolean enabled) {
- if (mFlashlightEnabled != enabled) {
- mFlashlightEnabled = enabled;
- postUpdateFlashlight();
- }
- }
-
- public void killFlashlight() {
- boolean enabled;
+ public void setFlashlight(boolean enabled) {
+ boolean pendingError = false;
synchronized (this) {
- enabled = mFlashlightEnabled;
+ if (mFlashlightEnabled != enabled) {
+ mFlashlightEnabled = enabled;
+ try {
+ mCameraManager.setTorchMode(mCameraId, enabled);
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Couldn't set torch mode", e);
+ mFlashlightEnabled = false;
+ pendingError = true;
+ }
+ }
}
- if (enabled) {
- mHandler.post(mKillFlashlightRunnable);
+ dispatchModeChanged(mFlashlightEnabled);
+ if (pendingError) {
+ dispatchError();
}
}
public synchronized boolean isAvailable() {
- return mCameraAvailable;
+ return mTorchAvailable;
}
public void addListener(FlashlightListener l) {
@@ -126,42 +118,6 @@
}
}
- private void startDevice() throws CameraAccessException {
- mCameraManager.openCamera(getCameraId(), mCameraListener, mHandler);
- }
-
- private void startSession() throws CameraAccessException {
- mSurfaceTexture = new SurfaceTexture(false);
- Size size = getSmallestSize(mCameraDevice.getId());
- mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
- mSurface = new Surface(mSurfaceTexture);
- ArrayList<Surface> outputs = new ArrayList<>(1);
- outputs.add(mSurface);
- mCameraDevice.createCaptureSession(outputs, mSessionListener, mHandler);
- }
-
- private Size getSmallestSize(String cameraId) throws CameraAccessException {
- Size[] outputSizes = mCameraManager.getCameraCharacteristics(cameraId)
- .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
- .getOutputSizes(SurfaceTexture.class);
- if (outputSizes == null || outputSizes.length == 0) {
- throw new IllegalStateException(
- "Camera " + cameraId + "doesn't support any outputSize.");
- }
- Size chosen = outputSizes[0];
- for (Size s : outputSizes) {
- if (chosen.getWidth() >= s.getWidth() && chosen.getHeight() >= s.getHeight()) {
- chosen = s;
- }
- }
- return chosen;
- }
-
- private void postUpdateFlashlight() {
- ensureHandler();
- mHandler.post(mUpdateFlashlightRunnable);
- }
-
private String getCameraId() throws CameraAccessException {
String[] ids = mCameraManager.getCameraIdList();
for (String id : ids) {
@@ -176,70 +132,12 @@
return null;
}
- private void updateFlashlight(boolean forceDisable) {
- try {
- boolean enabled;
- synchronized (this) {
- enabled = mFlashlightEnabled && !forceDisable;
- }
- if (enabled) {
- if (mCameraDevice == null) {
- startDevice();
- return;
- }
- if (mSession == null) {
- startSession();
- return;
- }
- if (mFlashlightRequest == null) {
- CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(
- CameraDevice.TEMPLATE_PREVIEW);
- builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
- builder.addTarget(mSurface);
- CaptureRequest request = builder.build();
- mSession.capture(request, null, mHandler);
- mFlashlightRequest = request;
- }
- } else {
- if (mCameraDevice != null) {
- mCameraDevice.close();
- teardown();
- }
- }
-
- } catch (CameraAccessException|IllegalStateException|UnsupportedOperationException e) {
- Log.e(TAG, "Error in updateFlashlight", e);
- handleError();
- }
- }
-
- private void teardown() {
- mCameraDevice = null;
- mSession = null;
- mFlashlightRequest = null;
- if (mSurface != null) {
- mSurface.release();
- mSurfaceTexture.release();
- }
- mSurface = null;
- mSurfaceTexture = null;
- }
-
- private void handleError() {
- synchronized (this) {
- mFlashlightEnabled = false;
- }
- dispatchError();
- dispatchOff();
- updateFlashlight(true /* forceDisable */);
- }
-
- private void dispatchOff() {
- dispatchListeners(DISPATCH_OFF, false /* argument (ignored) */);
+ private void dispatchModeChanged(boolean enabled) {
+ dispatchListeners(DISPATCH_CHANGED, enabled);
}
private void dispatchError() {
- dispatchListeners(DISPATCH_ERROR, false /* argument (ignored) */);
+ dispatchListeners(DISPATCH_CHANGED, false /* argument (ignored) */);
}
private void dispatchAvailabilityChanged(boolean available) {
@@ -255,8 +153,8 @@
if (l != null) {
if (message == DISPATCH_ERROR) {
l.onFlashlightError();
- } else if (message == DISPATCH_OFF) {
- l.onFlashlightOff();
+ } else if (message == DISPATCH_CHANGED) {
+ l.onFlashlightChanged(argument);
} else if (message == DISPATCH_AVAILABILITY_CHANGED) {
l.onFlashlightAvailabilityChanged(argument);
}
@@ -279,106 +177,57 @@
}
}
- private final CameraDevice.StateListener mCameraListener = new CameraDevice.StateListener() {
- @Override
- public void onOpened(CameraDevice camera) {
- mCameraDevice = camera;
- postUpdateFlashlight();
- }
+ private final CameraManager.TorchCallback mTorchCallback =
+ new CameraManager.TorchCallback() {
@Override
- public void onDisconnected(CameraDevice camera) {
- if (mCameraDevice == camera) {
- dispatchOff();
- teardown();
- }
- }
-
- @Override
- public void onError(CameraDevice camera, int error) {
- Log.e(TAG, "Camera error: camera=" + camera + " error=" + error);
- if (camera == mCameraDevice || mCameraDevice == null) {
- handleError();
- }
- }
- };
-
- private final CameraCaptureSession.StateListener mSessionListener =
- new CameraCaptureSession.StateListener() {
- @Override
- public void onConfigured(CameraCaptureSession session) {
- if (session.getDevice() == mCameraDevice) {
- mSession = session;
- } else {
- session.close();
- }
- postUpdateFlashlight();
- }
-
- @Override
- public void onConfigureFailed(CameraCaptureSession session) {
- Log.e(TAG, "Configure failed.");
- if (mSession == null || mSession == session) {
- handleError();
- }
- }
- };
-
- private final Runnable mUpdateFlashlightRunnable = new Runnable() {
- @Override
- public void run() {
- updateFlashlight(false /* forceDisable */);
- }
- };
-
- private final Runnable mKillFlashlightRunnable = new Runnable() {
- @Override
- public void run() {
- synchronized (this) {
- mFlashlightEnabled = false;
- }
- updateFlashlight(true /* forceDisable */);
- dispatchOff();
- }
- };
-
- private final CameraManager.AvailabilityCallback mAvailabilityCallback =
- new CameraManager.AvailabilityCallback() {
- @Override
- public void onCameraAvailable(String cameraId) {
- if (DEBUG) Log.d(TAG, "onCameraAvailable(" + cameraId + ")");
- if (cameraId.equals(mCameraId)) {
- setCameraAvailable(true);
- }
- }
-
- @Override
- public void onCameraUnavailable(String cameraId) {
- if (DEBUG) Log.d(TAG, "onCameraUnavailable(" + cameraId + ")");
- if (cameraId.equals(mCameraId)) {
+ public void onTorchModeUnavailable(String cameraId) {
+ if (TextUtils.equals(cameraId, mCameraId)) {
setCameraAvailable(false);
}
}
+ @Override
+ public void onTorchModeChanged(String cameraId, boolean enabled) {
+ if (TextUtils.equals(cameraId, mCameraId)) {
+ setCameraAvailable(true);
+ setTorchMode(enabled);
+ }
+ }
+
private void setCameraAvailable(boolean available) {
boolean changed;
synchronized (FlashlightController.this) {
- changed = mCameraAvailable != available;
- mCameraAvailable = available;
+ changed = mTorchAvailable != available;
+ mTorchAvailable = available;
}
if (changed) {
if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
dispatchAvailabilityChanged(available);
}
}
+
+ private void setTorchMode(boolean enabled) {
+ boolean changed;
+ synchronized (FlashlightController.this) {
+ changed = mFlashlightEnabled != enabled;
+ mFlashlightEnabled = enabled;
+ }
+ if (changed) {
+ if (DEBUG) Log.d(TAG, "dispatchModeChanged(" + enabled + ")");
+ dispatchModeChanged(enabled);
+ }
+ }
};
public interface FlashlightListener {
/**
- * Called when the flashlight turns off unexpectedly.
+ * Called when the flashlight was turned off or on.
+ * @param enabled true if the flashlight is currently turned on.
*/
- void onFlashlightOff();
+ void onFlashlightChanged(boolean enabled);
+
/**
* Called when there is an error that turns the flashlight off.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 2fbb812..f0dd943 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -190,7 +190,8 @@
NetworkCapabilities networkCapabilities =
mConnectivityManager.getNetworkCapabilities(network);
if (DEBUG) Log.d(TAG, "onAvailable " + network.netId + " : " + networkCapabilities);
- if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
+ if (networkCapabilities != null &&
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
setCurrentNetid(network.netId);
}
};
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 8562288..e8e942c 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -1351,7 +1351,7 @@
}
/**
- * calls create(cts, ContextType.NORMAL, CREATE_FLAG_NONE)
+ * calls create(ctx, ContextType.NORMAL, CREATE_FLAG_NONE)
*
* See documentation for @create for details
*
@@ -1363,7 +1363,7 @@
}
/**
- * calls create(cts, ct, CREATE_FLAG_NONE)
+ * calls create(ctx, ct, CREATE_FLAG_NONE)
*
* See documentation for @create for details
*
@@ -1375,7 +1375,8 @@
return create(ctx, ct, CREATE_FLAG_NONE);
}
- /**
+
+ /**
* Gets or creates a RenderScript context of the specified type.
*
* The returned context will be cached for future reuse within
@@ -1397,21 +1398,49 @@
*/
public static RenderScript create(Context ctx, ContextType ct, int flags) {
int v = ctx.getApplicationInfo().targetSdkVersion;
- if (v < 23) {
- return internalCreate(ctx, v, ct, flags);
+ return create(ctx, v, ct, flags);
+ }
+
+ /**
+ * calls create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE)
+ *
+ * Used by the RenderScriptThunker to maintain backward compatibility.
+ *
+ * @hide
+ * @param ctx The context.
+ * @param sdkVersion The target SDK Version.
+ * @return RenderScript
+ */
+ public static RenderScript create(Context ctx, int sdkVersion) {
+ return create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE);
+ }
+
+ /**
+ * Gets or creates a RenderScript context of the specified type.
+ *
+ * @hide
+ * @param ctx The context.
+ * @param ct The type of context to be created.
+ * @param sdkVersion The target SDK Version.
+ * @param flags The OR of the CREATE_FLAG_* options desired
+ * @return RenderScript
+ */
+ public static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) {
+ if (sdkVersion < 23) {
+ return internalCreate(ctx, sdkVersion, ct, flags);
}
synchronized (mProcessContextList) {
for (RenderScript prs : mProcessContextList) {
if ((prs.mContextType == ct) &&
(prs.mContextFlags == flags) &&
- (prs.mContextSdkVersion == v)) {
+ (prs.mContextSdkVersion == sdkVersion)) {
return prs;
}
}
- RenderScript prs = internalCreate(ctx, v, ct, flags);
+ RenderScript prs = internalCreate(ctx, sdkVersion, ct, flags);
prs.mIsProcessContext = true;
mProcessContextList.add(prs);
return prs;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 8fcdd39..31f9e22 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -22,6 +22,7 @@
import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.app.IBackupAgent;
+import android.app.PackageInstallObserver;
import android.app.PendingIntent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
@@ -45,7 +46,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -200,7 +200,6 @@
private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
- private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
private static final int MSG_RUN_BACKUP = 1;
private static final int MSG_RUN_ADB_BACKUP = 2;
private static final int MSG_RUN_RESTORE = 3;
@@ -1198,11 +1197,13 @@
temp = new RandomAccessFile(tempProcessedFile, "rws");
in = new RandomAccessFile(mEverStored, "r");
+ // Loop until we hit EOF
while (true) {
- PackageInfo info;
String pkg = in.readUTF();
try {
- info = mPackageManager.getPackageInfo(pkg, 0);
+ // is this package still present?
+ mPackageManager.getPackageInfo(pkg, 0);
+ // if we get here then yes it is; remember it
mEverStoredApps.add(pkg);
temp.writeUTF(pkg);
if (MORE_DEBUG) Slog.v(TAG, " + " + pkg);
@@ -4100,7 +4101,6 @@
SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
AtomicBoolean latch) throws IOException {
- int oldfd = output.getFd();
mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
mTarget = target;
mLatch = latch;
@@ -4812,7 +4812,7 @@
}
}
- class RestoreInstallObserver extends IPackageInstallObserver.Stub {
+ class RestoreInstallObserver extends PackageInstallObserver {
final AtomicBoolean mDone = new AtomicBoolean();
String mPackageName;
int mResult;
@@ -4838,8 +4838,8 @@
}
@Override
- public void packageInstalled(String packageName, int returnCode)
- throws RemoteException {
+ public void onPackageInstalled(String packageName, int returnCode,
+ String msg, Bundle extras) {
synchronized (mDone) {
mResult = returnCode;
mPackageName = packageName;
@@ -5095,7 +5095,9 @@
offset = extractLine(buffer, offset, str);
version = Integer.parseInt(str[0]); // app version
offset = extractLine(buffer, offset, str);
- int platformVersion = Integer.parseInt(str[0]);
+ // This is the platform version, which we don't use, but we parse it
+ // as a safety against corruption in the manifest.
+ Integer.parseInt(str[0]);
offset = extractLine(buffer, offset, str);
info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
offset = extractLine(buffer, offset, str);
@@ -6156,7 +6158,7 @@
}
}
- class RestoreInstallObserver extends IPackageInstallObserver.Stub {
+ class RestoreInstallObserver extends PackageInstallObserver {
final AtomicBoolean mDone = new AtomicBoolean();
String mPackageName;
int mResult;
@@ -6182,8 +6184,8 @@
}
@Override
- public void packageInstalled(String packageName, int returnCode)
- throws RemoteException {
+ public void onPackageInstalled(String packageName, int returnCode,
+ String msg, Bundle extras) {
synchronized (mDone) {
mResult = returnCode;
mPackageName = packageName;
@@ -6432,7 +6434,9 @@
offset = extractLine(buffer, offset, str);
version = Integer.parseInt(str[0]); // app version
offset = extractLine(buffer, offset, str);
- int platformVersion = Integer.parseInt(str[0]);
+ // This is the platform version, which we don't use, but we parse it
+ // as a safety against corruption in the manifest.
+ Integer.parseInt(str[0]);
offset = extractLine(buffer, offset, str);
info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
offset = extractLine(buffer, offset, str);
@@ -8357,7 +8361,9 @@
}
// make sure the screen is lit for the user interaction
- mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
+ mPowerManager.userActivity(SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ 0);
// start the confirmation countdown
startConfirmationTimeout(token, params);
@@ -8440,7 +8446,9 @@
}
// make sure the screen is lit for the user interaction
- mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
+ mPowerManager.userActivity(SystemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ 0);
// start the confirmation countdown
startConfirmationTimeout(token, params);
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index fd35b5e..4677f65 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -86,7 +86,10 @@
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
import android.text.style.SuggestionSpan;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.LruCache;
@@ -125,6 +128,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -134,8 +138,12 @@
public class InputMethodManagerService extends IInputMethodManager.Stub
implements ServiceConnection, Handler.Callback {
static final boolean DEBUG = false;
+ static final boolean DEBUG_RESTORE = DEBUG || false;
static final String TAG = "InputMethodManagerService";
+ private static final char INPUT_METHOD_SEPARATOR = ':';
+ private static final char INPUT_METHOD_SUBTYPE_SEPARATOR = ';';
+
static final int MSG_SHOW_IM_PICKER = 1;
static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 3;
@@ -466,12 +474,101 @@
|| Intent.ACTION_USER_REMOVED.equals(action)) {
updateCurrentProfileIds();
return;
+ } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
+ final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
+ if (Settings.Secure.ENABLED_INPUT_METHODS.equals(name)) {
+ final String prevValue = intent.getStringExtra(
+ Intent.EXTRA_SETTING_PREVIOUS_VALUE);
+ final String newValue = intent.getStringExtra(
+ Intent.EXTRA_SETTING_NEW_VALUE);
+ restoreEnabledInputMethods(mContext, prevValue, newValue);
+ }
} else {
Slog.w(TAG, "Unexpected intent " + intent);
}
}
}
+ // Apply the results of a restore operation to the set of enabled IMEs. Note that this
+ // does not attempt to validate on the fly with any installed device policy, so must only
+ // be run in the context of initial device setup.
+ //
+ // TODO: Move this method to InputMethodUtils with adding unit tests.
+ static void restoreEnabledInputMethods(Context context, String prevValue, String newValue) {
+ if (DEBUG_RESTORE) {
+ Slog.i(TAG, "Restoring enabled input methods:");
+ Slog.i(TAG, "prev=" + prevValue);
+ Slog.i(TAG, " new=" + newValue);
+ }
+ // 'new' is the just-restored state, 'prev' is what was in settings prior to the restore
+ ArrayMap<String, ArraySet<String>> prevMap = parseInputMethodsAndSubtypesString(prevValue);
+ ArrayMap<String, ArraySet<String>> newMap = parseInputMethodsAndSubtypesString(newValue);
+
+ // Merge the restored ime+subtype enabled states into the live state
+ for (ArrayMap.Entry<String, ArraySet<String>> entry : newMap.entrySet()) {
+ final String imeId = entry.getKey();
+ ArraySet<String> prevSubtypes = prevMap.get(imeId);
+ if (prevSubtypes == null) {
+ prevSubtypes = new ArraySet<String>(2);
+ prevMap.put(imeId, prevSubtypes);
+ }
+ prevSubtypes.addAll(entry.getValue());
+ }
+
+ final String mergedImesAndSubtypesString = buildInputMethodsAndSubtypesString(prevMap);
+ if (DEBUG_RESTORE) {
+ Slog.i(TAG, "Merged IME string:");
+ Slog.i(TAG, " " + mergedImesAndSubtypesString);
+ }
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.ENABLED_INPUT_METHODS, mergedImesAndSubtypesString);
+ }
+
+ // TODO: Move this method to InputMethodUtils with adding unit tests.
+ static String buildInputMethodsAndSubtypesString(ArrayMap<String, ArraySet<String>> map) {
+ // we want to use the canonical InputMethodSettings implementation,
+ // so we convert data structures first.
+ List<Pair<String, ArrayList<String>>> imeMap =
+ new ArrayList<Pair<String, ArrayList<String>>>(4);
+ for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) {
+ final String imeName = entry.getKey();
+ final ArraySet<String> subtypeSet = entry.getValue();
+ final ArrayList<String> subtypes = new ArrayList<String>(2);
+ if (subtypeSet != null) {
+ subtypes.addAll(subtypeSet);
+ }
+ imeMap.add(new Pair<String, ArrayList<String>>(imeName, subtypes));
+ }
+ return InputMethodSettings.buildInputMethodsSettingString(imeMap);
+ }
+
+ // TODO: Move this method to InputMethodUtils with adding unit tests.
+ static ArrayMap<String, ArraySet<String>> parseInputMethodsAndSubtypesString(
+ final String inputMethodsAndSubtypesString) {
+ final ArrayMap<String, ArraySet<String>> imeMap =
+ new ArrayMap<String, ArraySet<String>>();
+ if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
+ return imeMap;
+ }
+
+ final SimpleStringSplitter typeSplitter =
+ new SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
+ final SimpleStringSplitter subtypeSplitter =
+ new SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
+ List<Pair<String, ArrayList<String>>> allImeSettings =
+ InputMethodSettings.buildInputMethodsAndSubtypeList(inputMethodsAndSubtypesString,
+ typeSplitter,
+ subtypeSplitter);
+ for (Pair<String, ArrayList<String>> ime : allImeSettings) {
+ ArraySet<String> subtypes = new ArraySet<String>();
+ if (ime.second != null) {
+ subtypes.addAll(ime.second);
+ }
+ imeMap.put(ime.first, subtypes);
+ }
+ return imeMap;
+ }
+
class MyPackageMonitor extends PackageMonitor {
private boolean isChangingPackagesOfCurrentUser() {
final int userId = getChangingUserId();
@@ -675,6 +772,7 @@
broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
+ broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED);
mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
mNotificationShown = false;
diff --git a/services/core/java/com/android/server/MidiService.java b/services/core/java/com/android/server/MidiService.java
index 3418930..e753664 100644
--- a/services/core/java/com/android/server/MidiService.java
+++ b/services/core/java/com/android/server/MidiService.java
@@ -47,6 +47,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
public class MidiService extends IMidiManager.Stub {
@@ -269,7 +270,9 @@
public void binderDied() {
synchronized (mDevicesByInfo) {
- removeDeviceLocked(this);
+ if (mDevicesByInfo.remove(mDeviceInfo) != null) {
+ removeDeviceLocked(this);
+ }
}
}
@@ -368,6 +371,7 @@
synchronized (mDevicesByInfo) {
Device device = mDevicesByServer.get(server.asBinder());
if (device != null) {
+ mDevicesByInfo.remove(device.getDeviceInfo());
removeDeviceLocked(device);
}
}
@@ -454,16 +458,14 @@
// synchronize on mDevicesByInfo
private void removeDeviceLocked(Device device) {
- if (mDevicesByInfo.remove(device.getDeviceInfo()) != null) {
- IMidiDeviceServer server = device.getDeviceServer();
- if (server != null) {
- mDevicesByServer.remove(server);
- }
+ IMidiDeviceServer server = device.getDeviceServer();
+ if (server != null) {
+ mDevicesByServer.remove(server);
+ }
- synchronized (mClients) {
- for (Client c : mClients.values()) {
- c.deviceRemoved(device);
- }
+ synchronized (mClients) {
+ for (Client c : mClients.values()) {
+ c.deviceRemoved(device);
}
}
}
@@ -616,8 +618,11 @@
private void removePackageDeviceServers(String packageName) {
synchronized (mDevicesByInfo) {
- for (Device device : mDevicesByInfo.values()) {
+ Iterator<Device> iterator = mDevicesByInfo.values().iterator();
+ while (iterator.hasNext()) {
+ Device device = iterator.next();
if (packageName.equals(device.getPackageName())) {
+ iterator.remove();
removeDeviceLocked(device);
}
}
@@ -634,15 +639,19 @@
pw.println("Devices:");
pw.increaseIndent();
- for (Device device : mDevicesByInfo.values()) {
- pw.println(device.toString());
+ synchronized (mDevicesByInfo) {
+ for (Device device : mDevicesByInfo.values()) {
+ pw.println(device.toString());
+ }
}
pw.decreaseIndent();
pw.println("Clients:");
pw.increaseIndent();
- for (Client client : mClients.values()) {
- pw.println(client.toString());
+ synchronized (mClients) {
+ for (Client client : mClients.values()) {
+ pw.println(client.toString());
+ }
}
pw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 6ad128c..4c9d7d3 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -71,9 +71,11 @@
public static final class PermissionEntry {
public final String name;
public int[] gids;
+ public boolean perUser;
- PermissionEntry(String _name) {
- name = _name;
+ PermissionEntry(String name, boolean perUser) {
+ this.name = name;
+ this.perUser = perUser;
}
}
@@ -363,14 +365,14 @@
void readPermission(XmlPullParser parser, String name)
throws IOException, XmlPullParserException {
-
- name = name.intern();
-
- PermissionEntry perm = mPermissions.get(name);
- if (perm == null) {
- perm = new PermissionEntry(name);
- mPermissions.put(name, perm);
+ if (mPermissions.containsKey(name)) {
+ throw new IllegalStateException("Duplicate permission definition for " + name);
}
+
+ final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
+ final PermissionEntry perm = new PermissionEntry(name, perUser);
+ mPermissions.put(name, perm);
+
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 87dc420..64f3070 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -157,7 +157,6 @@
@Override
public void onStart() {
final Context context = getContext();
- mTwilightManager = getLocalService(TwilightManager.class);
final PowerManager powerManager =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -188,7 +187,11 @@
mNightMode = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.UI_NIGHT_MODE, defaultNightMode);
- mTwilightManager.registerListener(mTwilightListener, mHandler);
+ // Update the initial, static configurations.
+ synchronized (this) {
+ updateConfigurationLocked();
+ sendConfigurationLocked();
+ }
publishBinderService(Context.UI_MODE_SERVICE, mService);
}
@@ -297,8 +300,11 @@
pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration);
pw.print(" mSystemReady="); pw.println(mSystemReady);
- pw.print(" mTwilightService.getCurrentState()=");
- pw.println(mTwilightManager.getCurrentState());
+ if (mTwilightManager != null) {
+ // We may not have a TwilightManager.
+ pw.print(" mTwilightService.getCurrentState()=");
+ pw.println(mTwilightManager.getCurrentState());
+ }
}
}
@@ -306,6 +312,10 @@
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
synchronized (mLock) {
+ mTwilightManager = getLocalService(TwilightManager.class);
+ if (mTwilightManager != null) {
+ mTwilightManager.registerListener(mTwilightListener, mHandler);
+ }
mSystemReady = true;
mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
updateComputedNightModeLocked();
@@ -623,9 +633,11 @@
}
private void updateComputedNightModeLocked() {
- TwilightState state = mTwilightManager.getCurrentState();
- if (state != null) {
- mComputedNightMode = state.isNight();
+ if (mTwilightManager != null) {
+ TwilightState state = mTwilightManager.getCurrentState();
+ if (state != null) {
+ mComputedNightMode = state.isNight();
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 11c3ea6..78bd15d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -76,6 +76,7 @@
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.MemInfoReader;
@@ -1231,7 +1232,7 @@
TAG, "Death received in " + this
+ " for thread " + mAppThread.asBinder());
synchronized(ActivityManagerService.this) {
- appDiedLocked(mApp, mPid, mAppThread);
+ appDiedLocked(mApp, mPid, mAppThread, true);
}
}
}
@@ -3045,12 +3046,12 @@
int[] permGids = null;
try {
checkTime(startTime, "startProcess: getting gids from package manager");
- final PackageManager pm = mContext.getPackageManager();
- permGids = pm.getPackageGids(app.info.packageName);
+ permGids = AppGlobals.getPackageManager().getPackageGids(app.info.packageName,
+ app.userId);
if (Environment.isExternalStorageEmulated()) {
checkTime(startTime, "startProcess: checking external storage perm");
- if (pm.checkPermission(
+ if (mContext.getPackageManager().checkPermission(
android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
app.info.packageName) == PERMISSION_GRANTED) {
mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
@@ -3058,7 +3059,7 @@
mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
}
}
- } catch (PackageManager.NameNotFoundException e) {
+ } catch (RemoteException e) {
Slog.w(TAG, "Unable to retrieve gids", e);
}
@@ -3066,7 +3067,7 @@
* Add shared application and profile GIDs so applications can share some
* resources like shared libraries and access user-wide resources
*/
- if (permGids == null) {
+ if (ArrayUtils.isEmpty(permGids)) {
gids = new int[2];
} else {
gids = new int[permGids.length + 2];
@@ -4303,10 +4304,11 @@
}
final void appDiedLocked(ProcessRecord app) {
- appDiedLocked(app, app.pid, app.thread);
+ appDiedLocked(app, app.pid, app.thread, false);
}
- final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread) {
+ final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
+ boolean fromBinderDied) {
// First check if this ProcessRecord is actually active for the pid.
synchronized (mPidsSelfLocked) {
ProcessRecord curProc = mPidsSelfLocked.get(pid);
@@ -4322,7 +4324,9 @@
}
if (!app.killed) {
- Process.killProcessQuiet(pid);
+ if (!fromBinderDied) {
+ Process.killProcessQuiet(pid);
+ }
Process.killProcessGroup(app.info.uid, pid);
app.killed = true;
}
@@ -6530,7 +6534,7 @@
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
- return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
+ return checkComponentPermission(permission, pid, uid, -1, true);
}
@Override
@@ -6550,7 +6554,7 @@
pid = tlsIdentity.pid;
}
- return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
+ return checkComponentPermission(permission, pid, uid, -1, true);
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b102a07..f5fef63 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1112,7 +1112,7 @@
}
}
- private void setVisibile(ActivityRecord r, boolean visible) {
+ private void setVisible(ActivityRecord r, boolean visible) {
r.visible = visible;
mWindowManager.setAppVisibility(r.appToken, visible);
final ArrayList<ActivityContainer> containers = r.mChildContainers;
@@ -1297,7 +1297,7 @@
if (!r.visible || r.mLaunchTaskBehind) {
if (DEBUG_VISBILITY) Slog.v(
TAG, "Starting and making visible: " + r);
- setVisibile(r, true);
+ setVisible(r, true);
}
if (r != starting) {
mStackSupervisor.startSpecificActivityLocked(r, false, false);
@@ -1329,7 +1329,7 @@
r.updateOptionsLocked(r.returningOptions);
mUndrawnActivitiesBelowTopTranslucent.add(r);
}
- setVisibile(r, true);
+ setVisible(r, true);
r.sleeping = false;
r.app.pendingUiClean = true;
r.app.thread.scheduleWindowVisibility(r.appToken, true);
@@ -1364,7 +1364,7 @@
if (r.visible) {
if (DEBUG_VISBILITY) Slog.v(TAG, "Making invisible: " + r);
try {
- setVisibile(r, false);
+ setVisible(r, false);
switch (r.state) {
case STOPPING:
case STOPPED:
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e9e6496..cb96680 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -129,7 +129,7 @@
static final boolean DEBUG_RELEASE = DEBUG || false;
static final boolean DEBUG_SAVED_STATE = DEBUG || false;
static final boolean DEBUG_SCREENSHOTS = DEBUG || false;
- static final boolean DEBUG_STATES = DEBUG || false;
+ static final boolean DEBUG_STATES = DEBUG || true;
static final boolean DEBUG_VISIBLE_BEHIND = DEBUG || false;
public static final int HOME_STACK_ID = 0;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8fe1238..34c1c53 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -765,7 +765,7 @@
try {
perm = AppGlobals.getPackageManager().
checkPermission(r.requiredPermission,
- info.activityInfo.applicationInfo.packageName);
+ info.activityInfo.applicationInfo.packageName, r.userId);
} catch (RemoteException e) {
perm = PackageManager.PERMISSION_DENIED;
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index fe1260d..d79b5fd 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -41,6 +41,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -109,21 +110,22 @@
* Track Services that have currently active or pending jobs. The index is provided by
* {@link JobStatus#getServiceToken()}
*/
- final List<JobServiceContext> mActiveServices = new ArrayList<JobServiceContext>();
+ final List<JobServiceContext> mActiveServices = new ArrayList<>();
/** List of controllers that will notify this service of updates to jobs. */
List<StateController> mControllers;
/**
* Queue of pending jobs. The JobServiceContext class will receive jobs from this list
* when ready to execute them.
*/
- final ArrayList<JobStatus> mPendingJobs = new ArrayList<JobStatus>();
+ final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
- final ArrayList<Integer> mStartedUsers = new ArrayList();
+ final ArrayList<Integer> mStartedUsers = new ArrayList<>();
final JobHandler mHandler;
final JobSchedulerStub mJobSchedulerStub;
IBatteryStats mBatteryStats;
+ PowerManager mPowerManager;
/**
* Set to true once we are allowed to run third party apps.
@@ -131,6 +133,11 @@
boolean mReadyToRock;
/**
+ * True when in device idle mode, so we don't want to schedule any jobs.
+ */
+ boolean mDeviceIdleMode;
+
+ /**
* Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
* still clean up. On reinstall the package will have a new uid.
*/
@@ -154,6 +161,8 @@
Slog.d(TAG, "Removing jobs for user: " + userId);
}
cancelJobsForUser(userId);
+ } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) {
+ updateIdleMode(mPowerManager != null ? mPowerManager.isDeviceIdleMode() : false);
}
}
};
@@ -199,7 +208,7 @@
return outList;
}
- private void cancelJobsForUser(int userHandle) {
+ void cancelJobsForUser(int userHandle) {
List<JobStatus> jobsForUser;
synchronized (mJobs) {
jobsForUser = mJobs.getJobsByUser(userHandle);
@@ -257,6 +266,40 @@
}
}
+ void updateIdleMode(boolean enabled) {
+ boolean changed = false;
+ boolean rocking;
+ synchronized (mJobs) {
+ if (mDeviceIdleMode != enabled) {
+ changed = true;
+ }
+ rocking = mReadyToRock;
+ }
+ if (changed) {
+ if (rocking) {
+ for (int i=0; i<mControllers.size(); i++) {
+ mControllers.get(i).deviceIdleModeChanged(enabled);
+ }
+ }
+ synchronized (mJobs) {
+ mDeviceIdleMode = enabled;
+ if (enabled) {
+ // When becoming idle, make sure no jobs are actively running.
+ for (int i=0; i<mActiveServices.size(); i++) {
+ JobServiceContext jsc = mActiveServices.get(i);
+ final JobStatus executing = jsc.getRunningJob();
+ if (executing != null) {
+ jsc.cancelExecutingJob();
+ }
+ }
+ } else {
+ // When coming out of idle, allow thing to start back up.
+ mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ }
+ }
+ }
+ }
+
/**
* Initializes the system service.
* <p>
@@ -294,8 +337,10 @@
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
+ userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+ mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mJobs) {
// Let's go!
@@ -313,6 +358,7 @@
for (int i=0; i<jobs.size(); i++) {
JobStatus job = jobs.valueAt(i);
for (int controller=0; controller<mControllers.size(); controller++) {
+ mControllers.get(controller).deviceIdleModeChanged(mDeviceIdleMode);
mControllers.get(controller).maybeStartTrackingJob(job);
}
}
@@ -667,6 +713,10 @@
*/
private void maybeRunPendingJobsH() {
synchronized (mJobs) {
+ if (mDeviceIdleMode) {
+ // If device is idle, we will not schedule jobs to run.
+ return;
+ }
Iterator<JobStatus> it = mPendingJobs.iterator();
if (DEBUG) {
Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
@@ -878,6 +928,7 @@
}
pw.println();
pw.print("mReadyToRock="); pw.println(mReadyToRock);
+ pw.print("mDeviceIdleMode="); pw.println(mDeviceIdleMode);
}
pw.println();
}
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index 7d76fc0..efd1928 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -31,12 +31,17 @@
protected static final boolean DEBUG = false;
protected Context mContext;
protected StateChangedListener mStateChangedListener;
+ protected boolean mDeviceIdleMode;
public StateController(StateChangedListener stateChangedListener, Context context) {
mStateChangedListener = stateChangedListener;
mContext = context;
}
+ public void deviceIdleModeChanged(boolean enabled) {
+ mDeviceIdleMode = enabled;
+ }
+
/**
* Implement the logic here to decide whether a job should be tracked by this controller.
* This logic is put here so the JobManger can be completely agnostic of Controller logic.
@@ -50,5 +55,4 @@
public abstract void maybeStopTrackingJob(JobStatus jobStatus);
public abstract void dumpControllerState(PrintWriter pw);
-
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a530dfa..65949bf 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -55,6 +55,7 @@
import android.speech.RecognizerIntent;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.KeyEvent;
@@ -743,15 +744,23 @@
Log.w(TAG, "Attempted to dispatch null or non-media key event.");
return;
}
+
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
- if (DEBUG) {
- Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
- + keyEvent);
- }
-
try {
+ if (DEBUG) {
+ Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
+ + keyEvent);
+ }
+ if (!isUserSetupComplete()) {
+ // Global media key handling can have the side-effect of starting new
+ // activities which is undesirable while setup is in progress.
+ Slog.i(TAG, "Not dispatching media key event because user "
+ + "setup is in progress.");
+ return;
+ }
+
synchronized (mLock) {
// If we don't have a media button receiver to fall back on
// include non-playing sessions for dispatching
@@ -1025,6 +1034,11 @@
return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
}
+ private boolean isUserSetupComplete() {
+ return Settings.Secure.getIntForUser(getContext().getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+ }
+
// we only handle public stream types, which are 0-5
private boolean isValidLocalStreamType(int streamType) {
return streamType >= AudioManager.STREAM_VOICE_CALL
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 8d46775..5de7d42 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -71,7 +71,9 @@
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import android.Manifest;
import android.app.ActivityManager;
+import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.IProcessObserver;
@@ -83,6 +85,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
@@ -2021,6 +2024,17 @@
void updateRulesForUidLocked(int uid) {
if (!isUidValidForRules(uid)) return;
+ // quick check: if this uid doesn't have INTERNET permission, it doesn't have
+ // network access anyway, so it is a waste to mess with it here.
+ final IPackageManager ipm = AppGlobals.getPackageManager();
+ try {
+ if (ipm.checkUidPermission(Manifest.permission.INTERNET, uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ } catch (RemoteException e) {
+ }
+
final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
final boolean uidForeground = isUidForegroundLocked(uid);
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 4f27408..ec290ef 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -18,6 +18,9 @@
import android.content.pm.PackageParser;
import android.content.pm.PermissionInfo;
+import android.os.UserHandle;
+
+import com.android.internal.util.ArrayUtils;
final class BasePermission {
final static int TYPE_NORMAL = 0;
@@ -40,9 +43,17 @@
PermissionInfo pendingInfo;
+ /** UID that owns the definition of this permission */
int uid;
- int[] gids;
+ /** Additional GIDs given to apps granted this permission */
+ private int[] gids;
+
+ /**
+ * Flag indicating that {@link #gids} should be adjusted based on the
+ * {@link UserHandle} the granted app is running as.
+ */
+ private boolean perUser;
BasePermission(String _name, String _sourcePackage, int _type) {
name = _name;
@@ -52,8 +63,35 @@
protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
}
+ @Override
public String toString() {
return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
+ "}";
}
+
+ public void setGids(int[] gids, boolean perUser) {
+ this.gids = gids;
+ this.perUser = perUser;
+ }
+
+ public boolean hasGids() {
+ return ArrayUtils.isEmpty(gids);
+ }
+
+ public int[] computeGids(int userId) {
+ if (perUser) {
+ final int[] userGids = new int[gids.length];
+ for (int i = 0; i < gids.length; i++) {
+ userGids[i] = UserHandle.getUid(userId, gids[i]);
+ }
+ return userGids;
+ } else {
+ return gids;
+ }
+ }
+
+ public boolean isRuntime() {
+ return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 52411bf..2629e48 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -54,7 +54,6 @@
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.internal.util.ArrayUtils.appendInt;
-import static com.android.internal.util.ArrayUtils.removeInt;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -249,6 +248,9 @@
private static final boolean DEBUG_DEXOPT = false;
private static final boolean DEBUG_ABI_SELECTION = false;
+ private static final boolean RUNTIME_PERMISSIONS_ENABLED =
+ SystemProperties.getInt("ro.runtime.premissions.enabled", 0) == 1;
+
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
@@ -321,10 +323,28 @@
DEFAULT_CONTAINER_PACKAGE,
"com.android.defcontainer.DefaultContainerService");
+ private static final String KILL_APP_REASON_GIDS_CHANGED =
+ "permission grant or revoke changed gids";
+
+ private static final String KILL_APP_REASON_PERMISSIONS_REVOKED =
+ "permissions revoked";
+
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
+ /** Permission grant: not grant the permission. */
+ private static final int GRANT_DENIED = 1;
+
+ /** Permission grant: grant the permission as an install permission. */
+ private static final int GRANT_INSTALL = 2;
+
+ /** Permission grant: grant the permission as a runtime permission. */
+ private static final int GRANT_RUNTIME = 3;
+
+ /** Permission grant: grant as runtime a permission that was granted as an install time one. */
+ private static final int GRANT_UPGRADE = 4;
+
final ServiceThread mHandlerThread;
final PackageHandler mHandler;
@@ -994,6 +1014,15 @@
res.removedInfo.sendBroadcast(false, true, false);
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, res.uid);
+
+ // Now that we successfully installed the package, grant runtime
+ // permissions if requested before broadcasting the install.
+ if ((args.installFlags
+ & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0) {
+ grantRequestedRuntimePermissions(res.pkg,
+ args.user.getIdentifier());
+ }
+
// Determine the set of users who are adding this
// package for the first time vs. those who are seeing
// an update.
@@ -1214,6 +1243,32 @@
}
}
+ private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int userId) {
+ if (userId >= UserHandle.USER_OWNER) {
+ grantRequestedRuntimePermissionsForUser(pkg, userId);
+ } else if (userId == UserHandle.USER_ALL) {
+ for (int someUserId : UserManagerService.getInstance().getUserIds()) {
+ grantRequestedRuntimePermissionsForUser(pkg, someUserId);
+ }
+ }
+ }
+
+ private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId) {
+ SettingBase sb = (SettingBase) pkg.mExtras;
+ if (sb == null) {
+ return;
+ }
+
+ PermissionsState permissionsState = sb.getPermissionsState();
+
+ for (String permission : pkg.requestedPermissions) {
+ BasePermission bp = mSettings.mPermissions.get(permission);
+ if (bp != null && bp.isRuntime()) {
+ permissionsState.grantRuntimePermission(bp, userId);
+ }
+ }
+ }
+
Bundle extrasForInstallResult(PackageInstalledInfo res) {
Bundle extras = null;
switch (res.returnCode) {
@@ -1243,7 +1298,7 @@
}
}
- public static final PackageManagerService main(Context context, Installer installer,
+ public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
@@ -1293,7 +1348,7 @@
mOnlyCore = onlyCore;
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
- mSettings = new Settings(context);
+ mSettings = new Settings(mContext, mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
@@ -1374,7 +1429,7 @@
mSettings.mPermissions.put(perm.name, bp);
}
if (perm.gids != null) {
- bp.gids = appendInts(bp.gids, perm.gids);
+ bp.setGids(perm.gids, perm.perUser);
}
}
@@ -1832,14 +1887,8 @@
final String packageName = info.activityInfo.packageName;
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- continue;
- }
-
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
- if (!gp.grantedPermissions
- .contains(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)) {
+ if (checkPermission(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
continue;
}
@@ -1895,26 +1944,21 @@
return cur;
}
- static int[] removeInts(int[] cur, int[] rem) {
- if (rem == null) return cur;
- if (cur == null) return cur;
- final int N = rem.length;
- for (int i=0; i<N; i++) {
- cur = removeInt(cur, rem[i]);
- }
- return cur;
- }
-
PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
final PackageSetting ps = (PackageSetting) p.mExtras;
if (ps == null) {
return null;
}
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+
+ PermissionsState permissionsState = ps.getPermissionsState();
+
+ final int[] gids = permissionsState.computeGids(userId);
+ Set<String> permissions = permissionsState.getPermissions(userId);
+
final PackageUserState state = ps.readUserState(userId);
- return PackageParser.generatePackageInfo(p, gp.gids, flags,
- ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
+ return PackageParser.generatePackageInfo(p, gids, flags,
+ ps.firstInstallTime, ps.lastUpdateTime, permissions,
state, userId);
}
@@ -1986,6 +2030,7 @@
public int getPackageUid(String packageName, int userId) {
if (!sUserManager.exists(userId)) return -1;
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package uid");
+
// reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -2002,22 +2047,30 @@
}
@Override
- public int[] getPackageGids(String packageName) {
+ public int[] getPackageGids(String packageName, int userId) throws RemoteException {
+ if (!sUserManager.exists(userId)) {
+ return null;
+ }
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
+ "getPackageGids");
+
// reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
- if (DEBUG_PACKAGE_INFO)
+ if (DEBUG_PACKAGE_INFO) {
Log.v(TAG, "getPackageGids" + packageName + ": " + p);
+ }
if (p != null) {
- final PackageSetting ps = (PackageSetting)p.mExtras;
- return ps.getGids();
+ PackageSetting ps = (PackageSetting) p.mExtras;
+ return ps.getPermissionsState().computeGids(userId);
}
}
- // stupid thing to indicate an error.
- return new int[0];
+
+ return null;
}
- static final PermissionInfo generatePermissionInfo(
+ static PermissionInfo generatePermissionInfo(
BasePermission bp, int flags) {
if (bp.perm != null) {
return PackageParser.generatePermissionInfo(bp.perm, flags);
@@ -2381,30 +2434,37 @@
}
@Override
- public int checkPermission(String permName, String pkgName) {
+ public int checkPermission(String permName, String pkgName, int userId) {
+ if (!sUserManager.exists(userId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+
synchronized (mPackages) {
- PackageParser.Package p = mPackages.get(pkgName);
+ final PackageParser.Package p = mPackages.get(pkgName);
if (p != null && p.mExtras != null) {
- PackageSetting ps = (PackageSetting)p.mExtras;
- if (ps.sharedUser != null) {
- if (ps.sharedUser.grantedPermissions.contains(permName)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- } else if (ps.grantedPermissions.contains(permName)) {
+ final PackageSetting ps = (PackageSetting) p.mExtras;
+ if (ps.getPermissionsState().hasPermission(permName, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
+
return PackageManager.PERMISSION_DENIED;
}
@Override
public int checkUidPermission(String permName, int uid) {
+ final int userId = UserHandle.getUserId(uid);
+
+ if (!sUserManager.exists(userId)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj != null) {
- GrantedPermissions gp = (GrantedPermissions)obj;
- if (gp.grantedPermissions.contains(permName)) {
+ final SettingBase ps = (SettingBase) obj;
+ if (ps.getPermissionsState().hasPermission(permName, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
@@ -2414,6 +2474,7 @@
}
}
}
+
return PackageManager.PERMISSION_DENIED;
}
@@ -2620,120 +2681,114 @@
}
}
- private static void checkGrantRevokePermissions(PackageParser.Package pkg, BasePermission bp) {
+ private static void enforceDeclaredAsUsedAndRuntimePermission(PackageParser.Package pkg,
+ BasePermission bp) {
int index = pkg.requestedPermissions.indexOf(bp.name);
if (index == -1) {
throw new SecurityException("Package " + pkg.packageName
+ " has not requested permission " + bp.name);
}
- boolean isNormal =
- ((bp.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE)
- == PermissionInfo.PROTECTION_NORMAL);
- boolean isDangerous =
- ((bp.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE)
- == PermissionInfo.PROTECTION_DANGEROUS);
- boolean isDevelopment =
- ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0);
-
- if (!isNormal && !isDangerous && !isDevelopment) {
+ if (!bp.isRuntime()) {
throw new SecurityException("Permission " + bp.name
+ " is not a changeable permission type");
}
-
- if (isNormal || isDangerous) {
- if (pkg.requestedPermissionsRequired.get(index)) {
- throw new SecurityException("Can't change " + bp.name
- + ". It is required by the application");
- }
- }
}
@Override
- public void grantPermission(String packageName, String permissionName) {
+ public boolean grantPermission(String packageName, String name, int userId) {
+ if (!sUserManager.exists(userId)) {
+ return false;
+ }
+
mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
+ android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+ "grantPermission");
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ "grantPermission");
+
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final BasePermission bp = mSettings.mPermissions.get(permissionName);
+
+ final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
- throw new IllegalArgumentException("Unknown permission: " + permissionName);
+ throw new IllegalArgumentException("Unknown permission: " + name);
}
- checkGrantRevokePermissions(pkg, bp);
+ enforceDeclaredAsUsedAndRuntimePermission(pkg, bp);
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null) {
- return;
+ final SettingBase sb = (SettingBase) pkg.mExtras;
+ if (sb == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps;
- if (gp.grantedPermissions.add(permissionName)) {
- if (ps.haveGids) {
- gp.gids = appendInts(gp.gids, bp.gids);
+
+ final PermissionsState permissionsState = sb.getPermissionsState();
+
+ final int result = permissionsState.grantRuntimePermission(bp, userId);
+ switch (result) {
+ case PermissionsState.PERMISSION_OPERATION_FAILURE: {
+ return false;
}
- mSettings.writeLPr();
+
+ case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
+ killSettingPackagesForUser(sb, userId, KILL_APP_REASON_GIDS_CHANGED);
+ } break;
}
+
+ // Not critical if that is lost - app has to request again.
+ mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+
+ return true;
}
}
@Override
- public void revokePermission(String packageName, String permissionName) {
- int changedAppId = -1;
+ public boolean revokePermission(String packageName, String name, int userId) {
+ if (!sUserManager.exists(userId)) {
+ return false;
+ }
+
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+ "revokePermission");
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+ "revokePermission");
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- if (pkg.applicationInfo.uid != Binder.getCallingUid()) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
- }
- final BasePermission bp = mSettings.mPermissions.get(permissionName);
+
+ final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
- throw new IllegalArgumentException("Unknown permission: " + permissionName);
+ throw new IllegalArgumentException("Unknown permission: " + name);
}
- checkGrantRevokePermissions(pkg, bp);
+ enforceDeclaredAsUsedAndRuntimePermission(pkg, bp);
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null) {
- return;
+ final SettingBase sb = (SettingBase) pkg.mExtras;
+ if (sb == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps;
- if (gp.grantedPermissions.remove(permissionName)) {
- gp.grantedPermissions.remove(permissionName);
- if (ps.haveGids) {
- gp.gids = removeInts(gp.gids, bp.gids);
- }
- mSettings.writeLPr();
- changedAppId = ps.appId;
- }
- }
- if (changedAppId >= 0) {
- // We changed the perm on someone, kill its processes.
- IActivityManager am = ActivityManagerNative.getDefault();
- if (am != null) {
- final int callingUserId = UserHandle.getCallingUserId();
- final long ident = Binder.clearCallingIdentity();
- try {
- //XXX we should only revoke for the calling user's app permissions,
- // but for now we impact all users.
- //am.killUid(UserHandle.getUid(callingUserId, changedAppId),
- // "revoke " + permissionName);
- int[] users = sUserManager.getUserIds();
- for (int user : users) {
- am.killUid(UserHandle.getUid(user, changedAppId),
- "revoke " + permissionName);
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ final PermissionsState permissionsState = sb.getPermissionsState();
+
+ if (permissionsState.revokeRuntimePermission(bp, userId) ==
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ return false;
}
+
+ killSettingPackagesForUser(sb, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+
+ // Critical, after this call all should never have the permission.
+ mSettings.writeRuntimePermissionsForUserLPr(userId, true);
+
+ return true;
}
}
@@ -2794,6 +2849,46 @@
}
}
+ private void killSettingPackagesForUser(SettingBase sb, int userId, String reason) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (sb instanceof SharedUserSetting) {
+ SharedUserSetting sus = (SharedUserSetting) sb;
+ final int packageCount = sus.packages.size();
+ for (int i = 0; i < packageCount; i++) {
+ PackageSetting susPs = sus.packages.valueAt(i);
+ if (userId == UserHandle.USER_ALL) {
+ killApplication(susPs.pkg.packageName, susPs.appId, reason);
+ } else {
+ final int uid = UserHandle.getUid(userId, susPs.appId);
+ killUid(uid, reason);
+ }
+ }
+ } else if (sb instanceof PackageSetting) {
+ PackageSetting ps = (PackageSetting) sb;
+ if (userId == UserHandle.USER_ALL) {
+ killApplication(ps.pkg.packageName, ps.appId, reason);
+ } else {
+ final int uid = UserHandle.getUid(userId, ps.appId);
+ killUid(uid, reason);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private static void killUid(int uid, String reason) {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ if (am != null) {
+ try {
+ am.killUid(uid, reason);
+ } catch (RemoteException e) {
+ /* ignore - same process */
+ }
+ }
+ }
+
/**
* Compares two sets of signatures. Returns:
* <br />
@@ -3875,9 +3970,10 @@
private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps,
String[] permissions, boolean[] tmp, int flags, int userId) {
int numMatch = 0;
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+ final PermissionsState permissionsState = ps.getPermissionsState();
for (int i=0; i<permissions.length; i++) {
- if (gp.grantedPermissions.contains(permissions[i])) {
+ final String permission = permissions[i];
+ if (permissionsState.hasPermission(permission, userId)) {
tmp[i] = true;
numMatch++;
} else {
@@ -6853,36 +6949,42 @@
private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
String packageOfInterest) {
+ // IMPORTANT: There are two types of permissions: install and runtime.
+ // Install time permissions are granted when the app is installed to
+ // all device users and users added in the future. Runtime permissions
+ // are granted at runtime explicitly to specific users. Normal and signature
+ // protected permissions are install time permissions. Dangerous permissions
+ // are install permissions if the app's target SDK is Lollipop MR1 or older,
+ // otherwise they are runtime permissions. This function does not manage
+ // runtime permissions except for the case an app targeting Lollipop MR1
+ // being upgraded to target a newer SDK, in which case dangerous permissions
+ // are transformed from install time to runtime ones.
+
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {
return;
}
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
- ArraySet<String> origPermissions = gp.grantedPermissions;
+
+ PermissionsState permissionsState = ps.getPermissionsState();
+ PermissionsState origPermissions = permissionsState;
+
boolean changedPermission = false;
if (replace) {
ps.permissionsFixed = false;
- if (gp == ps) {
- origPermissions = new ArraySet<String>(gp.grantedPermissions);
- gp.grantedPermissions.clear();
- gp.gids = mGlobalGids;
- }
+ origPermissions = new PermissionsState(permissionsState);
+ permissionsState.reset();
}
- if (gp.gids == null) {
- gp.gids = mGlobalGids;
- }
+ permissionsState.setGlobalGids(mGlobalGids);
final int N = pkg.requestedPermissions.size();
for (int i=0; i<N; i++) {
final String name = pkg.requestedPermissions.get(i);
- final boolean required = pkg.requestedPermissionsRequired.get(i);
final BasePermission bp = mSettings.mPermissions.get(name);
+
if (DEBUG_INSTALL) {
- if (gp != ps) {
- Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
- }
+ Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
}
if (bp == null || bp.packageSetting == null) {
@@ -6894,10 +6996,11 @@
}
final String perm = bp.name;
- boolean allowed;
boolean allowedSig = false;
- if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
- // Keep track of app op permissions.
+ int grant = GRANT_DENIED;
+
+ // Keep track of app op permissions.
+ if ((bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name);
if (pkgs == null) {
pkgs = new ArraySet<>();
@@ -6905,65 +7008,106 @@
}
pkgs.add(pkg.packageName);
}
+
final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
- if (level == PermissionInfo.PROTECTION_NORMAL
- || level == PermissionInfo.PROTECTION_DANGEROUS) {
- // We grant a normal or dangerous permission if any of the following
- // are true:
- // 1) The permission is required
- // 2) The permission is optional, but was granted in the past
- // 3) The permission is optional, but was requested by an
- // app in /system (not /data)
- //
- // Otherwise, reject the permission.
- allowed = (required || origPermissions.contains(perm)
- || (isSystemApp(ps) && !isUpdatedSystemApp(ps)));
- } else if (bp.packageSetting == null) {
- // This permission is invalid; skip it.
- allowed = false;
- } else if (level == PermissionInfo.PROTECTION_SIGNATURE) {
- allowed = grantSignaturePermission(perm, pkg, bp, origPermissions);
- if (allowed) {
- allowedSig = true;
- }
- } else {
- allowed = false;
+ switch (level) {
+ case PermissionInfo.PROTECTION_NORMAL: {
+ // For all apps normal permissions are install time ones.
+ grant = GRANT_INSTALL;
+ } break;
+
+ case PermissionInfo.PROTECTION_DANGEROUS: {
+ if (!RUNTIME_PERMISSIONS_ENABLED
+ || pkg.applicationInfo.targetSdkVersion
+ <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ // For legacy apps dangerous permissions are install time ones.
+ grant = GRANT_INSTALL;
+ } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // For modern system apps dangerous permissions are install time ones.
+ grant = GRANT_INSTALL;
+ } else {
+ if (origPermissions.hasInstallPermission(bp.name)) {
+ // For legacy apps that became modern, install becomes runtime.
+ grant = GRANT_UPGRADE;
+ } else if (replace) {
+ // For upgraded modern apps keep runtime permissions unchanged.
+ grant = GRANT_RUNTIME;
+ }
+ }
+ } break;
+
+ case PermissionInfo.PROTECTION_SIGNATURE: {
+ // For all apps signature permissions are install time ones.
+ allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
+ if (allowedSig) {
+ grant = GRANT_INSTALL;
+ }
+ } break;
}
+
if (DEBUG_INSTALL) {
- if (gp != ps) {
- Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
- }
+ Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
}
- if (allowed) {
+
+ if (grant != GRANT_DENIED) {
if (!isSystemApp(ps) && ps.permissionsFixed) {
// If this is an existing, non-system package, then
// we can't add any new permissions to it.
- if (!allowedSig && !gp.grantedPermissions.contains(perm)) {
+ if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
// Except... if this is a permission that was added
// to the platform (note: need to only do this when
// updating the platform).
- allowed = isNewPlatformPermissionForPackage(perm, pkg);
+ if (!isNewPlatformPermissionForPackage(perm, pkg)) {
+ grant = GRANT_DENIED;
+ }
}
}
- if (allowed) {
- if (!gp.grantedPermissions.contains(perm)) {
- changedPermission = true;
- gp.grantedPermissions.add(perm);
- gp.gids = appendInts(gp.gids, bp.gids);
- } else if (!ps.haveGids) {
- gp.gids = appendInts(gp.gids, bp.gids);
- }
- } else {
- if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
- Slog.w(TAG, "Not granting permission " + perm
- + " to package " + pkg.packageName
- + " because it was previously installed without");
- }
+
+ switch (grant) {
+ case GRANT_INSTALL: {
+ // Grant an install permission.
+ if (permissionsState.grantInstallPermission(bp) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ changedPermission = true;
+ }
+ } break;
+
+ case GRANT_RUNTIME: {
+ // Grant previously granted runtime permissions.
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ if (origPermissions.hasRuntimePermission(bp.name, userId)) {
+ if (permissionsState.grantRuntimePermission(bp, userId) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ changedPermission = true;
+ }
+ }
+ }
+ } break;
+
+ case GRANT_UPGRADE: {
+ // Grant runtime permissions for a previously held install permission.
+ permissionsState.revokeInstallPermission(bp);
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ if (permissionsState.grantRuntimePermission(bp, userId) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ changedPermission = true;
+ }
+ }
+ } break;
+
+ default: {
+ if (packageOfInterest == null
+ || packageOfInterest.equals(pkg.packageName)) {
+ Slog.w(TAG, "Not granting permission " + perm
+ + " to package " + pkg.packageName
+ + " because it was previously installed without");
+ }
+ } break;
}
} else {
- if (gp.grantedPermissions.remove(perm)) {
+ if (permissionsState.revokeInstallPermission(bp) !=
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
changedPermission = true;
- gp.gids = removeInts(gp.gids, bp.gids);
Slog.i(TAG, "Un-granting permission " + perm
+ " from package " + pkg.packageName
+ " (protectionLevel=" + bp.protectionLevel
@@ -6990,7 +7134,6 @@
// changed.
ps.permissionsFixed = true;
}
- ps.haveGids = true;
}
private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
@@ -7011,7 +7154,7 @@
}
private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
- BasePermission bp, ArraySet<String> origPermissions) {
+ BasePermission bp, PermissionsState origPermissions) {
boolean allowed;
allowed = (compareSignatures(
bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
@@ -7026,10 +7169,7 @@
if (isUpdatedSystemApp(pkg)) {
final PackageSetting sysPs = mSettings
.getDisabledSystemPkgLPr(pkg.packageName);
- final GrantedPermissions origGp = sysPs.sharedUser != null
- ? sysPs.sharedUser : sysPs;
-
- if (origGp.grantedPermissions.contains(perm)) {
+ if (sysPs.getPermissionsState().hasInstallPermission(perm)) {
// If the original was granted this permission, we take
// that grant decision as read and propagate it to the
// update.
@@ -7063,7 +7203,7 @@
& PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
// For development permissions, a development permission
// is granted only if it was already granted.
- allowed = origPermissions.contains(perm);
+ allowed = origPermissions.hasInstallPermission(perm);
}
return allowed;
}
@@ -10821,11 +10961,26 @@
mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
outInfo.removedAppId = mSettings.removePackageLPw(packageName);
}
- if (deletedPs != null) {
- updatePermissionsLPw(deletedPs.name, null, 0);
- if (deletedPs.sharedUser != null) {
- // remove permissions associated with package
- mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);
+ updatePermissionsLPw(deletedPs.name, null, 0);
+ if (deletedPs.sharedUser != null) {
+ // Remove permissions associated with package. Since runtime
+ // permissions are per user we have to kill the removed package
+ // or packages running under the shared user of the removed
+ // package if revoking the permissions requested only by the removed
+ // package is successful and this causes a change in gids.
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,
+ userId);
+ if (userIdToKill == userId) {
+ // If gids changed for this user, kill all affected packages.
+ killSettingPackagesForUser(deletedPs, userIdToKill,
+ KILL_APP_REASON_GIDS_CHANGED);
+ } else if (userIdToKill == UserHandle.USER_ALL) {
+ // If gids changed for all users, kill them all - done.
+ killSettingPackagesForUser(deletedPs, userIdToKill,
+ KILL_APP_REASON_GIDS_CHANGED);
+ break;
+ }
}
}
clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 06d842a..889164c 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -57,8 +57,10 @@
+ " " + name + "/" + appId + "}";
}
- public int[] getGids() {
- return sharedUser != null ? sharedUser.gids : gids;
+ public PermissionsState getPermissionsState() {
+ return (sharedUser != null)
+ ? sharedUser.getPermissionsState()
+ : super.getPermissionsState();
}
public boolean isPrivileged() {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 4b8ca42..9e8b3df 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -29,7 +29,7 @@
/**
* Settings base class for pending and resolved classes.
*/
-class PackageSettingBase extends GrantedPermissions {
+abstract class PackageSettingBase extends SettingBase {
/**
* Indicates the state of installation. Used by PackageManager to figure out
* incomplete installations. Say a package is being installed (the state is
@@ -93,7 +93,6 @@
PackageSignatures signatures = new PackageSignatures();
boolean permissionsFixed;
- boolean haveGids;
PackageKeySetData keySetData = new PackageKeySetData();
@@ -147,7 +146,6 @@
signatures = new PackageSignatures(base.signatures);
permissionsFixed = base.permissionsFixed;
- haveGids = base.haveGids;
userState.clear();
for (int i=0; i<base.userState.size(); i++) {
userState.put(base.userState.keyAt(i),
@@ -160,7 +158,6 @@
installerPackageName = base.installerPackageName;
keySetData = new PackageKeySetData(base.keySetData);
-
}
void init(File codePath, File resourcePath, String legacyNativeLibraryPathString,
@@ -201,9 +198,7 @@
* Make a shallow copy of this package settings.
*/
public void copyFrom(PackageSettingBase base) {
- grantedPermissions = base.grantedPermissions;
- gids = base.gids;
-
+ getPermissionsState().copyFrom(base.getPermissionsState());
primaryCpuAbiString = base.primaryCpuAbiString;
secondaryCpuAbiString = base.secondaryCpuAbiString;
cpuAbiOverrideString = base.cpuAbiOverrideString;
@@ -212,7 +207,6 @@
lastUpdateTime = base.lastUpdateTime;
signatures = base.signatures;
permissionsFixed = base.permissionsFixed;
- haveGids = base.haveGids;
userState.clear();
for (int i=0; i<base.userState.size(); i++) {
userState.put(base.userState.keyAt(i), base.userState.valueAt(i));
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
new file mode 100644
index 0000000..3e0e342
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * This class encapsulates the permissions for a package or a shared user.
+ * <p>
+ * There are two types of permissions: install (granted at installation)
+ * and runtime (granted at runtime). Install permissions are granted to
+ * all device users while runtime permissions are granted explicitly to
+ * specific users.
+ * </p>
+ * <p>
+ * The permissions are kept on a per device user basis. For example, an
+ * application may have some runtime permissions granted under the device
+ * owner but not granted under the secondary user.
+ * <p>
+ * This class is also responsible for keeping track of the Linux gids per
+ * user for a package or a shared user. The gids are computed as a set of
+ * the gids for all granted permissions' gids on a per user basis.
+ * </p>
+ */
+public final class PermissionsState {
+
+ /** The permission operation succeeded and no gids changed. */
+ public static final int PERMISSION_OPERATION_SUCCESS = 1;
+
+ /** The permission operation succeeded and gids changed. */
+ public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 2;
+
+ /** The permission operation failed. */
+ public static final int PERMISSION_OPERATION_FAILURE = 3;
+
+ private static final int[] USERS_ALL = {UserHandle.USER_ALL};
+
+ private static final int[] USERS_NONE = {};
+
+ private static final int[] NO_GIDS = {};
+
+ private ArrayMap<String, PermissionData> mPermissions;
+
+ private int[] mGlobalGids = NO_GIDS;
+
+ public PermissionsState() {
+ /* do nothing */
+ }
+
+ public PermissionsState(PermissionsState prototype) {
+ copyFrom(prototype);
+ }
+
+ /**
+ * Sets the global gids, applicable to all users.
+ *
+ * @param globalGids The global gids.
+ */
+ public void setGlobalGids(int[] globalGids) {
+ if (!ArrayUtils.isEmpty(globalGids)) {
+ mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
+ }
+ }
+
+ /**
+ * Initialized this instance from another one.
+ *
+ * @param other The other instance.
+ */
+ public void copyFrom(PermissionsState other) {
+ if (mPermissions != null) {
+ if (other.mPermissions == null) {
+ mPermissions = null;
+ } else {
+ mPermissions.clear();
+ }
+ }
+ if (other.mPermissions != null) {
+ if (mPermissions == null) {
+ mPermissions = new ArrayMap<>();
+ }
+ final int permissionCount = other.mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String name = other.mPermissions.keyAt(i);
+ PermissionData permissionData = other.mPermissions.valueAt(i);
+ mPermissions.put(name, new PermissionData(permissionData));
+ }
+ }
+
+ mGlobalGids = NO_GIDS;
+ if (other.mGlobalGids != NO_GIDS) {
+ mGlobalGids = Arrays.copyOf(other.mGlobalGids,
+ other.mGlobalGids.length);
+ }
+ }
+
+ /**
+ * Grant an install permission.
+ *
+ * @param permission The permission to grant.
+ * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
+ */
+ public int grantInstallPermission(BasePermission permission) {
+ return grantPermission(permission, UserHandle.USER_ALL);
+ }
+
+ /**
+ * Revoke an install permission.
+ *
+ * @param permission The permission to revoke.
+ * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
+ */
+ public int revokeInstallPermission(BasePermission permission) {
+ return revokePermission(permission, UserHandle.USER_ALL);
+ }
+
+ /**
+ * Grant a runtime permission.
+ *
+ * @param permission The permission to grant.
+ * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
+ */
+ public int grantRuntimePermission(BasePermission permission, int userId) {
+ return grantPermission(permission, userId);
+ }
+
+ /**
+ * Revoke a runtime permission for a given device user.
+ *
+ * @param permission The permission to revoke.
+ * @param userId The device user id.
+ * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
+ */
+ public int revokeRuntimePermission(BasePermission permission, int userId) {
+ return revokePermission(permission, userId);
+ }
+
+ /**
+ * Gets whether this state has a given permission, regardless if
+ * it is install time or runtime one.
+ *
+ * @param name The permission name.
+ * @return Whether this state has the permission.
+ */
+ public boolean hasPermission(String name) {
+ return mPermissions != null && mPermissions.get(name) != null;
+ }
+
+ /**
+ * Gets whether this state has a given runtime permission for a
+ * given device user id.
+ *
+ * @param name The permission name.
+ * @param userId The device user id.
+ * @return Whether this state has the permission.
+ */
+ public boolean hasRuntimePermission(String name, int userId) {
+ return !hasInstallPermission(name) && hasPermission(name, userId);
+ }
+
+ /**
+ * Gets whether this state has a given install permission.
+ *
+ * @param name The permission name.
+ * @return Whether this state has the permission.
+ */
+ public boolean hasInstallPermission(String name) {
+ return hasPermission(name, UserHandle.USER_ALL);
+ }
+
+ /**
+ * Revokes a permission for all users regardless if it is an install or
+ * a runtime permission.
+ *
+ * @param permission The permission to revoke.
+ * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
+ */
+ public int revokePermission(BasePermission permission) {
+ if (!hasPermission(permission.name)) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ int result = PERMISSION_OPERATION_SUCCESS;
+
+ PermissionData permissionData = mPermissions.get(permission.name);
+ if (permissionData.hasGids()) {
+ for (int userId : permissionData.getUserIds()) {
+ if (revokePermission(permission, userId)
+ == PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+ result = PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+ break;
+ }
+ }
+ }
+
+ mPermissions.remove(permission.name);
+
+ return result;
+ }
+
+ /**
+ * Gets whether the state has a given permission for the specified
+ * user, regardless if this is an install or a runtime permission.
+ *
+ * @param name The permission name.
+ * @param userId The device user id.
+ * @return Whether the user has the permission.
+ */
+ public boolean hasPermission(String name, int userId) {
+ enforceValidUserId(userId);
+
+ if (mPermissions == null) {
+ return false;
+ }
+
+ PermissionData permissionData = mPermissions.get(name);
+ return permissionData != null && permissionData.hasUserId(userId);
+ }
+
+ /**
+ * Gets all permissions regardless if they are install or runtime.
+ *
+ * @return The permissions or an empty set.
+ */
+ public Set<String> getPermissions() {
+ if (mPermissions != null) {
+ return mPermissions.keySet();
+ }
+
+ return Collections.emptySet();
+ }
+
+ /**
+ * Gets all permissions for a given device user id regardless if they
+ * are install time or runtime permissions.
+ *
+ * @param userId The device user id.
+ * @return The permissions or an empty set.
+ */
+ public Set<String> getPermissions(int userId) {
+ enforceValidUserId(userId);
+
+ if (mPermissions == null) {
+ return Collections.emptySet();
+ }
+
+ Set<String> permissions = new ArraySet<>();
+
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String permission = mPermissions.keyAt(i);
+ if (userId == UserHandle.USER_ALL) {
+ if (hasInstallPermission(permission)) {
+ permissions.add(permission);
+ }
+ } else {
+ if (hasRuntimePermission(permission, userId)) {
+ permissions.add(permission);
+ }
+ }
+ }
+
+ return permissions;
+ }
+
+ /**
+ * Gets all runtime permissions.
+ *
+ * @return The permissions or an empty set.
+ */
+ public Set<String> getRuntimePermissions(int userId) {
+ return getPermissions(userId);
+ }
+
+ /**
+ * Gets all install permissions.
+ *
+ * @return The permissions or an empty set.
+ */
+ public Set<String> getInstallPermissions() {
+ return getPermissions(UserHandle.USER_ALL);
+ }
+
+ /**
+ * Compute the Linux gids for a given device user from the permissions
+ * granted to this user. Note that these are computed to avoid additional
+ * state as they are rarely accessed.
+ *
+ * @param userId The device user id.
+ * @return The gids for the device user.
+ */
+ public int[] computeGids(int userId) {
+ enforceValidUserId(userId);
+
+ int[] gids = mGlobalGids;
+
+ if (mPermissions != null) {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String permission = mPermissions.keyAt(i);
+ if (!hasPermission(permission, userId)) {
+ continue;
+ }
+ PermissionData permissionData = mPermissions.valueAt(i);
+ final int[] permGids = permissionData.computeGids(userId);
+ if (permGids != NO_GIDS) {
+ gids = appendInts(gids, permGids);
+ }
+ }
+ }
+
+ return gids;
+ }
+
+ /**
+ * Compute the Linux gids for all device users from the permissions
+ * granted to these users.
+ *
+ * @return The gids for all device users.
+ */
+ public int[] computeGids() {
+ int[] gids = mGlobalGids;
+
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ final int[] userGids = computeGids(userId);
+ gids = appendInts(gids, userGids);
+ }
+
+ return gids;
+ }
+
+ /**
+ * Resets the internal state of this object.
+ */
+ public void reset() {
+ mGlobalGids = NO_GIDS;
+ mPermissions = null;
+ }
+
+ private int grantPermission(BasePermission permission, int userId) {
+ if (hasPermission(permission.name, userId)) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ final boolean hasGids = permission.hasGids();
+ final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
+
+ if (mPermissions == null) {
+ mPermissions = new ArrayMap<>();
+ }
+
+ PermissionData permissionData = mPermissions.get(permission.name);
+ if (permissionData == null) {
+ permissionData = new PermissionData(permission);
+ mPermissions.put(permission.name, permissionData);
+ }
+
+ if (!permissionData.addUserId(userId)) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ if (hasGids) {
+ final int[] newGids = computeGids(userId);
+ if (oldGids.length != newGids.length) {
+ return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+ }
+ }
+
+ return PERMISSION_OPERATION_SUCCESS;
+ }
+
+ private int revokePermission(BasePermission permission, int userId) {
+ if (!hasPermission(permission.name, userId)) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ final boolean hasGids = permission.hasGids();
+ final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
+
+ PermissionData permissionData = mPermissions.get(permission.name);
+
+ if (!permissionData.removeUserId(userId)) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ if (permissionData.getUserIds() == USERS_NONE) {
+ mPermissions.remove(permission.name);
+ }
+
+ if (mPermissions.isEmpty()) {
+ mPermissions = null;
+ }
+
+ if (hasGids) {
+ final int[] newGids = computeGids(userId);
+ if (oldGids.length != newGids.length) {
+ return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+ }
+ }
+
+ return PERMISSION_OPERATION_SUCCESS;
+ }
+
+ private static int[] appendInts(int[] current, int[] added) {
+ if (current != null && added != null) {
+ for (int guid : added) {
+ current = ArrayUtils.appendInt(current, guid);
+ }
+ }
+ return current;
+ }
+
+ private static void enforceValidUserId(int userId) {
+ if (userId != UserHandle.USER_ALL && userId < 0) {
+ throw new IllegalArgumentException("Invalid userId:" + userId);
+ }
+ }
+
+ private static final class PermissionData {
+ private final BasePermission mPerm;
+ private int[] mUserIds = USERS_NONE;
+
+ public PermissionData(BasePermission perm) {
+ mPerm = perm;
+ }
+
+ public PermissionData(PermissionData other) {
+ this(other.mPerm);
+
+ if (other.mUserIds == USERS_ALL || other.mUserIds == USERS_NONE) {
+ mUserIds = other.mUserIds;
+ } else {
+ mUserIds = Arrays.copyOf(other.mUserIds, other.mUserIds.length);
+ }
+ }
+
+ public boolean hasGids() {
+ return mPerm.hasGids();
+ }
+
+ public int[] computeGids(int userId) {
+ return mPerm.computeGids(userId);
+ }
+
+ public int[] getUserIds() {
+ return mUserIds;
+ }
+
+ public boolean hasUserId(int userId) {
+ if (mUserIds == USERS_ALL) {
+ return true;
+ }
+
+ if (userId != UserHandle.USER_ALL) {
+ return ArrayUtils.contains(mUserIds, userId);
+ }
+
+ return false;
+ }
+
+ public boolean addUserId(int userId) {
+ if (hasUserId(userId)) {
+ return false;
+ }
+
+ if (userId == UserHandle.USER_ALL) {
+ mUserIds = USERS_ALL;
+ return true;
+ }
+
+ mUserIds = ArrayUtils.appendInt(mUserIds, userId);
+
+ return true;
+ }
+
+ public boolean removeUserId(int userId) {
+ if (!hasUserId(userId)) {
+ return false;
+ }
+
+ if (mUserIds == USERS_ALL) {
+ mUserIds = UserManagerService.getInstance().getUserIds();
+ }
+
+ mUserIds = ArrayUtils.removeInt(mUserIds, userId);
+
+ if (mUserIds.length == 0) {
+ mUserIds = USERS_NONE;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/GrantedPermissions.java b/services/core/java/com/android/server/pm/SettingBase.java
similarity index 74%
rename from services/core/java/com/android/server/pm/GrantedPermissions.java
rename to services/core/java/com/android/server/pm/SettingBase.java
index e87546c..d350c09 100644
--- a/services/core/java/com/android/server/pm/GrantedPermissions.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -19,27 +19,26 @@
import android.content.pm.ApplicationInfo;
import android.util.ArraySet;
-class GrantedPermissions {
+abstract class SettingBase {
int pkgFlags;
int pkgPrivateFlags;
- ArraySet<String> grantedPermissions = new ArraySet<String>();
+ private final PermissionsState mPermissionsState;
- int[] gids;
-
- GrantedPermissions(int pkgFlags, int pkgPrivateFlags) {
+ SettingBase(int pkgFlags, int pkgPrivateFlags) {
setFlags(pkgFlags);
setPrivateFlags(pkgPrivateFlags);
+ mPermissionsState = new PermissionsState();
}
- @SuppressWarnings("unchecked")
- GrantedPermissions(GrantedPermissions base) {
+ SettingBase(SettingBase base) {
pkgFlags = base.pkgFlags;
- grantedPermissions = new ArraySet<>(base.grantedPermissions);
+ pkgPrivateFlags = base.pkgPrivateFlags;
+ mPermissionsState = new PermissionsState(base.mPermissionsState);
+ }
- if (base.gids != null) {
- gids = base.gids.clone();
- }
+ public PermissionsState getPermissionsState() {
+ return mPermissionsState;
}
void setFlags(int pkgFlags) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b820d7e8..82aa74a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -25,6 +25,7 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.PACKAGE_INFO_GID;
+import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
@@ -33,17 +34,27 @@
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Message;
import android.os.PatternMatcher;
import android.os.Process;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.AtomicFile;
import android.util.LogPrinter;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.PackageManagerService.DumpState;
+import java.io.FileNotFoundException;
import java.util.Collection;
import org.xmlpull.v1.XmlPullParser;
@@ -51,7 +62,6 @@
import org.xmlpull.v1.XmlSerializer;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
@@ -134,6 +144,8 @@
private static final boolean DEBUG_STOPPED = false;
private static final boolean DEBUG_MU = false;
+ private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml";
+
private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage";
private static final String ATTR_ENFORCEMENT = "enforcement";
@@ -142,6 +154,9 @@
private static final String TAG_ENABLED_COMPONENTS = "enabled-components";
private static final String TAG_PACKAGE_RESTRICTIONS = "package-restrictions";
private static final String TAG_PACKAGE = "pkg";
+ private static final String TAG_SHARED_USER = "shared-user";
+ private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions";
+ private static final String TAG_PERMISSIONS = "perms";
private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES =
"persistent-preferred-activities";
static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
@@ -161,6 +176,11 @@
private static final String ATTR_INSTALLED = "inst";
private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
+ private final Object mLock;
+ private final Context mContext;
+
+ private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
+
private final File mSettingsFilename;
private final File mBackupSettingsFilename;
private final File mPackageListFilename;
@@ -257,11 +277,16 @@
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
- Settings(Context context) {
- this(context, Environment.getDataDirectory());
+ Settings(Context context, Object lock) {
+ this(context, Environment.getDataDirectory(), lock);
}
- Settings(Context context, File dataDir) {
+ Settings(Context context, File dataDir, Object lock) {
+ mContext = context;
+ mLock = lock;
+
+ mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
+
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
@@ -460,7 +485,7 @@
bp.pendingInfo.packageName = newPkg;
}
bp.uid = 0;
- bp.gids = null;
+ bp.setGids(null, false);
}
}
}
@@ -468,9 +493,9 @@
private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString,
- int vc, int pkgFlags, int pkgPrivateFlags, UserHandle installUser, boolean add,
- boolean allowInstall) {
+ String legacyNativeLibraryPathString, String primaryCpuAbiString,
+ String secondaryCpuAbiString, int vc, int pkgFlags, int pkgPrivateFlags,
+ UserHandle installUser, boolean add, boolean allowInstall) {
PackageSetting p = mPackages.get(name);
UserManagerService userManager = UserManagerService.getInstance();
if (p != null) {
@@ -589,7 +614,7 @@
}
p.appId = dis.appId;
// Clone permissions
- p.grantedPermissions = new ArraySet<String>(dis.grantedPermissions);
+ p.getPermissionsState().copyFrom(dis.getPermissionsState());
// Clone component info
List<UserInfo> users = getAllUsers();
if (users != null) {
@@ -732,45 +757,60 @@
* not in use by other permissions of packages in the
* shared user setting.
*/
- void updateSharedUserPermsLPw(PackageSetting deletedPs, int[] globalGids) {
+ int updateSharedUserPermsLPw(PackageSetting deletedPs, int userId) {
if ((deletedPs == null) || (deletedPs.pkg == null)) {
Slog.i(PackageManagerService.TAG,
"Trying to update info for null package. Just ignoring");
- return;
+ return UserHandle.USER_NULL;
}
+
// No sharedUserId
if (deletedPs.sharedUser == null) {
- return;
+ return UserHandle.USER_NULL;
}
+
SharedUserSetting sus = deletedPs.sharedUser;
+
// Update permissions
for (String eachPerm : deletedPs.pkg.requestedPermissions) {
- boolean used = false;
- if (!sus.grantedPermissions.contains(eachPerm)) {
+ BasePermission bp = mPermissions.get(eachPerm);
+ if (bp == null) {
continue;
}
- for (PackageSetting pkg:sus.packages) {
- if (pkg.pkg != null &&
- !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&
- pkg.pkg.requestedPermissions.contains(eachPerm)) {
+
+ // If no user has the permission, nothing to remove.
+ if (!sus.getPermissionsState().hasPermission(bp.name, userId)) {
+ continue;
+ }
+
+ boolean used = false;
+
+ // Check if another package in the shared user needs the permission.
+ for (PackageSetting pkg : sus.packages) {
+ if (pkg.pkg != null
+ && !pkg.pkg.packageName.equals(deletedPs.pkg.packageName)
+ && pkg.pkg.requestedPermissions.contains(eachPerm)) {
used = true;
break;
}
}
+
if (!used) {
- // can safely delete this permission from list
- sus.grantedPermissions.remove(eachPerm);
+ // Try to revoke as an install permission which is for all users.
+ if (sus.getPermissionsState().revokeInstallPermission(bp) ==
+ PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+ return UserHandle.USER_ALL;
+ }
+
+ // Try to revoke as an install permission which is per user.
+ if (sus.getPermissionsState().revokeRuntimePermission(bp, userId) ==
+ PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+ return userId;
+ }
}
}
- // Update gids
- int newGids[] = globalGids;
- for (String eachPerm : sus.grantedPermissions) {
- BasePermission bp = mPermissions.get(eachPerm);
- if (bp != null) {
- newGids = PackageManagerService.appendInts(newGids, bp.gids);
- }
- }
- sus.gids = newGids;
+
+ return UserHandle.USER_NULL;
}
int removePackageLPw(String name) {
@@ -895,7 +935,17 @@
}
private File getUserPackagesStateFile(int userId) {
- return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml");
+ // TODO: Implement a cleaner solution when adding tests.
+ // This instead of Environment.getUserSystemDirectory(userId) to support testing.
+ File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId));
+ return new File(userDir, "package-restrictions.xml");
+ }
+
+ private File getUserRuntimePermissionsFile(int userId) {
+ // TODO: Implement a cleaner solution when adding tests.
+ // This instead of Environment.getUserSystemDirectory(userId) to support testing.
+ File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId));
+ return new File(userDir, RUNTIME_PERMISSIONS_FILE_NAME);
}
private File getUserPackagesStateBackupFile(int userId) {
@@ -912,15 +962,9 @@
}
}
- void readAllUsersPackageRestrictionsLPr() {
- List<UserInfo> users = getAllUsers();
- if (users == null) {
- readPackageRestrictionsLPr(0);
- return;
- }
-
- for (UserInfo user : users) {
- readPackageRestrictionsLPr(user.id);
+ void writeAllRuntimePermissionsLPr() {
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
}
}
@@ -1360,6 +1404,7 @@
}
serializer.endTag(null, TAG_DISABLED_COMPONENTS);
}
+
serializer.endTag(null, TAG_PACKAGE);
}
}
@@ -1403,6 +1448,58 @@
}
}
+ void readInstallPermissionsLPr(XmlPullParser parser,
+ PermissionsState permissionsState) throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_ITEM)) {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+
+ BasePermission bp = mPermissions.get(name);
+ if (bp == null) {
+ Slog.w(PackageManagerService.TAG, "Unknown permission: " + name);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
+ if (permissionsState.grantInstallPermission(bp) ==
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ } else {
+ Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ void writePermissionsLPr(XmlSerializer serializer, Set<String> permissions)
+ throws IOException {
+ if (permissions.isEmpty()) {
+ return;
+ }
+
+ serializer.startTag(null, TAG_PERMISSIONS);
+
+ for (String permission : permissions) {
+ serializer.startTag(null, TAG_ITEM);
+ serializer.attribute(null, ATTR_NAME, permission);
+ serializer.endTag(null, TAG_ITEM);
+ }
+
+ serializer.endTag(null, TAG_PERMISSIONS);
+ }
+
// Note: assumed "stopped" field is already cleared in all packages.
// Legacy reader, used to read in the old file format after an upgrade. Not used after that.
void readStoppedLPw() {
@@ -1594,13 +1691,7 @@
serializer.attribute(null, "userId",
Integer.toString(usr.userId));
usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
- serializer.startTag(null, "perms");
- for (String name : usr.grantedPermissions) {
- serializer.startTag(null, TAG_ITEM);
- serializer.attribute(null, ATTR_NAME, name);
- serializer.endTag(null, TAG_ITEM);
- }
- serializer.endTag(null, "perms");
+ writePermissionsLPr(serializer, usr.getPermissionsState().getInstallPermissions());
serializer.endTag(null, "shared-user");
}
@@ -1614,7 +1705,7 @@
serializer.endTag(null, "cleaning-package");
}
}
-
+
if (mRenamedPackages.size() > 0) {
for (Map.Entry<String, String> e : mRenamedPackages.entrySet()) {
serializer.startTag(null, "renamed-package");
@@ -1623,7 +1714,7 @@
serializer.endTag(null, "renamed-package");
}
}
-
+
mKeySetManagerService.writeKeySetManagerServiceLPr(serializer);
serializer.endTag(null, "packages");
@@ -1662,7 +1753,7 @@
final ApplicationInfo ai = pkg.pkg.applicationInfo;
final String dataPath = ai.dataDir;
final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- final int[] gids = pkg.getGids();
+ final int[] gids = pkg.getPermissionsState().computeGids();
// Avoid any application that has a space in its path.
if (dataPath.indexOf(" ") >= 0)
@@ -1718,6 +1809,8 @@
}
writeAllUsersPackageRestrictionsLPr();
+
+ writeAllRuntimePermissionsLPr();
return;
} catch(XmlPullParserException e) {
@@ -1770,26 +1863,12 @@
} else {
serializer.attribute(null, "sharedUserId", Integer.toString(pkg.appId));
}
- serializer.startTag(null, "perms");
+
+ // If this is a shared user, the permissions will be written there.
if (pkg.sharedUser == null) {
- // If this is a shared user, the permissions will
- // be written there. We still need to write an
- // empty permissions list so permissionsFixed will
- // be set.
- for (final String name : pkg.grantedPermissions) {
- BasePermission bp = mPermissions.get(name);
- if (bp != null) {
- // We only need to write signature or system permissions but
- // this wont
- // match the semantics of grantedPermissions. So write all
- // permissions.
- serializer.startTag(null, TAG_ITEM);
- serializer.attribute(null, ATTR_NAME, name);
- serializer.endTag(null, TAG_ITEM);
- }
- }
+ writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
}
- serializer.endTag(null, "perms");
+
serializer.endTag(null, "updated-package");
}
@@ -1840,19 +1919,7 @@
}
pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- serializer.startTag(null, "perms");
- if (pkg.sharedUser == null) {
- // If this is a shared user, the permissions will
- // be written there. We still need to write an
- // empty permissions list so permissionsFixed will
- // be set.
- for (final String name : pkg.grantedPermissions) {
- serializer.startTag(null, TAG_ITEM);
- serializer.attribute(null, ATTR_NAME, name);
- serializer.endTag(null, TAG_ITEM);
- }
- }
- serializer.endTag(null, "perms");
+ writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
}
writeSigningKeySetsLPr(serializer, pkg.keySetData);
@@ -2161,9 +2228,11 @@
} else {
if (users == null) {
readPackageRestrictionsLPr(0);
+ mRuntimePermissionsPersistence.readStateForUserSyncLPr(UserHandle.USER_OWNER);
} else {
for (UserInfo user : users) {
readPackageRestrictionsLPr(user.id);
+ mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
}
}
}
@@ -2537,7 +2606,7 @@
final String ptype = parser.getAttributeValue(null, "type");
if (name != null && sourcePackage != null) {
final boolean dynamic = "dynamic".equals(ptype);
- final BasePermission bp = new BasePermission(name, sourcePackage,
+ final BasePermission bp = new BasePermission(name.intern(), sourcePackage,
dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
bp.protectionLevel = readInt(parser, null, "protection",
PermissionInfo.PROTECTION_NORMAL);
@@ -2643,6 +2712,7 @@
String sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
ps.appId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
}
+
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2651,9 +2721,8 @@
continue;
}
- String tagName = parser.getName();
- if (tagName.equals("perms")) {
- readGrantedPermissionsLPw(parser, ps.grantedPermissions);
+ if (parser.getName().equals(TAG_PERMISSIONS)) {
+ readInstallPermissionsLPr(parser, ps.getPermissionsState());
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <updated-package>: " + parser.getName());
@@ -2711,7 +2780,7 @@
if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
primaryCpuAbiString = legacyCpuAbiString;
}
-;
+
version = parser.getAttributeValue(null, "version");
if (version != null) {
try {
@@ -2902,7 +2971,6 @@
packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_COMPLETE;
}
}
-
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2919,8 +2987,9 @@
readEnabledComponentsLPw(packageSetting, parser, 0);
} else if (tagName.equals("sigs")) {
packageSetting.signatures.readXml(parser, mPastSignatures);
- } else if (tagName.equals("perms")) {
- readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions);
+ } else if (tagName.equals(TAG_PERMISSIONS)) {
+ readInstallPermissionsLPr(parser,
+ packageSetting.getPermissionsState());
packageSetting.permissionsFixed = true;
} else if (tagName.equals("proper-signing-keyset")) {
long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
@@ -3039,7 +3108,6 @@
"Error in package manager settings: package " + name + " has bad userId "
+ idStr + " at " + parser.getPositionDescription());
}
- ;
if (su != null) {
int outerDepth = parser.getDepth();
@@ -3054,47 +3122,18 @@
if (tagName.equals("sigs")) {
su.signatures.readXml(parser, mPastSignatures);
} else if (tagName.equals("perms")) {
- readGrantedPermissionsLPw(parser, su.grantedPermissions);
+ readInstallPermissionsLPr(parser, su.getPermissionsState());
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <shared-user>: " + parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
-
} else {
XmlUtils.skipCurrentTag(parser);
}
}
- private void readGrantedPermissionsLPw(XmlPullParser parser, ArraySet<String> outPerms)
- throws IOException, XmlPullParserException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals(TAG_ITEM)) {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- if (name != null) {
- outPerms.add(name.intern());
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <perms> has" + " no name at "
- + parser.getPositionDescription());
- }
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Unknown element under <perms>: " + parser.getName());
- }
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
void createNewUserLILPw(PackageManagerService service, Installer installer,
int userHandle, File path) {
path.mkdir();
@@ -3126,6 +3165,8 @@
file = getUserPackagesStateBackupFile(userId);
file.delete();
removeCrossProfileIntentFiltersLPw(userId);
+
+ mRuntimePermissionsPersistence.onUserRemoved(userId);
}
void removeCrossProfileIntentFiltersLPw(int userId) {
@@ -3317,7 +3358,7 @@
return null;
}
- static final void printFlags(PrintWriter pw, int val, Object[] spec) {
+ static void printFlags(PrintWriter pw, int val, Object[] spec) {
pw.print("[ ");
for (int i=0; i<spec.length; i+=2) {
int mask = (Integer)spec[i];
@@ -3414,8 +3455,8 @@
pw.println(ps.name);
}
- pw.print(prefix); pw.print(" userId="); pw.print(ps.appId);
- pw.print(" gids="); pw.println(PackageManagerService.arrayToString(ps.gids));
+ pw.print(prefix); pw.print(" userId="); pw.println(ps.appId);
+
if (ps.sharedUser != null) {
pw.print(prefix); pw.print(" sharedUser="); pw.println(ps.sharedUser);
}
@@ -3525,10 +3566,15 @@
}
pw.print(prefix); pw.print(" signatures="); pw.println(ps.signatures);
pw.print(prefix); pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed);
- pw.print(" haveGids="); pw.print(ps.haveGids);
pw.print(" installStatus="); pw.println(ps.installStatus);
pw.print(prefix); pw.print(" pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
pw.println();
+
+ if (ps.sharedUser == null) {
+ PermissionsState permissionsState = ps.getPermissionsState();
+ dumpInstallPermissionsLPr(pw, prefix + " ", permissionsState);
+ }
+
for (UserInfo user : users) {
pw.print(prefix); pw.print(" User "); pw.print(user.id); pw.print(": ");
pw.print(" installed=");
@@ -3546,6 +3592,14 @@
pw.print(prefix); pw.print(" lastDisabledCaller: ");
pw.println(lastDisabledAppCaller);
}
+
+ if (ps.sharedUser == null) {
+ PermissionsState permissionsState = ps.getPermissionsState();
+ dumpGidsLPr(pw, prefix + " ", permissionsState.computeGids(user.id));
+ dumpRuntimePermissionsLPr(pw, prefix + " ", permissionsState
+ .getRuntimePermissions(user.id));
+ }
+
ArraySet<String> cmp = ps.getDisabledComponents(user.id);
if (cmp != null && cmp.size() > 0) {
pw.print(prefix); pw.println(" disabledComponents:");
@@ -3561,12 +3615,6 @@
}
}
}
- if (ps.grantedPermissions.size() > 0) {
- pw.print(prefix); pw.println(" grantedPermissions:");
- for (String s : ps.grantedPermissions) {
- pw.print(prefix); pw.print(" "); pw.println(s);
- }
- }
}
void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState, boolean checkin) {
@@ -3652,7 +3700,8 @@
pw.println("):");
pw.print(" sourcePackage="); pw.println(p.sourcePackage);
pw.print(" uid="); pw.print(p.uid);
- pw.print(" gids="); pw.print(PackageManagerService.arrayToString(p.gids));
+ pw.print(" gids="); pw.print(Arrays.toString(
+ p.computeGids(UserHandle.USER_OWNER)));
pw.print(" type="); pw.print(p.type);
pw.print(" prot=");
pw.println(PermissionInfo.protectionToString(p.protectionLevel));
@@ -3688,14 +3737,21 @@
pw.print("] (");
pw.print(Integer.toHexString(System.identityHashCode(su)));
pw.println("):");
- pw.print(" userId=");
- pw.print(su.userId);
- pw.print(" gids=");
- pw.println(PackageManagerService.arrayToString(su.gids));
- pw.println(" grantedPermissions:");
- for (String s : su.grantedPermissions) {
- pw.print(" ");
- pw.println(s);
+
+ String prefix = " ";
+ pw.print(prefix); pw.print("userId="); pw.println(su.userId);
+
+ PermissionsState permissionsState = su.getPermissionsState();
+ dumpInstallPermissionsLPr(pw, prefix, permissionsState);
+
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ final int[] gids = permissionsState.computeGids(userId);
+ Set<String> permissions = permissionsState.getRuntimePermissions(userId);
+ if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) {
+ pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": ");
+ dumpGidsLPr(pw, prefix + " ", gids);
+ dumpRuntimePermissionsLPr(pw, prefix + " ", permissions);
+ }
}
} else {
pw.print("suid,"); pw.print(su.userId); pw.print(","); pw.println(su.name);
@@ -3730,4 +3786,373 @@
pw.print("]");
}
}
+
+ void dumpGidsLPr(PrintWriter pw, String prefix, int[] gids) {
+ if (!ArrayUtils.isEmpty(gids)) {
+ pw.print(prefix); pw.print("gids="); pw.println(
+ PackageManagerService.arrayToString(gids));
+ }
+ }
+
+ void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, Set<String> permissions) {
+ if (!permissions.isEmpty()) {
+ pw.print(prefix); pw.println("runtime permissions:");
+ for (String permission : permissions) {
+ pw.print(prefix); pw.print(" "); pw.println(permission);
+ }
+ }
+ }
+
+ void dumpInstallPermissionsLPr(PrintWriter pw, String prefix,
+ PermissionsState permissionsState) {
+ Set<String> permissions = permissionsState.getInstallPermissions();
+ if (!permissions.isEmpty()) {
+ pw.print(prefix); pw.println("install permissions:");
+ for (String permission : permissions) {
+ pw.print(prefix); pw.print(" "); pw.println(permission);
+ }
+ }
+ }
+
+ public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
+ if (sync) {
+ mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId);
+ } else {
+ mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
+ }
+ }
+
+ private final class RuntimePermissionPersistence {
+ private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 200;
+
+ private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000;
+
+ private final Handler mHandler = new MyHandler();
+
+ private final Object mLock;
+
+ @GuardedBy("mLock")
+ private SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
+
+ @GuardedBy("mLock")
+ private SparseLongArray mLastNotWrittenMutationTimesMillis = new SparseLongArray();
+
+ public RuntimePermissionPersistence(Object lock) {
+ mLock = lock;
+ }
+
+ public void writePermissionsForUserSyncLPr(int userId) {
+ mHandler.removeMessages(userId);
+ writePermissionsSync(userId);
+ }
+
+ public void writePermissionsForUserAsyncLPr(int userId) {
+ final long currentTimeMillis = SystemClock.uptimeMillis();
+
+ if (mWriteScheduled.get(userId)) {
+ mHandler.removeMessages(userId);
+
+ // If enough time passed, write without holding off anymore.
+ final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis
+ .get(userId);
+ final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
+ - lastNotWrittenMutationTimeMillis;
+ if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) {
+ mHandler.obtainMessage(userId).sendToTarget();
+ return;
+ }
+
+ // Hold off a bit more as settings are frequently changing.
+ final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis
+ + MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0);
+ final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS,
+ maxDelayMillis);
+
+ Message message = mHandler.obtainMessage(userId);
+ mHandler.sendMessageDelayed(message, writeDelayMillis);
+ } else {
+ mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
+ Message message = mHandler.obtainMessage(userId);
+ mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
+ mWriteScheduled.put(userId, true);
+ }
+ }
+
+ private void writePermissionsSync(int userId) {
+ AtomicFile destination = new AtomicFile(getUserRuntimePermissionsFile(userId));
+
+ ArrayMap<String, Set<String>> permissionsForPackage = new ArrayMap<>();
+ ArrayMap<String, Set<String>> permissionsForSharedUser = new ArrayMap<>();
+
+ synchronized (mLock) {
+ mWriteScheduled.delete(userId);
+
+ final int packageCount = mPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ String packageName = mPackages.keyAt(i);
+ PackageSetting packageSetting = mPackages.valueAt(i);
+ if (packageSetting.sharedUser == null) {
+ PermissionsState permissionsState = packageSetting.getPermissionsState();
+ Set<String> permissions = permissionsState.getRuntimePermissions(userId);
+ if (!permissions.isEmpty()) {
+ permissionsForPackage.put(packageName, permissions);
+ }
+ }
+ }
+
+ final int sharedUserCount = mSharedUsers.size();
+ for (int i = 0; i < sharedUserCount; i++) {
+ String sharedUserName = mSharedUsers.keyAt(i);
+ SharedUserSetting sharedUser = mSharedUsers.valueAt(i);
+ PermissionsState permissionsState = sharedUser.getPermissionsState();
+ Set<String> permissions = permissionsState.getRuntimePermissions(userId);
+ if (!permissions.isEmpty()) {
+ permissionsForSharedUser.put(sharedUserName, permissions);
+ }
+ }
+ }
+
+ FileOutputStream out = null;
+ try {
+ out = destination.startWrite();
+
+ XmlSerializer serializer = Xml.newSerializer();
+ serializer.setOutput(out, "utf-8");
+ serializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_RUNTIME_PERMISSIONS);
+
+ final int packageCount = permissionsForPackage.size();
+ for (int i = 0; i < packageCount; i++) {
+ String packageName = permissionsForPackage.keyAt(i);
+ Set<String> permissions = permissionsForPackage.valueAt(i);
+ serializer.startTag(null, TAG_PACKAGE);
+ serializer.attribute(null, ATTR_NAME, packageName);
+ writePermissions(serializer, permissions);
+ serializer.endTag(null, TAG_PACKAGE);
+ }
+
+ final int sharedUserCount = permissionsForSharedUser.size();
+ for (int i = 0; i < sharedUserCount; i++) {
+ String packageName = permissionsForSharedUser.keyAt(i);
+ Set<String> permissions = permissionsForSharedUser.valueAt(i);
+ serializer.startTag(null, TAG_SHARED_USER);
+ serializer.attribute(null, ATTR_NAME, packageName);
+ writePermissions(serializer, permissions);
+ serializer.endTag(null, TAG_SHARED_USER);
+ }
+
+ serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
+ serializer.endDocument();
+ destination.finishWrite(out);
+ } catch (IOException e) {
+ Slog.wtf(PackageManagerService.TAG,
+ "Failed to write settings, restoring backup", e);
+ destination.failWrite(out);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ private void onUserRemoved(int userId) {
+ // Make sure we do not
+ mHandler.removeMessages(userId);
+
+ for (SettingBase sb : mPackages.values()) {
+ revokeRuntimePermissions(sb, userId);
+ }
+
+ for (SettingBase sb : mSharedUsers.values()) {
+ revokeRuntimePermissions(sb, userId);
+ }
+ }
+
+ private void revokeRuntimePermissions(SettingBase sb, int userId) {
+ PermissionsState permissionsState = sb.getPermissionsState();
+ for (String permission : permissionsState.getRuntimePermissions(userId)) {
+ BasePermission bp = mPermissions.get(permission);
+ if (bp != null) {
+ permissionsState.revokeRuntimePermission(bp, userId);
+ }
+ }
+ }
+
+ public void readStateForUserSyncLPr(int userId) {
+ File permissionsFile = getUserRuntimePermissionsFile(userId);
+ if (!permissionsFile.exists()) {
+ return;
+ }
+
+ FileInputStream in;
+ try {
+ in = new FileInputStream(permissionsFile);
+ } catch (FileNotFoundException fnfe) {
+ Slog.i(PackageManagerService.TAG, "No permissions state");
+ return;
+ }
+
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+ parseRuntimePermissionsLPr(parser, userId);
+ } catch (XmlPullParserException | IOException ise) {
+ throw new IllegalStateException("Failed parsing permissions file: "
+ + permissionsFile , ise);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ private void parseRuntimePermissionsLPr(XmlPullParser parser, int userId)
+ throws IOException, XmlPullParserException {
+ parser.next();
+ skipEmptyTextTags(parser);
+ if (!accept(parser, XmlPullParser.START_TAG, TAG_RUNTIME_PERMISSIONS)) {
+ return;
+ }
+
+ parser.next();
+
+ while (parsePackageLPr(parser, userId)
+ || parseSharedUserLPr(parser, userId)) {
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_RUNTIME_PERMISSIONS);
+ }
+
+ private boolean parsePackageLPr(XmlPullParser parser, int userId)
+ throws IOException, XmlPullParserException {
+ skipEmptyTextTags(parser);
+ if (!accept(parser, XmlPullParser.START_TAG, TAG_PACKAGE)) {
+ return false;
+ }
+
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+
+ parser.next();
+
+ PackageSetting ps = mPackages.get(name);
+ if (ps != null) {
+ while (parsePermissionLPr(parser, ps.getPermissionsState(), userId)) {
+ parser.next();
+ }
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_PACKAGE);
+
+ return true;
+ }
+
+ private boolean parseSharedUserLPr(XmlPullParser parser, int userId)
+ throws IOException, XmlPullParserException {
+ skipEmptyTextTags(parser);
+ if (!accept(parser, XmlPullParser.START_TAG, TAG_SHARED_USER)) {
+ return false;
+ }
+
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+
+ parser.next();
+
+ SharedUserSetting sus = mSharedUsers.get(name);
+ if (sus != null) {
+ while (parsePermissionLPr(parser, sus.getPermissionsState(), userId)) {
+ parser.next();
+ }
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_SHARED_USER);
+
+ return true;
+ }
+
+ private boolean parsePermissionLPr(XmlPullParser parser, PermissionsState permissionsState,
+ int userId) throws IOException, XmlPullParserException {
+ skipEmptyTextTags(parser);
+ if (!accept(parser, XmlPullParser.START_TAG, TAG_ITEM)) {
+ return false;
+ }
+
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+
+ parser.next();
+
+ BasePermission bp = mPermissions.get(name);
+ if (bp != null) {
+ if (permissionsState.grantRuntimePermission(bp, userId) ==
+ PermissionsState.PERMISSION_OPERATION_FAILURE) {
+ Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+ }
+ } else {
+ Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_ITEM);
+
+ return true;
+ }
+
+ private void expect(XmlPullParser parser, int type, String tag)
+ throws IOException, XmlPullParserException {
+ if (!accept(parser, type, tag)) {
+ throw new XmlPullParserException("Expected event: " + type
+ + " and tag: " + tag + " but got event: " + parser.getEventType()
+ + " and tag:" + parser.getName());
+ }
+ }
+
+ private void skipEmptyTextTags(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ while (accept(parser, XmlPullParser.TEXT, null)
+ && parser.isWhitespace()) {
+ parser.next();
+ }
+ }
+
+ private boolean accept(XmlPullParser parser, int type, String tag)
+ throws IOException, XmlPullParserException {
+ if (parser.getEventType() != type) {
+ return false;
+ }
+ if (tag != null) {
+ if (!tag.equals(parser.getName())) {
+ return false;
+ }
+ } else if (parser.getName() != null) {
+ return false;
+ }
+ return true;
+ }
+
+ private void writePermissions(XmlSerializer serializer, Set<String> permissions)
+ throws IOException {
+ for (String permission : permissions) {
+ serializer.startTag(null, TAG_ITEM);
+ serializer.attribute(null, ATTR_NAME, permission);
+ serializer.endTag(null, TAG_ITEM);
+ }
+ }
+
+ private final class MyHandler extends Handler {
+ public MyHandler() {
+ super(BackgroundThread.getHandler().getLooper());
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ final int userId = message.what;
+ Runnable callback = (Runnable) message.obj;
+ writePermissionsSync(userId);
+ if (callback != null) {
+ callback.run();
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index d95739c..06e020a 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -21,7 +21,7 @@
/**
* Settings data for a particular shared user ID we know about.
*/
-final class SharedUserSetting extends GrantedPermissions {
+final class SharedUserSetting extends SettingBase {
final String name;
int userId;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 930ef9a..be6550c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4025,7 +4025,7 @@
}
private void offsetInputMethodWindowLw(WindowState win) {
- int top = win.getDisplayFrameLw().top;
+ int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
top += win.getGivenContentInsetsLw().top;
if (mContentBottom > top) {
mContentBottom = top;
@@ -4044,7 +4044,7 @@
}
private void offsetVoiceInputWindowLw(WindowState win) {
- int top = win.getDisplayFrameLw().top;
+ int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
top += win.getGivenContentInsetsLw().top;
if (mVoiceContentBottom > top) {
mVoiceContentBottom = top;
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/power/DeviceIdleController.java
similarity index 84%
rename from services/core/java/com/android/server/DeviceIdleController.java
rename to services/core/java/com/android/server/power/DeviceIdleController.java
index 062992d..dd00446 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/power/DeviceIdleController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.power;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -30,12 +30,16 @@
import android.hardware.display.DisplayManager;
import android.net.INetworkPolicyManager;
import android.os.Binder;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.TimeUtils;
import android.view.Display;
import com.android.internal.app.IBatteryStats;
+import com.android.server.SystemService;
import com.android.server.am.BatteryStatsService;
import java.io.FileDescriptor;
@@ -60,6 +64,11 @@
*/
private static final long DEFAULT_INACTIVE_TIMEOUT = 30*60*1000L;
/**
+ * This is the time, after seeing motion, that we wait after becoming inactive from
+ * that until we start looking for motion again.
+ */
+ private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT = 10*60*1000L;
+ /**
* This is the time, after the inactive timeout elapses, that we will wait looking
* for significant motion until we truly consider the device to be idle.
*/
@@ -94,11 +103,13 @@
private AlarmManager mAlarmManager;
private IBatteryStats mBatteryStats;
+ private PowerManagerInternal mLocalPowerManager;
private INetworkPolicyManager mNetworkPolicyManager;
private DisplayManager mDisplayManager;
private SensorManager mSensorManager;
private Sensor mSigMotionSensor;
private PendingIntent mAlarmIntent;
+ private Intent mIdleIntent;
private Display mCurDisplay;
private boolean mScreenOn;
private boolean mCharging;
@@ -124,6 +135,7 @@
private int mState;
+ private long mInactiveTimeout;
private long mNextAlarmTime;
private long mNextIdlePendingDelay;
private long mNextIdleDelay;
@@ -181,6 +193,7 @@
synchronized (this) {
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mBatteryStats = BatteryStatsService.getService();
+ mLocalPowerManager = getLocalService(PowerManagerInternal.class);
mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
mDisplayManager = (DisplayManager) getContext().getSystemService(
@@ -193,6 +206,9 @@
.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
+ mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(ACTION_STEP_IDLE_STATE);
@@ -205,6 +221,7 @@
// a battery update the next time the level drops.
mCharging = true;
mState = STATE_ACTIVE;
+ mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT;
updateDisplayLocked();
}
@@ -238,12 +255,17 @@
void becomeActiveLocked() {
if (mState != STATE_ACTIVE) {
+ mLocalPowerManager.setDeviceIdleMode(false);
try {
mNetworkPolicyManager.setDeviceIdleMode(false);
mBatteryStats.noteDeviceIdleMode(false, true, false);
} catch (RemoteException e) {
}
+ if (mState == STATE_IDLE) {
+ getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+ }
mState = STATE_ACTIVE;
+ mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT;
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
cancelAlarmLocked();
@@ -256,7 +278,9 @@
// Screen has turned off; we are now going to become inactive and start
// waiting to see if we will ultimately go idle.
mState = STATE_INACTIVE;
- scheduleAlarmLocked(DEFAULT_INACTIVE_TIMEOUT, false);
+ mNextIdlePendingDelay = 0;
+ mNextIdleDelay = 0;
+ scheduleAlarmLocked(mInactiveTimeout, false);
}
}
@@ -283,11 +307,13 @@
mNextIdleDelay = DEFAULT_MAX_IDLE_TIMEOUT;
}
mState = STATE_IDLE;
+ mLocalPowerManager.setDeviceIdleMode(true);
try {
mNetworkPolicyManager.setDeviceIdleMode(true);
mBatteryStats.noteDeviceIdleMode(true, false, false);
} catch (RemoteException e) {
}
+ getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
break;
case STATE_IDLE:
// We have been idling long enough, now it is time to do some work.
@@ -297,11 +323,13 @@
mNextIdlePendingDelay = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
}
mState = STATE_IDLE_PENDING;
+ mLocalPowerManager.setDeviceIdleMode(false);
try {
mNetworkPolicyManager.setDeviceIdleMode(false);
mBatteryStats.noteDeviceIdleMode(false, false, false);
} catch (RemoteException e) {
}
+ getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
break;
}
}
@@ -313,13 +341,18 @@
// state to wait again for no motion. Note that we only monitor for significant
// motion after moving out of the inactive state, so no need to worry about that.
if (mState != STATE_ACTIVE) {
- mState = STATE_INACTIVE;
+ mLocalPowerManager.setDeviceIdleMode(false);
try {
mNetworkPolicyManager.setDeviceIdleMode(false);
mBatteryStats.noteDeviceIdleMode(false, false, true);
} catch (RemoteException e) {
}
- stepIdleStateLocked();
+ if (mState == STATE_IDLE) {
+ getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+ }
+ mState = STATE_ACTIVE;
+ mInactiveTimeout = DEFAULT_MOTION_INACTIVE_TIMEOUT;
+ becomeInactiveIfAppropriateLocked();
}
}
@@ -399,26 +432,30 @@
}
}
- pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor);
- pw.print(" mCurDisplay="); pw.println(mCurDisplay);
- pw.print(" mScreenOn="); pw.println(mScreenOn);
- pw.print(" mCharging="); pw.println(mCharging);
- pw.print(" mSigMotionActive="); pw.println(mSigMotionActive);
- pw.print(" mState="); pw.println(stateToString(mState));
- if (mNextAlarmTime != 0) {
- pw.print(" mNextAlarmTime=");
- TimeUtils.formatDuration(mNextAlarmTime, SystemClock.elapsedRealtime(), pw);
+ synchronized (this) {
+ pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor);
+ pw.print(" mCurDisplay="); pw.println(mCurDisplay);
+ pw.print(" mScreenOn="); pw.println(mScreenOn);
+ pw.print(" mCharging="); pw.println(mCharging);
+ pw.print(" mSigMotionActive="); pw.println(mSigMotionActive);
+ pw.print(" mState="); pw.println(stateToString(mState));
+ pw.print(" mInactiveTimeout="); TimeUtils.formatDuration(mInactiveTimeout, pw);
pw.println();
- }
- if (mNextIdlePendingDelay != 0) {
- pw.print(" mNextIdlePendingDelay=");
- TimeUtils.formatDuration(mNextIdlePendingDelay, pw);
- pw.println();
- }
- if (mNextIdleDelay != 0) {
- pw.print(" mNextIdleDelay=");
- TimeUtils.formatDuration(mNextIdleDelay, pw);
- pw.println();
+ if (mNextAlarmTime != 0) {
+ pw.print(" mNextAlarmTime=");
+ TimeUtils.formatDuration(mNextAlarmTime, SystemClock.elapsedRealtime(), pw);
+ pw.println();
+ }
+ if (mNextIdlePendingDelay != 0) {
+ pw.print(" mNextIdlePendingDelay=");
+ TimeUtils.formatDuration(mNextIdlePendingDelay, pw);
+ pw.println();
+ }
+ if (mNextIdleDelay != 0) {
+ pw.print(" mNextIdleDelay=");
+ TimeUtils.formatDuration(mNextIdleDelay, pw);
+ pw.println();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9e373b7..33b451d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -420,6 +420,9 @@
// True if the battery level is currently considered low.
private boolean mBatteryLevelLow;
+ // True if we are currently in device idle mode.
+ private boolean mDeviceIdleMode;
+
// True if theater mode is enabled
private boolean mTheaterModeEnabled;
@@ -2178,6 +2181,12 @@
}
}
+ private boolean isDeviceIdleModeInternal() {
+ synchronized (mLock) {
+ return mDeviceIdleMode;
+ }
+ }
+
private void handleBatteryStateChangedLocked() {
mDirty |= DIRTY_BATTERY_STATE;
updatePowerStateLocked();
@@ -3050,6 +3059,16 @@
}
}
+ @Override // Binder call
+ public boolean isDeviceIdleMode() {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return isDeviceIdleModeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* Reboots the device.
*
@@ -3295,5 +3314,12 @@
mLowPowerModeListeners.add(listener);
}
}
+
+ @Override
+ public void setDeviceIdleMode(boolean enabled) {
+ synchronized (mLock) {
+ mDeviceIdleMode = enabled;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index c09ea5c..e385be3 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -140,10 +140,9 @@
}
/**
- * @param layer The new layer value.
- * @param inTransaction Whether the call is made within a surface transaction.
+ * NOTE: Must be called with Surface transaction open.
*/
- void adjustSurface(int layer, boolean inTransaction) {
+ private void adjustBounds() {
final int dw, dh;
final float xPos, yPos;
if (!mStack.isFullscreen()) {
@@ -163,29 +162,24 @@
yPos = -1 * dh / 6;
}
- try {
- if (!inTransaction) {
- SurfaceControl.openTransaction();
- }
- mDimSurface.setPosition(xPos, yPos);
- mDimSurface.setSize(dw, dh);
- mDimSurface.setLayer(layer);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failure setting size or layer", e);
- } finally {
- if (!inTransaction) {
- SurfaceControl.closeTransaction();
- }
- }
+ mDimSurface.setPosition(xPos, yPos);
+ mDimSurface.setSize(dw, dh);
+
mLastBounds.set(mBounds);
- mLayer = layer;
}
- // Assumes that surface transactions are currently closed.
+ /** @param bounds The new bounds to set */
void setBounds(Rect bounds) {
mBounds.set(bounds);
if (isDimming() && !mLastBounds.equals(bounds)) {
- adjustSurface(mLayer, false);
+ try {
+ SurfaceControl.openTransaction();
+ adjustBounds();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failure setting size", e);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
}
}
@@ -224,9 +218,10 @@
return;
}
- if (!mLastBounds.equals(mBounds) || mLayer != layer) {
- adjustSurface(layer, true);
+ if (!mLastBounds.equals(mBounds)) {
+ adjustBounds();
}
+ setLayer(layer);
long curTime = SystemClock.uptimeMillis();
final boolean animating = isAnimating();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 6d09f55..b61a6f7 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -373,10 +373,8 @@
mService.requestTraversalLocked();
}
- mAnimationBackgroundSurface.destroySurface();
- mAnimationBackgroundSurface = null;
- mDimLayer.destroySurface();
- mDimLayer = null;
+ close();
+
mDisplayContent = null;
}
@@ -501,8 +499,14 @@
}
void close() {
- mDimLayer.mDimSurface.destroy();
- mAnimationBackgroundSurface.mDimSurface.destroy();
+ if (mAnimationBackgroundSurface != null) {
+ mAnimationBackgroundSurface.destroySurface();
+ mAnimationBackgroundSurface = null;
+ }
+ if (mDimLayer != null) {
+ mDimLayer.destroySurface();
+ mDimLayer = null;
+ }
}
public void dump(String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4c80b07..09bc2ab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -178,7 +178,7 @@
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_ORIENTATION = false;
static final boolean DEBUG_CONFIGURATION = false;
- static final boolean DEBUG_APP_TRANSITIONS = false;
+ static final boolean DEBUG_APP_TRANSITIONS = true;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
@@ -189,7 +189,7 @@
static final boolean DEBUG_LAYOUT_REPEATS = true;
static final boolean DEBUG_SURFACE_TRACE = false;
static final boolean DEBUG_WINDOW_TRACE = false;
- static final boolean DEBUG_TASK_MOVEMENT = false;
+ static final boolean DEBUG_TASK_MOVEMENT = true;
static final boolean DEBUG_STACK = false;
static final boolean DEBUG_DISPLAY = false;
static final boolean DEBUG_POWER = false;
@@ -507,6 +507,7 @@
boolean mClientFreezingScreen = false;
int mAppsFreezingScreen = 0;
int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ int mLastKeyguardForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
int mLayoutSeq = 0;
@@ -3115,6 +3116,10 @@
}
winAnimator.mEnteringAnimation = true;
if (toBeDisplayed) {
+ if ((win.mAttrs.softInputMode & SOFT_INPUT_MASK_ADJUST)
+ == SOFT_INPUT_ADJUST_RESIZE) {
+ win.mLayoutNeeded = true;
+ }
if (win.isDrawnLw() && okToDisplay()) {
winAnimator.applyEnterAnimationLocked();
}
@@ -3700,41 +3705,68 @@
}
}
- public int getOrientationFromWindowsLocked() {
- if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) {
- // If the display is frozen, some activities may be in the middle
- // of restarting, and thus have removed their old window. If the
- // window has the flag to hide the lock screen, then the lock screen
- // can re-appear and inflict its own orientation on us. Keep the
- // orientation stable until this all settles down.
- return mLastWindowForcedOrientation;
+ public int getOrientationLocked() {
+ if (mDisplayFrozen) {
+ if (mLastWindowForcedOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Display is frozen, return "
+ + mLastWindowForcedOrientation);
+ // If the display is frozen, some activities may be in the middle
+ // of restarting, and thus have removed their old window. If the
+ // window has the flag to hide the lock screen, then the lock screen
+ // can re-appear and inflict its own orientation on us. Keep the
+ // orientation stable until this all settles down.
+ return mLastWindowForcedOrientation;
+ }
+ } else {
+ // TODO(multidisplay): Change to the correct display.
+ final WindowList windows = getDefaultWindowListLocked();
+ for (int pos = windows.size() - 1; pos >= 0; --pos) {
+ WindowState win = windows.get(pos);
+ if (win.mAppToken != null) {
+ // We hit an application window. so the orientation will be determined by the
+ // app window. No point in continuing further.
+ break;
+ }
+ if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) {
+ continue;
+ }
+ int req = win.mAttrs.screenOrientation;
+ if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
+ (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
+ continue;
+ }
+
+ if (DEBUG_ORIENTATION) Slog.v(TAG, win + " forcing orientation to " + req);
+ if (mPolicy.isKeyguardHostWindow(win.mAttrs)) {
+ mLastKeyguardForcedOrientation = req;
+ }
+ return (mLastWindowForcedOrientation = req);
+ }
+ mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+ if (mPolicy.isKeyguardLocked()) {
+ // The screen is locked and no top system window is requesting an orientation.
+ // Return either the orientation of the show-when-locked app (if there is any) or
+ // the orientation of the keyguard. No point in searching from the rest of apps.
+ WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
+ AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
+ null : winShowWhenLocked.mAppToken;
+ if (appShowWhenLocked != null) {
+ int req = appShowWhenLocked.requestedOrientation;
+ if (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+ req = mLastKeyguardForcedOrientation;
+ }
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + appShowWhenLocked
+ + " -- show when locked, return " + req);
+ return req;
+ }
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
+ "No one is requesting an orientation when the screen is locked");
+ return mLastKeyguardForcedOrientation;
+ }
}
- // TODO(multidisplay): Change to the correct display.
- final WindowList windows = getDefaultWindowListLocked();
- for (int pos = windows.size() - 1; pos >= 0; --pos) {
- WindowState win = windows.get(pos);
- if (win.mAppToken != null) {
- // We hit an application window. so the orientation will be determined by the
- // app window. No point in continuing further.
- return (mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- }
- if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) {
- continue;
- }
- int req = win.mAttrs.screenOrientation;
- if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
- (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
- continue;
- }
-
- if (DEBUG_ORIENTATION) Slog.v(TAG, win + " forcing orientation to " + req);
- return (mLastWindowForcedOrientation = req);
- }
- return (mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- }
-
- public int getOrientationFromAppTokensLocked() {
+ // Top system windows are not requesting an orientation. Start searching from apps.
int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean findingBehind = false;
boolean lastFullscreen = false;
@@ -3804,8 +3836,11 @@
findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
}
}
- if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation");
- return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation, return "
+ + mForcedAppOrientation);
+ // The next app has not been requested to be visible, so we keep the current orientation
+ // to prevent freezing/unfreezing the display too early.
+ return mForcedAppOrientation;
}
@Override
@@ -3887,11 +3922,7 @@
boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
long ident = Binder.clearCallingIdentity();
try {
- int req = getOrientationFromWindowsLocked();
- if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
- req = getOrientationFromAppTokensLocked();
- }
-
+ int req = getOrientationLocked();
if (req != mForcedAppOrientation) {
mForcedAppOrientation = req;
//send a message to Policy indicating orientation change to take
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index bcd8d42..65e3534 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -66,7 +66,6 @@
import com.android.server.media.MediaRouterService;
import com.android.server.media.MediaSessionService;
import com.android.server.media.projection.MediaProjectionManagerService;
-import com.android.server.MidiService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
import com.android.server.notification.NotificationManagerService;
@@ -76,6 +75,7 @@
import com.android.server.pm.LauncherAppsService;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.UserManagerService;
+import com.android.server.power.DeviceIdleController;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
import com.android.server.restrictions.RestrictionsManagerService;
@@ -406,12 +406,9 @@
AudioService audioService = null;
MmsServiceBroker mmsService = null;
EntropyMixer entropyMixer = null;
- MidiService midiService = null;
boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
- boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
- boolean disableTelephony = SystemProperties.getBoolean("config.disable_telephony", false);
boolean disableLocation = SystemProperties.getBoolean("config.disable_location", false);
boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);
@@ -520,7 +517,6 @@
LockSettingsService lockSettings = null;
AssetAtlasService atlas = null;
MediaRouterService mediaRouter = null;
- MidiService midi = null;
// Bring up services needed for UI.
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
@@ -564,6 +560,10 @@
}
}
+ // We start this here so that we update our configuration to set watch or television
+ // as appropriate.
+ mSystemServiceManager.startService(UiModeManagerService.class);
+
try {
mPackageManagerService.performBootDexOpt();
} catch (Throwable e) {
@@ -788,29 +788,25 @@
}
}
- if (!disableMedia && !"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
- try {
- Slog.i(TAG, "Audio Service");
- audioService = new AudioService(context);
- ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
- } catch (Throwable e) {
- reportWtf("starting Audio Service", e);
- }
+ try {
+ Slog.i(TAG, "Audio Service");
+ audioService = new AudioService(context);
+ ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
+ } catch (Throwable e) {
+ reportWtf("starting Audio Service", e);
}
if (!disableNonCoreServices) {
mSystemServiceManager.startService(DockObserver.class);
}
- if (!disableMedia) {
- try {
- Slog.i(TAG, "Wired Accessory Manager");
- // Listen for wired headset changes
- inputManager.setWiredAccessoryCallbacks(
- new WiredAccessoryManager(context, inputManager));
- } catch (Throwable e) {
- reportWtf("starting WiredAccessoryManager", e);
- }
+ try {
+ Slog.i(TAG, "Wired Accessory Manager");
+ // Listen for wired headset changes
+ inputManager.setWiredAccessoryCallbacks(
+ new WiredAccessoryManager(context, inputManager));
+ } catch (Throwable e) {
+ reportWtf("starting WiredAccessoryManager", e);
}
if (!disableNonCoreServices) {
@@ -843,8 +839,6 @@
mSystemServiceManager.startService(TwilightService.class);
- mSystemServiceManager.startService(UiModeManagerService.class);
-
mSystemServiceManager.startService(JobSchedulerService.class);
if (!disableNonCoreServices) {
@@ -889,14 +883,12 @@
}
}
- if (!disableMedia) {
- try {
- Slog.i(TAG, "CommonTimeManagementService");
- commonTimeMgmtService = new CommonTimeManagementService(context);
- ServiceManager.addService("commontime_management", commonTimeMgmtService);
- } catch (Throwable e) {
- reportWtf("starting CommonTimeManagementService service", e);
- }
+ try {
+ Slog.i(TAG, "CommonTimeManagementService");
+ commonTimeMgmtService = new CommonTimeManagementService(context);
+ ServiceManager.addService("commontime_management", commonTimeMgmtService);
+ } catch (Throwable e) {
+ reportWtf("starting CommonTimeManagementService service", e);
}
if (!disableNetwork) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index b631331..4dc1131 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -32,7 +32,6 @@
import java.io.IOException;
public class PackageManagerSettingsTests extends AndroidTestCase {
-
private static final String PACKAGE_NAME_2 = "com.google.app2";
private static final String PACKAGE_NAME_3 = "com.android.app3";
private static final String PACKAGE_NAME_1 = "com.google.app1";
@@ -56,7 +55,7 @@
writeFile(new File(getContext().getFilesDir(), "system/packages.xml"),
("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ "<packages>"
- + "<last-platform-version internal=\"15\" external=\"0\" />"
+ + "<last-platform-version internal=\"15\" external=\"0\" fingerprint=\"foo\" />"
+ "<permission-trees>"
+ "<item name=\"com.google.android.permtree\" package=\"com.google.android.permpackage\" />"
+ "</permission-trees>"
@@ -110,28 +109,32 @@
.getBytes());
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ private void deleteSystemFolder() {
+ File systemFolder = new File(getContext().getFilesDir(), "system");
+ deleteFolder(systemFolder);
+ }
+
+ private static void deleteFolder(File folder) {
+ File[] files = folder.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ deleteFolder(file);
+ }
+ }
+ folder.delete();
}
private void writeOldFiles() {
+ deleteSystemFolder();
writePackagesXml();
writeStoppedPackagesXml();
writePackagesList();
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
public void testSettingsReadOld() {
- // Debug.waitForDebugger();
-
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext(), getContext().getFilesDir());
+ Settings settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_3));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_1));
@@ -149,11 +152,12 @@
public void testNewPackageRestrictionsFile() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext(), getContext().getFilesDir());
+ Settings settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
+ settings.writeLPr();
// Create Settings again to make it read from the new files
- settings = new Settings(getContext(), getContext().getFilesDir());
+ settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_2);
@@ -164,7 +168,7 @@
public void testEnableDisable() {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
- Settings settings = new Settings(getContext(), getContext().getFilesDir());
+ Settings settings = new Settings(getContext(), getContext().getFilesDir(), new Object());
assertEquals(true, settings.readLPw(null, null, 0, false));
// Enable/Disable a package
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index cfbebba..b265d47 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -506,6 +506,11 @@
}
@Override
+ public int checkSelfPermission(String permission) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void enforcePermission(
String permission, int pid, int uid, String message) {
throw new UnsupportedOperationException();
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 7531d7b..67a8c2b 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -191,13 +191,13 @@
/** @hide */
@Override
- public void grantPermission(String packageName, String permissionName) {
+ public void grantPermission(String packageName, String permissionName, UserHandle user) {
throw new UnsupportedOperationException();
}
/** @hide */
@Override
- public void revokePermission(String packageName, String permissionName) {
+ public void revokePermission(String packageName, String permissionName, UserHandle user) {
throw new UnsupportedOperationException();
}
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
index ecc7782..56c8119 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
@@ -14,9 +14,12 @@
package com.android.test.dynamic;
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
import android.app.Activity;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.os.Bundle;
+import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.GridLayout;
@@ -52,6 +55,26 @@
button.setWidth(400);
button.setHeight(400);
button.setBackgroundResource(icon[i]);
+ AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground();
+ d.addListener(new AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ Log.v(LOGCAT, "Animator start");
+ }
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ Log.v(LOGCAT, "Animator repeat");
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ Log.v(LOGCAT, "Animator end");
+ }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ Log.v(LOGCAT, "Animator cancel");
+ }
+ });
+
container.addView(button);
button.setOnClickListener(this);
}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
index 2ad23ed5..bdc1276 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
@@ -47,12 +47,12 @@
public void setAssistStructure(AssistStructure as) {
mAssistStructure = as;
mTextRects.clear();
- final int N = as.getWindowCount();
+ final int N = as.getWindowNodeCount();
if (N > 0) {
- AssistStructure.ViewNode window = new AssistStructure.ViewNode();
for (int i=0; i<N; i++) {
- as.getWindowAt(i, window);
- buildTextRects(window, 0, 0);
+ AssistStructure.WindowNode windowNode = as.getWindowNodeAt(i);
+ buildTextRects(windowNode.getRootViewNode(), windowNode.getLeft(),
+ windowNode.getTop());
}
}
invalidate();
@@ -79,9 +79,8 @@
if (N > 0) {
left -= root.getScrollX();
top -= root.getScrollY();
- AssistStructure.ViewNode child = new AssistStructure.ViewNode();
for (int i=0; i<N; i++) {
- root.getChildAt(i, child);
+ AssistStructure.ViewNode child = root.getChildAt(i);
buildTextRects(child, left, top);
}
}
diff --git a/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml b/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml
index f965ba7..d97d82c 100644
--- a/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml
+++ b/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml
@@ -1,14 +1,14 @@
<component name="ProjectRunConfigurationManager">
- <configuration default="false" name="All in bridge" type="JUnit" factoryName="JUnit" singleton="true" nameIsGenerated="true">
+ <configuration default="false" name="All in bridge" type="JUnit" factoryName="JUnit" singleton="true">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="bridge" />
- <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
- <option name="ALTERNATIVE_JRE_PATH" value="" />
+ <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
+ <option name="ALTERNATIVE_JRE_PATH" value="1.7" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
- <option name="VM_PARAMETERS" value="-ea -Dtest_res.dir="$PROJECT_DIR$/bridge/tests/res"" />
+ <option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
<option name="ENV_VARIABLES" />
@@ -18,13 +18,7 @@
</option>
<envs />
<patterns />
- <RunnerSettings RunnerId="Debug">
- <option name="DEBUG_PORT" value="" />
- <option name="TRANSPORT" value="0" />
- <option name="LOCAL" value="true" />
- </RunnerSettings>
<RunnerSettings RunnerId="Run" />
- <ConfigurationWrapper RunnerId="Debug" />
<ConfigurationWrapper RunnerId="Run" />
<method />
</configuration>
diff --git a/tools/layoutlib/.idea/runConfigurations/Bridge_quick.xml b/tools/layoutlib/.idea/runConfigurations/Bridge_quick.xml
new file mode 100644
index 0000000..4f0eb8d
--- /dev/null
+++ b/tools/layoutlib/.idea/runConfigurations/Bridge_quick.xml
@@ -0,0 +1,29 @@
+<component name="ProjectRunConfigurationManager">
+ <configuration default="false" name="Bridge quick" type="JUnit" factoryName="JUnit">
+ <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
+ <module name="bridge" />
+ <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+ <option name="ALTERNATIVE_JRE_PATH" value="" />
+ <option name="PACKAGE_NAME" />
+ <option name="MAIN_CLASS_NAME" value="" />
+ <option name="METHOD_NAME" value="" />
+ <option name="TEST_OBJECT" value="pattern" />
+ <option name="VM_PARAMETERS" value="-ea" />
+ <option name="PARAMETERS" value="" />
+ <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
+ <option name="ENV_VARIABLES" />
+ <option name="PASS_PARENT_ENVS" value="true" />
+ <option name="TEST_SEARCH_SCOPE">
+ <value defaultName="singleModule" />
+ </option>
+ <envs />
+ <patterns>
+ <pattern testClass="com.android.layoutlib.bridge.TestDelegates" />
+ <pattern testClass="android.graphics.Matrix_DelegateTest" />
+ <pattern testClass="com.android.layoutlib.bridge.android.BridgeXmlBlockParserTest" />
+ </patterns>
+ <RunnerSettings RunnerId="Run" />
+ <ConfigurationWrapper RunnerId="Run" />
+ <method />
+ </configuration>
+</component>
\ No newline at end of file
diff --git a/tools/layoutlib/.idea/runConfigurations/Create.xml b/tools/layoutlib/.idea/runConfigurations/Create.xml
index fb0b866..ff173e5 100644
--- a/tools/layoutlib/.idea/runConfigurations/Create.xml
+++ b/tools/layoutlib/.idea/runConfigurations/Create.xml
@@ -5,8 +5,8 @@
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/../../../../" />
- <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
- <option name="ALTERNATIVE_JRE_PATH" value="1.6" />
+ <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+ <option name="ALTERNATIVE_JRE_PATH" value="" />
<option name="ENABLE_SWING_INSPECTOR" value="false" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index be75dde..4d2d100 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -675,7 +675,7 @@
graphics.fillRoundRect(
(int)left, (int)top,
(int)(right - left), (int)(bottom - top),
- (int)rx, (int)ry);
+ 2 * (int)rx, 2 * (int)ry);
}
if (style == Paint.Style.STROKE.nativeInt ||
@@ -683,7 +683,7 @@
graphics.drawRoundRect(
(int)left, (int)top,
(int)(right - left), (int)(bottom - top),
- (int)rx, (int)ry);
+ 2 * (int)rx, 2 * (int)ry);
}
}
});
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 4acbd1c..80036e5 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -131,11 +131,11 @@
}
@Override
- public View createViewFromTag(View parent, String name, AttributeSet attrs,
- boolean inheritContext) {
+ public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
+ boolean ignoreThemeAttrs) {
View view;
try {
- view = super.createViewFromTag(parent, name, attrs, inheritContext);
+ view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttrs);
} catch (InflateException e) {
// try to load the class from using the custom view loader
try {
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index 7a73fae..7f1e977 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -21,9 +21,11 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
+import android.util.TypedValue;
import android.util.Xml;
import java.io.IOException;
@@ -36,9 +38,13 @@
*
*/
public class LayoutInflater_Delegate {
-
private static final String TAG_MERGE = "merge";
+ private static final String ATTR_LAYOUT = "layout";
+
+ private static final int[] ATTRS_THEME = new int[] {
+ com.android.internal.R.attr.theme };
+
public static boolean sIsInInclude = false;
/**
@@ -49,7 +55,7 @@
*/
@LayoutlibDelegate
/* package */ static void rInflate(LayoutInflater thisInflater, XmlPullParser parser,
- View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext)
+ View parent, Context context, AttributeSet attrs, boolean finishInflate)
throws XmlPullParserException, IOException {
if (finishInflate == false) {
@@ -61,7 +67,7 @@
// ---- START DEFAULT IMPLEMENTATION.
- thisInflater.rInflate_Original(parser, parent, attrs, finishInflate, inheritContext);
+ thisInflater.rInflate_Original(parser, parent, context, attrs, finishInflate);
// ---- END DEFAULT IMPLEMENTATION.
@@ -74,15 +80,50 @@
}
@LayoutlibDelegate
- public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, View parent,
- AttributeSet attrs, boolean inheritContext) throws XmlPullParserException, IOException {
-
+ public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser,
+ Context context, View parent, AttributeSet attrs)
+ throws XmlPullParserException, IOException {
int type;
if (parent instanceof ViewGroup) {
- final int layout = attrs.getAttributeResourceValue(null, "layout", 0);
+ // Apply a theme wrapper, if requested. This is sort of a weird
+ // edge case, since developers think the <include> overwrites
+ // values in the AttributeSet of the included View. So, if the
+ // included View has a theme attribute, we'll need to ignore it.
+ final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
+ final int themeResId = ta.getResourceId(0, 0);
+ final boolean hasThemeOverride = themeResId != 0;
+ if (hasThemeOverride) {
+ context = new ContextThemeWrapper(context, themeResId);
+ }
+ ta.recycle();
+
+ // If the layout is pointing to a theme attribute, we have to
+ // massage the value to get a resource identifier out of it.
+ int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
if (layout == 0) {
- final String value = attrs.getAttributeValue(null, "layout");
+ final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
+ if (value == null || value.length() <= 0) {
+ throw new InflateException("You must specify a layout in the"
+ + " include tag: <include layout=\"@layout/layoutID\" />");
+ }
+
+ // Attempt to resolve the "?attr/name" string to an identifier.
+ layout = context.getResources().getIdentifier(value.substring(1), null, null);
+ }
+
+ // The layout might be referencing a theme attribute.
+ // ---- START CHANGES
+ if (layout != 0) {
+ final TypedValue tempValue = new TypedValue();
+ if (context.getTheme().resolveAttribute(layout, tempValue, true)) {
+ layout = tempValue.resourceId;
+ }
+ }
+ // ---- END CHANGES
+
+ if (layout == 0) {
+ final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
if (value == null) {
throw new InflateException("You must specifiy a layout in the"
+ " include tag: <include layout=\"@layout/layoutID\" />");
@@ -111,13 +152,24 @@
if (TAG_MERGE.equals(childName)) {
// Inflate all children.
- thisInflater.rInflate(childParser, parent, childAttrs, false,
- inheritContext);
+ thisInflater.rInflate(childParser, parent, context, childAttrs, false);
} else {
final View view = thisInflater.createViewFromTag(parent, childName,
- childAttrs, inheritContext);
+ context, childAttrs, hasThemeOverride);
final ViewGroup group = (ViewGroup) parent;
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, com.android.internal.R.styleable.Include);
+ final int id = a.getResourceId(
+ com.android.internal.R.styleable.Include_id, View.NO_ID);
+ final int visibility = a.getInt(
+ com.android.internal.R.styleable.Include_visibility, -1);
+ final boolean hasWidth = a.hasValue(
+ com.android.internal.R.styleable.Include_layout_width);
+ final boolean hasHeight = a.hasValue(
+ com.android.internal.R.styleable.Include_layout_height);
+ a.recycle();
+
// We try to load the layout params set in the <include /> tag. If
// they don't exist, we will rely on the layout params set in the
// included XML file.
@@ -127,40 +179,27 @@
// successfully loaded layout params from the <include /> tag,
// false means we need to rely on the included layout params.
ViewGroup.LayoutParams params = null;
- try {
- // ---- START CHANGES
- sIsInInclude = true;
- // ---- END CHANGES
+ if (hasWidth && hasHeight) {
+ try {
+ // ---- START CHANGES
+ sIsInInclude = true;
+ // ---- END CHANGES
- params = group.generateLayoutParams(attrs);
+ params = group.generateLayoutParams(attrs);
- } catch (RuntimeException e) {
- // ---- START CHANGES
- sIsInInclude = false;
- // ---- END CHANGES
-
- params = group.generateLayoutParams(childAttrs);
- } finally {
- // ---- START CHANGES
- sIsInInclude = false;
- // ---- END CHANGES
-
- if (params != null) {
- view.setLayoutParams(params);
+ } finally {
+ // ---- START CHANGES
+ sIsInInclude = false;
+ // ---- END CHANGES
}
}
+ if (params == null) {
+ params = group.generateLayoutParams(childAttrs);
+ }
+ view.setLayoutParams(params);
// Inflate all children.
- thisInflater.rInflate(childParser, view, childAttrs, true, true);
-
- // Attempt to override the included layout's android:id with the
- // one set on the <include /> tag itself.
- TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.View, 0, 0);
- int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
- // While we're at it, let's try to override android:visibility.
- int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1);
- a.recycle();
+ thisInflater.rInflateChildren(childParser, view, childAttrs, true);
if (id != View.NO_ID) {
view.setId(id);
@@ -188,12 +227,6 @@
throw new InflateException("<include /> can only be used inside of a ViewGroup");
}
- final int currentDepth = parser.getDepth();
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
- // Empty
- }
+ LayoutInflater.consumeChildElements(parser);
}
-
-
}
diff --git a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
new file mode 100644
index 0000000..ec3a8d6
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Path;
+import android.graphics.Path.FillType;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region.Op;
+import android.graphics.Shader.TileMode;
+
+/**
+ * Paints shadow for rounded rectangles. Inspiration from CardView. Couldn't use that directly,
+ * since it modifies the size of the content, that we can't do.
+ */
+public class RectShadowPainter {
+
+
+ private static final int START_COLOR = ResourceHelper.getColor("#37000000");
+ private static final int END_COLOR = ResourceHelper.getColor("#03000000");
+ private static final float PERPENDICULAR_ANGLE = 90f;
+
+ public static void paintShadow(Outline viewOutline, float elevation, Canvas canvas) {
+ float shadowSize = elevationToShadow(elevation);
+ int saved = modifyCanvas(canvas, shadowSize);
+ if (saved == -1) {
+ return;
+ }
+ try {
+ Paint cornerPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+ cornerPaint.setStyle(Style.FILL);
+ Paint edgePaint = new Paint(cornerPaint);
+ edgePaint.setAntiAlias(false);
+ Rect outline = viewOutline.mRect;
+ float radius = viewOutline.mRadius;
+ float outerArcRadius = radius + shadowSize;
+ int[] colors = {START_COLOR, START_COLOR, END_COLOR};
+ cornerPaint.setShader(new RadialGradient(0, 0, outerArcRadius, colors,
+ new float[]{0f, radius / outerArcRadius, 1f}, TileMode.CLAMP));
+ edgePaint.setShader(new LinearGradient(0, 0, -shadowSize, 0, START_COLOR, END_COLOR,
+ TileMode.CLAMP));
+ Path path = new Path();
+ path.setFillType(FillType.EVEN_ODD);
+ // A rectangle bounding the complete shadow.
+ RectF shadowRect = new RectF(outline);
+ shadowRect.inset(-shadowSize, -shadowSize);
+ // A rectangle with edges corresponding to the straight edges of the outline.
+ RectF inset = new RectF(outline);
+ inset.inset(radius, radius);
+ // A rectangle used to represent the edge shadow.
+ RectF edgeShadowRect = new RectF();
+
+
+ // left and right sides.
+ edgeShadowRect.set(-shadowSize, 0f, 0f, inset.height());
+ // Left shadow
+ sideShadow(canvas, edgePaint, edgeShadowRect, outline.left, inset.top, 0);
+ // Right shadow
+ sideShadow(canvas, edgePaint, edgeShadowRect, outline.right, inset.bottom, 2);
+ // Top shadow
+ edgeShadowRect.set(-shadowSize, 0, 0, inset.width());
+ sideShadow(canvas, edgePaint, edgeShadowRect, inset.right, outline.top, 1);
+ // bottom shadow. This needs an inset so that blank doesn't appear when the content is
+ // moved up.
+ edgeShadowRect.set(-shadowSize, 0, shadowSize / 2f, inset.width());
+ edgePaint.setShader(new LinearGradient(edgeShadowRect.right, 0, edgeShadowRect.left, 0,
+ colors, new float[]{0f, 1 / 3f, 1f}, TileMode.CLAMP));
+ sideShadow(canvas, edgePaint, edgeShadowRect, inset.left, outline.bottom, 3);
+
+ // Draw corners.
+ drawCorner(canvas, cornerPaint, path, inset.right, inset.bottom, outerArcRadius, 0);
+ drawCorner(canvas, cornerPaint, path, inset.left, inset.bottom, outerArcRadius, 1);
+ drawCorner(canvas, cornerPaint, path, inset.left, inset.top, outerArcRadius, 2);
+ drawCorner(canvas, cornerPaint, path, inset.right, inset.top, outerArcRadius, 3);
+ } finally {
+ canvas.restoreToCount(saved);
+ }
+ }
+
+ private static float elevationToShadow(float elevation) {
+ // The factor is chosen by eyeballing the shadow size on device and preview.
+ return elevation * 0.5f;
+ }
+
+ /**
+ * Translate canvas by half of shadow size up, so that it appears that light is coming
+ * slightly from above. Also, remove clipping, so that shadow is not clipped.
+ */
+ private static int modifyCanvas(Canvas canvas, float shadowSize) {
+ Rect clipBounds = canvas.getClipBounds();
+ if (clipBounds.isEmpty()) {
+ return -1;
+ }
+ int saved = canvas.save();
+ // Usually canvas has been translated to the top left corner of the view when this is
+ // called. So, setting a clip rect at 0,0 will clip the top left part of the shadow.
+ // Thus, we just expand in each direction by width and height of the canvas.
+ canvas.clipRect(-canvas.getWidth(), -canvas.getHeight(), canvas.getWidth(),
+ canvas.getHeight(), Op.REPLACE);
+ canvas.translate(0, shadowSize / 2f);
+ return saved;
+ }
+
+ private static void sideShadow(Canvas canvas, Paint edgePaint,
+ RectF edgeShadowRect, float dx, float dy, int rotations) {
+ int saved = canvas.save();
+ canvas.translate(dx, dy);
+ canvas.rotate(rotations * PERPENDICULAR_ANGLE);
+ canvas.drawRect(edgeShadowRect, edgePaint);
+ canvas.restoreToCount(saved);
+ }
+
+ /**
+ * @param canvas Canvas to draw the rectangle on.
+ * @param paint Paint to use when drawing the corner.
+ * @param path A path to reuse. Prevents allocating memory for each path.
+ * @param x Center of circle, which this corner is a part of.
+ * @param y Center of circle, which this corner is a part of.
+ * @param radius radius of the arc
+ * @param rotations number of quarter rotations before starting to paint the arc.
+ */
+ private static void drawCorner(Canvas canvas, Paint paint, Path path, float x, float y,
+ float radius, int rotations) {
+ int saved = canvas.save();
+ canvas.translate(x, y);
+ path.reset();
+ path.arcTo(-radius, -radius, radius, radius, rotations * PERPENDICULAR_ANGLE,
+ PERPENDICULAR_ANGLE, false);
+ path.lineTo(0, 0);
+ path.close();
+ canvas.drawPath(path, paint);
+ canvas.restoreToCount(saved);
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
index 82ae1df..e72a0db 100644
--- a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
@@ -16,12 +16,9 @@
package android.view;
-import com.android.annotations.NonNull;
-import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.resources.Density;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
import android.graphics.Canvas;
@@ -29,8 +26,6 @@
import android.graphics.Path_Delegate;
import android.graphics.Rect;
import android.graphics.Region.Op;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
import android.view.animation.Transformation;
import java.awt.Graphics2D;
@@ -50,33 +45,36 @@
@LayoutlibDelegate
/*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child,
long drawingTime) {
- boolean retVal = thisVG.drawChild_Original(canvas, child, drawingTime);
if (child.getZ() > thisVG.getZ()) {
ViewOutlineProvider outlineProvider = child.getOutlineProvider();
Outline outline = new Outline();
outlineProvider.getOutline(child, outline);
-
+ if (outline.mPath == null && outline.mRect == null) {
+ // Sometimes, the bounds of the background drawable are not set until View.draw()
+ // is called. So, we set the bounds manually and try to get the outline again.
+ child.getBackground().setBounds(0, 0, child.mRight - child.mLeft,
+ child.mBottom - child.mTop);
+ outlineProvider.getOutline(child, outline);
+ }
if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) {
int restoreTo = transformCanvas(thisVG, canvas, child);
drawShadow(thisVG, canvas, child, outline);
canvas.restoreToCount(restoreTo);
}
}
- return retVal;
+ return thisVG.drawChild_Original(canvas, child, drawingTime);
}
private static void drawShadow(ViewGroup parent, Canvas canvas, View child,
Outline outline) {
+ float elevation = getElevation(child, parent);
+ if(outline.mRect != null) {
+ RectShadowPainter.paintShadow(outline, elevation, canvas);
+ return;
+ }
BufferedImage shadow = null;
- int x = 0;
- if (outline.mRect != null) {
- Shadow s = getRectShadow(parent, canvas, child, outline);
- if (s != null) {
- shadow = s.mShadow;
- x = -s.mShadowWidth;
- }
- } else if (outline.mPath != null) {
- shadow = getPathShadow(child, outline, canvas);
+ if (outline.mPath != null) {
+ shadow = getPathShadow(outline, canvas, elevation);
}
if (shadow == null) {
return;
@@ -85,52 +83,17 @@
Density.getEnum(canvas.getDensity()));
Rect clipBounds = canvas.getClipBounds();
Rect newBounds = new Rect(clipBounds);
- newBounds.left = newBounds.left + x;
+ newBounds.inset((int)-elevation, (int)-elevation);
canvas.clipRect(newBounds, Op.REPLACE);
- canvas.drawBitmap(bitmap, x, 0, null);
+ canvas.drawBitmap(bitmap, 0, 0, null);
canvas.clipRect(clipBounds, Op.REPLACE);
}
- private static Shadow getRectShadow(ViewGroup parent, Canvas canvas, View child,
- Outline outline) {
- BufferedImage shadow;
- Rect clipBounds = canvas.getClipBounds();
- if (clipBounds.isEmpty()) {
- return null;
- }
- float height = child.getZ() - parent.getZ();
- // Draw large shadow if difference in z index is more than 10dp
- float largeShadowThreshold = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f,
- getMetrics(child));
- boolean largeShadow = height > largeShadowThreshold;
- int shadowSize = largeShadow ? ShadowPainter.SHADOW_SIZE : ShadowPainter.SMALL_SHADOW_SIZE;
- shadow = new BufferedImage(clipBounds.width() + shadowSize, clipBounds.height(),
- BufferedImage.TYPE_INT_ARGB);
- Graphics2D graphics = shadow.createGraphics();
- Rect rect = outline.mRect;
- if (largeShadow) {
- ShadowPainter.drawRectangleShadow(graphics,
- rect.left + shadowSize, rect.top, rect.width(), rect.height());
- } else {
- ShadowPainter.drawSmallRectangleShadow(graphics,
- rect.left + shadowSize, rect.top, rect.width(), rect.height());
- }
- graphics.dispose();
- return new Shadow(shadow, shadowSize);
+ private static float getElevation(View child, ViewGroup parent) {
+ return child.getZ() - parent.getZ();
}
- @NonNull
- private static DisplayMetrics getMetrics(View view) {
- Context context = view.getContext();
- context = BridgeContext.getBaseContext(context);
- if (context instanceof BridgeContext) {
- return ((BridgeContext) context).getMetrics();
- }
- throw new RuntimeException("View " + view.getClass().getName() + " not created with the " +
- "right context");
- }
-
- private static BufferedImage getPathShadow(View child, Outline outline, Canvas canvas) {
+ private static BufferedImage getPathShadow(Outline outline, Canvas canvas, float elevation) {
Rect clipBounds = canvas.getClipBounds();
if (clipBounds.isEmpty()) {
return null;
@@ -140,7 +103,7 @@
Graphics2D graphics = image.createGraphics();
graphics.draw(Path_Delegate.getDelegate(outline.mPath.mNativePath).getJavaShape());
graphics.dispose();
- return ShadowPainter.createDropShadow(image, ((int) child.getZ()));
+ return ShadowPainter.createDropShadow(image, (int) elevation);
}
// Copied from android.view.View#draw(Canvas, ViewGroup, long) and removed code paths
@@ -194,15 +157,4 @@
}
return restoreTo;
}
-
- private static class Shadow {
- public BufferedImage mShadow;
- public int mShadowWidth;
-
- public Shadow(BufferedImage shadow, int shadowWidth) {
- mShadow = shadow;
- mShadowWidth = shadowWidth;
- }
-
- }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index e1c58fd..8e74ce1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1010,6 +1010,12 @@
}
@Override
+ public int checkSelfPermission(String arg0) {
+ // pass
+ return 0;
+ }
+
+ @Override
public int checkPermission(String arg0, int arg1, int arg2, IBinder arg3) {
// pass
return 0;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 39ebdfc..6282fe5 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -145,4 +145,9 @@
public void boostScreenBrightness(long time) throws RemoteException {
// pass for now.
}
+
+ @Override
+ public boolean isDeviceIdleMode() throws RemoteException {
+ return false;
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index dde041b..9f9b968 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -16,6 +16,8 @@
package com.android.layoutlib.bridge.bars;
+import android.os.Build.VERSION_CODES;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -40,12 +42,12 @@
private static final int BLACK = 0xFF000000;
public static boolean showOnScreenNavBar(int platformVersion) {
- return platformVersion == 0 || platformVersion >= ICE_CREAM_SANDWICH;
+ return isGreaterOrEqual(platformVersion, ICE_CREAM_SANDWICH);
}
public static int getStatusBarColor(int platformVersion) {
// return white for froyo and earlier; black otherwise.
- return platformVersion == 0 || platformVersion >= GINGERBREAD ? BLACK : WHITE;
+ return isGreaterOrEqual(platformVersion, GINGERBREAD) ? BLACK : WHITE;
}
public static List<String> getResourceDirs(int platformVersion) {
@@ -98,7 +100,7 @@
}
public static int getTimeColor(int platformVersion) {
- if (platformVersion == 0 || platformVersion >= KITKAT ||
+ if (isGreaterOrEqual(platformVersion, KITKAT) ||
platformVersion > FROYO && platformVersion < HONEYCOMB) {
// Gingerbread and KitKat onwards.
return WHITE;
@@ -117,4 +119,13 @@
public static String getWifiIconType(int platformVersion) {
return platformVersion == 0 ? "xml" : "png";
}
+
+ /**
+ * Compare simulated platform version and code from {@link VERSION_CODES} to check if
+ * the simulated platform is greater than or equal to the version code.
+ */
+ public static boolean isGreaterOrEqual(int platformVersion, int code) {
+ // simulated platform version = 0 means that we use the latest.
+ return platformVersion == 0 || platformVersion >= code;
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 13ddf07..bc1a41d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -16,6 +16,7 @@
package com.android.layoutlib.bridge.bars;
+import com.android.annotations.NonNull;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.StyleResourceValue;
@@ -26,6 +27,7 @@
import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.resources.Density;
import com.android.resources.LayoutDirection;
+import com.android.resources.ResourceType;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -47,6 +49,8 @@
import java.io.IOException;
import java.io.InputStream;
+import static android.os.Build.VERSION_CODES.LOLLIPOP;
+
/**
* Base "bar" class for the window decor around the the edited layout.
* This is basically an horizontal layout that loads a given layout on creation (it is read
@@ -63,7 +67,7 @@
protected abstract TextView getStyleableTextView();
- protected CustomBar(Context context, int orientation, String layoutPath,
+ protected CustomBar(BridgeContext context, int orientation, String layoutPath,
String name, int simulatedPlatformVersion) throws XmlPullParserException {
super(context);
mSimulatedPlatformVersion = simulatedPlatformVersion;
@@ -197,7 +201,7 @@
ResourceValue textColor = res.findItemInStyle(textStyle, "textColor",
- true /*isFrameworkAttr*/);
+ true);
textColor = res.resolveResValue(textColor);
if (textColor != null) {
ColorStateList stateList = ResourceHelper.getColorStateList(
@@ -210,12 +214,39 @@
}
}
+ /**
+ * Given a theme attribute name, get the color referenced by it. The theme attribute may be
+ * used in a layout like "?attr/foo".
+ * <p/>
+ * Returns 0 if not found.
+ *
+ * @throws NumberFormatException if color resolved to an invalid string.
+ */
+ protected int getThemeAttrColor(@NonNull String attrName, boolean isFramework) {
+ if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) {
+ return 0;
+ }
+ assert mContext instanceof BridgeContext;
+ BridgeContext context = ((BridgeContext) mContext);
+ RenderResources renderResources = context.getRenderResources();
+ // From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
+ ResourceValue resource = renderResources.findItemInTheme(attrName, isFramework);
+ if (resource != null) {
+ // Form @color/bar to the #AARRGGBB
+ resource = renderResources.resolveResValue(resource);
+ }
+ if (resource != null && ResourceType.COLOR.equals(resource.getResourceType())) {
+ return ResourceHelper.getColor(resource.getValue());
+ }
+ return 0;
+ }
+
private ResourceValue getResourceValue(String reference) {
BridgeContext bridgeContext = (BridgeContext) mContext;
RenderResources res = bridgeContext.getRenderResources();
// find the resource
- ResourceValue value = res.findResValue(reference, false /*isFramework*/);
+ ResourceValue value = res.findResValue(reference, false);
// resolve it if needed
return res.resolveResValue(value);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index 283ff57..9450b6c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -16,22 +16,26 @@
package com.android.layoutlib.bridge.bars;
+import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.resources.Density;
import org.xmlpull.v1.XmlPullParserException;
-import android.content.Context;
import android.widget.LinearLayout;
import android.widget.TextView;
public class NavigationBar extends CustomBar {
- public NavigationBar(Context context, Density density, int orientation, boolean isRtl,
+ /** Navigation bar background color attribute name. */
+ private static final String ATTR_COLOR = "navigationBarColor";
+
+ public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
boolean rtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException {
super(context, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml",
simulatedPlatformVersion);
- setBackgroundColor(0xFF000000);
+ int color = getThemeAttrColor(ATTR_COLOR, true);
+ setBackgroundColor(color == 0 ? 0xFF000000 : color);
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index c7c62d6..e5f1f68 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -25,7 +25,6 @@
import org.xmlpull.v1.XmlPullParserException;
-import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.view.View;
@@ -38,20 +37,21 @@
public class StatusBar extends CustomBar {
- private final Context mContext;
private final int mSimulatedPlatformVersion;
+ /** Status bar background color attribute name. */
+ private static final String ATTR_COLOR = "colorPrimaryDark";
- public StatusBar(Context context, Density density, int direction, boolean RtlEnabled,
+ public StatusBar(BridgeContext context, Density density, int direction, boolean RtlEnabled,
int simulatedPlatformVersion) throws XmlPullParserException {
// FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar.
super(context, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml",
simulatedPlatformVersion);
- mContext = context;
mSimulatedPlatformVersion = simulatedPlatformVersion;
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
- setBackgroundColor(Config.getStatusBarColor(simulatedPlatformVersion));
+ int color = getThemeAttrColor(ATTR_COLOR, true);
+ setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
// Cannot access the inside items through id because no R.id values have been
// created for them.
@@ -82,10 +82,8 @@
try {
BridgeXmlBlockParser parser = new BridgeXmlBlockParser(
ParserFactory.create(stream, null), (BridgeContext) mContext, true);
- Drawable drawable = Drawable.createFromXml(mContext.getResources(), parser);
- if (drawable != null) {
- imageView.setImageDrawable(drawable);
- }
+ imageView.setImageDrawable(
+ Drawable.createFromXml(mContext.getResources(), parser));
} catch (XmlPullParserException e) {
Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e,
null);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
index 10f1383..c610601 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
@@ -16,9 +16,10 @@
package com.android.layoutlib.bridge.bars;
+import com.android.layoutlib.bridge.android.BridgeContext;
+
import org.xmlpull.v1.XmlPullParserException;
-import android.content.Context;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -26,7 +27,7 @@
private TextView mTextView;
- public TitleBar(Context context, String label, int simulatedPlatformVersion)
+ public TitleBar(BridgeContext context, String label, int simulatedPlatformVersion)
throws XmlPullParserException {
super(context, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml",
simulatedPlatformVersion);
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle
index 80be12d..491dee8 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle
@@ -3,7 +3,7 @@
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:0.12.+'
+ classpath 'com.android.tools.build:gradle:1.1.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -19,23 +19,21 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 20
- buildToolsVersion '20'
+ compileSdkVersion 21
+ buildToolsVersion '21.1.2'
defaultConfig {
applicationId 'com.android.layoutlib.test.myapplication'
minSdkVersion 19
- targetSdkVersion 20
+ targetSdkVersion 21
versionCode 1
versionName '1.0'
}
buildTypes {
release {
- runProguard false
+ minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
- productFlavors {
- }
}
dependencies {
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
index 2b4f7bf..e29e490 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
new file mode 100644
index 0000000..4ae0da7
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class
new file mode 100644
index 0000000..6729eb4
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
index d252462..985d267 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
index 9bab801..5142ca6 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
index 7ad8605..cb52ba5 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
index e9e0a33..5290cf6 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
index d109302..49b1df6 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
index 816ecc8..85b2029 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
index b034b75..428fdf4 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
index f86b1d3..027d5d3 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
index 8bbae90..c7d64f8 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
index 8af745d..8831b71 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
index 943cdf1..e38f437 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
index 5de946b..3b51ffe 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Tue Mar 17 15:13:06 PDT 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/androidTest/java/com/android/layoulib/test/myapplication/ApplicationTest.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/androidTest/java/com/android/layoulib/test/myapplication/ApplicationTest.java
deleted file mode 100644
index 7304af1..0000000
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/androidTest/java/com/android/layoulib/test/myapplication/ApplicationTest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.android.layoulib.test.myapplication;
-
-import android.app.Application;
-import android.test.ApplicationTestCase;
-
-/**
- * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
- */
-public class ApplicationTest extends ApplicationTestCase<Application> {
- public ApplicationTest() {
- super(Application.class);
- }
-}
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomCalendar.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomCalendar.java
new file mode 100644
index 0000000..80bbaf1
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomCalendar.java
@@ -0,0 +1,31 @@
+package com.android.layoutlib.test.myapplication;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.CalendarView;
+
+public class CustomCalendar extends CalendarView {
+ public CustomCalendar(Context context) {
+ super(context);
+ init();
+ }
+
+ public CustomCalendar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public CustomCalendar(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public CustomCalendar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ private void init() {
+ setDate(871703200000L, false, true);
+ }
+}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomDate.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomDate.java
new file mode 100644
index 0000000..cb750f4
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomDate.java
@@ -0,0 +1,31 @@
+package com.android.layoutlib.test.myapplication;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.DatePicker;
+
+public class CustomDate extends DatePicker {
+ public CustomDate(Context context) {
+ super(context);
+ init();
+ }
+
+ public CustomDate(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public CustomDate(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public CustomDate(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ private void init() {
+ init(2015, 0, 20, null);
+ }
+}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
index b8ec5661..c1f663e 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
@@ -1,17 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical" android:layout_width="match_parent"
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:columnCount="2"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some text"/>
- <DatePicker
+ <Switch
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:checked="true"
+ android:layout_gravity="center"
+ />
+ <com.android.layoutlib.test.myapplication.CustomDate
android:layout_width="100dp"
- android:layout_height="100dp"/>
- <CalendarView
- android:layout_width="100dp"
- android:layout_height="100dp"/>
-</LinearLayout>
\ No newline at end of file
+ android:layout_height="wrap_content"/>
+ <com.android.layoutlib.test.myapplication.CustomCalendar
+ android:layout_width="200dp"
+ android:layout_gravity="center_horizontal"
+ android:layout_height="200dp"/>
+</GridLayout>
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index a86fcdd..ac23564 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -316,7 +316,8 @@
FolderConfiguration config = configGenerator.getFolderConfig();
ResourceResolver resourceResolver =
ResourceResolver.create(mProjectResources.getConfiguredResources(config),
- mFrameworkRepo.getConfiguredResources(config), "Theme.Material", false);
+ mFrameworkRepo.getConfiguredResources(config),
+ "Theme.Material.Light.DarkActionBar", false);
return new SessionParams(
layoutParser,
@@ -336,7 +337,7 @@
@Override
public void warning(String tag, String message, Object data) {
System.out.println("Warning " + tag + ": " + message);
- fail(message);
+ failWithMsg(message);
}
@Override
@@ -346,13 +347,13 @@
if (throwable != null) {
throwable.printStackTrace();
}
- fail(message);
+ failWithMsg(message);
}
@Override
public void error(String tag, String message, Object data) {
System.out.println("Error " + tag + ": " + message);
- fail(message);
+ failWithMsg(message);
}
@Override
@@ -361,7 +362,7 @@
if (throwable != null) {
throwable.printStackTrace();
}
- fail(message);
+ failWithMsg(message);
}
};
}
@@ -376,12 +377,12 @@
if (t != null) {
t.printStackTrace();
}
- fail(String.format(msgFormat, args));
+ failWithMsg(msgFormat, args);
}
@Override
public void warning(String msgFormat, Object... args) {
- fail(String.format(msgFormat, args));
+ failWithMsg(msgFormat, args);
}
@Override
@@ -397,4 +398,8 @@
}
return mLogger;
}
+
+ private static void failWithMsg(String msgFormat, Object... args) {
+ fail(msgFormat == null || args == null ? "" : String.format(msgFormat, args));
+ }
}