Merge "Updating layouts to use frame vs translation and clipping."
diff --git a/api/current.txt b/api/current.txt
index 542695c..49c1272 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -728,6 +728,7 @@
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
field public static final int labelTextSize = 16843317; // 0x1010235
+ field public static final int languageTag = 16844041; // 0x1010509
field public static final int largeHeap = 16843610; // 0x101035a
field public static final int largeScreens = 16843398; // 0x1010286
field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -7780,8 +7781,7 @@
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;
method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
- method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
- method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
+ method public abstract android.content.Context createDeviceEncryptedStorageContext();
method public abstract android.content.Context createDisplayContext(android.view.Display);
method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String[] databaseList();
@@ -7837,8 +7837,7 @@
method public abstract deprecated int getWallpaperDesiredMinimumHeight();
method public abstract deprecated int getWallpaperDesiredMinimumWidth();
method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
- method public abstract boolean isCredentialEncrypted();
- method public abstract boolean isDeviceEncrypted();
+ method public abstract boolean isDeviceEncryptedStorage();
method public boolean isRestricted();
method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -7970,8 +7969,7 @@
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;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
- method public android.content.Context createCredentialEncryptedContext(android.content.Context);
- method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+ method public android.content.Context createDeviceEncryptedStorageContext();
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -8021,8 +8019,7 @@
method public deprecated int getWallpaperDesiredMinimumHeight();
method public deprecated int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
- method public boolean isCredentialEncrypted();
- method public boolean isDeviceEncrypted();
+ method public boolean isDeviceEncryptedStorage();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -11545,6 +11542,7 @@
field public static final int PRIVATE = 34; // 0x22
field public static final int RAW10 = 37; // 0x25
field public static final int RAW12 = 38; // 0x26
+ field public static final int RAW_PRIVATE = 36; // 0x24
field public static final int RAW_SENSOR = 32; // 0x20
field public static final int RGB_565 = 4; // 0x4
field public static final int UNKNOWN = 0; // 0x0
@@ -20736,6 +20734,7 @@
public static class MediaRouter.RouteInfo {
method public android.media.MediaRouter.RouteCategory getCategory();
method public java.lang.CharSequence getDescription();
+ method public int getDeviceType();
method public android.media.MediaRouter.RouteGroup getGroup();
method public android.graphics.drawable.Drawable getIconDrawable();
method public java.lang.CharSequence getName();
@@ -20754,6 +20753,10 @@
method public void requestSetVolume(int);
method public void requestUpdateVolume(int);
method public void setTag(java.lang.Object);
+ field public static final int DEVICE_TYPE_BLUETOOTH = 3; // 0x3
+ field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+ field public static final int DEVICE_TYPE_TV = 1; // 0x1
+ field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
@@ -28095,7 +28098,9 @@
method public boolean isSystemUser();
method public boolean isUserAGoat();
method public boolean isUserRunning(android.os.UserHandle);
+ method public boolean isUserRunningAndLocked();
method public boolean isUserRunningAndLocked(android.os.UserHandle);
+ method public boolean isUserRunningAndUnlocked();
method public boolean isUserRunningAndUnlocked(android.os.UserHandle);
method public boolean isUserRunningOrStopping(android.os.UserHandle);
method public deprecated boolean setRestrictionsChallenge(java.lang.String);
@@ -35218,6 +35223,7 @@
field public static final java.lang.String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+ field public static final java.lang.String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN = "vvm_cellular_data_required";
field public static final java.lang.String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
field public static final java.lang.String KEY_VVM_PORT_NUMBER_INT = "vvm_port_number_int";
field public static final java.lang.String KEY_VVM_TYPE_STRING = "vvm_type_string";
@@ -36248,8 +36254,7 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
- method public android.content.Context createCredentialEncryptedContext(android.content.Context);
- method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+ method public android.content.Context createDeviceEncryptedStorageContext();
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -36298,8 +36303,7 @@
method public int getWallpaperDesiredMinimumHeight();
method public int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
- method public boolean isCredentialEncrypted();
- method public boolean isDeviceEncrypted();
+ method public boolean isDeviceEncryptedStorage();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -38701,9 +38705,9 @@
method public int describeContents();
method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
- method public java.util.Locale getBestMatch(java.lang.String[]);
method public static android.util.LocaleList getDefault();
method public static android.util.LocaleList getEmptyLocaleList();
+ method public java.util.Locale getFirstMatch(java.lang.String[]);
method public java.util.Locale getPrimary();
method public boolean isEmpty();
method public int size();
@@ -40245,6 +40249,7 @@
field public static final int AXIS_RX = 12; // 0xc
field public static final int AXIS_RY = 13; // 0xd
field public static final int AXIS_RZ = 14; // 0xe
+ field public static final int AXIS_SCROLL = 26; // 0x1a
field public static final int AXIS_SIZE = 3; // 0x3
field public static final int AXIS_THROTTLE = 19; // 0x13
field public static final int AXIS_TILT = 25; // 0x19
@@ -40553,7 +40558,6 @@
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
method public void createContextMenu(android.view.ContextMenu);
method public void destroyDrawingCache();
- method public final boolean didLayoutParamsChange();
method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
method public void dispatchConfigurationChanged(android.content.res.Configuration);
method public void dispatchDisplayHint(int);
@@ -40779,7 +40783,6 @@
method public boolean isOpaque();
method protected boolean isPaddingOffsetRequired();
method public boolean isPaddingRelative();
- method public final boolean isPartialLayoutRequested();
method public boolean isPressed();
method public boolean isSaveEnabled();
method public boolean isSaveFromParentEnabled();
@@ -41391,7 +41394,6 @@
method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>);
method protected boolean drawChild(android.graphics.Canvas, android.view.View, long);
method public void endViewTransition(android.view.View);
- method public int findDependentLayoutAxes(android.view.View, int);
method public android.view.View focusSearch(android.view.View, int);
method public void focusableViewAvailable(android.view.View);
method public boolean gatherTransparentRegion(android.graphics.Region);
@@ -41458,8 +41460,6 @@
method public void requestChildFocus(android.view.View, android.view.View);
method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
method public void requestDisallowInterceptTouchEvent(boolean);
- method public void requestLayoutForChild(android.view.View);
- method public void requestPartialLayoutForChild(android.view.View);
method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public void requestTransparentRegion(android.view.View);
method public void scheduleLayoutAnimation();
@@ -41574,7 +41574,6 @@
method public abstract void childHasTransientStateChanged(android.view.View, boolean);
method public abstract void clearChildFocus(android.view.View);
method public abstract void createContextMenu(android.view.ContextMenu);
- method public abstract int findDependentLayoutAxes(android.view.View, int);
method public abstract android.view.View focusSearch(android.view.View, int);
method public abstract void focusableViewAvailable(android.view.View);
method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
@@ -41604,16 +41603,12 @@
method public abstract void requestDisallowInterceptTouchEvent(boolean);
method public abstract void requestFitSystemWindows();
method public abstract void requestLayout();
- method public abstract void requestLayoutForChild(android.view.View);
method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public abstract void requestTransparentRegion(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View, float, float);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
- field public static final int FLAG_LAYOUT_AXIS_ANY = 3; // 0x3
- field public static final int FLAG_LAYOUT_AXIS_HORIZONTAL = 1; // 0x1
- field public static final int FLAG_LAYOUT_AXIS_VERTICAL = 2; // 0x2
}
public class ViewPropertyAnimator {
@@ -41844,6 +41839,7 @@
method public abstract void setContentView(int);
method public abstract void setContentView(android.view.View);
method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+ method public abstract void setDecorCaptionShade(int);
method protected void setDefaultWindowFormat(int);
method public void setDimAmount(float);
method public void setElevation(float);
@@ -41864,6 +41860,8 @@
method public void setMediaController(android.media.session.MediaController);
method public abstract void setNavigationBarColor(int);
method public void setReenterTransition(android.transition.Transition);
+ method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
+ method public final void setRestrictedCaptionAreaListener(android.view.Window.RestrictedCaptionAreaListener);
method public void setReturnTransition(android.transition.Transition);
method public void setSharedElementEnterTransition(android.transition.Transition);
method public void setSharedElementExitTransition(android.transition.Transition);
@@ -41892,6 +41890,9 @@
method public abstract void takeKeyEvents(boolean);
method public abstract void takeSurface(android.view.SurfaceHolder.Callback2);
method public abstract void togglePanel(int, android.view.KeyEvent);
+ field public static final int DECOR_CAPTION_SHADE_AUTO = 0; // 0x0
+ field public static final int DECOR_CAPTION_SHADE_DARK = 2; // 0x2
+ field public static final int DECOR_CAPTION_SHADE_LIGHT = 1; // 0x1
field protected static final deprecated int DEFAULT_FEATURES = 65; // 0x41
field public static final int FEATURE_ACTION_BAR = 8; // 0x8
field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
@@ -41946,6 +41947,10 @@
method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
}
+ public static abstract interface Window.RestrictedCaptionAreaListener {
+ method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
+ }
+
public final class WindowAnimationFrameStats extends android.view.FrameStats implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -43210,7 +43215,8 @@
method public java.lang.String getExtraValue();
method public java.lang.String getExtraValueOf(java.lang.String);
method public int getIconResId();
- method public java.lang.String getLocale();
+ method public java.lang.String getLanguageTag();
+ method public deprecated java.lang.String getLocale();
method public java.lang.String getMode();
method public int getNameResId();
method public boolean isAsciiCapable();
@@ -43225,6 +43231,7 @@
method public android.view.inputmethod.InputMethodSubtype build();
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean);
+ method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int);
@@ -43288,7 +43295,8 @@
method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
method public java.lang.String getExtraValue();
method public java.lang.String getExtraValueOf(java.lang.String);
- method public java.lang.String getLocale();
+ method public java.lang.String getLanguageTag();
+ method public deprecated java.lang.String getLocale();
method public int getNameResId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR;
diff --git a/api/system-current.txt b/api/system-current.txt
index 46e8bc9..0fe65c3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -822,6 +822,7 @@
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
field public static final int labelTextSize = 16843317; // 0x1010235
+ field public static final int languageTag = 16844041; // 0x1010509
field public static final int largeHeap = 16843610; // 0x101035a
field public static final int largeScreens = 16843398; // 0x1010286
field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -8024,8 +8025,8 @@
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;
method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
- method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
- method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
+ method public abstract android.content.Context createCredentialEncryptedStorageContext();
+ method public abstract android.content.Context createDeviceEncryptedStorageContext();
method public abstract android.content.Context createDisplayContext(android.view.Display);
method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String[] databaseList();
@@ -8081,8 +8082,8 @@
method public abstract deprecated int getWallpaperDesiredMinimumHeight();
method public abstract deprecated int getWallpaperDesiredMinimumWidth();
method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
- method public abstract boolean isCredentialEncrypted();
- method public abstract boolean isDeviceEncrypted();
+ method public abstract boolean isCredentialEncryptedStorage();
+ method public abstract boolean isDeviceEncryptedStorage();
method public boolean isRestricted();
method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -8222,8 +8223,8 @@
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;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
- method public android.content.Context createCredentialEncryptedContext(android.content.Context);
- method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+ method public android.content.Context createCredentialEncryptedStorageContext();
+ method public android.content.Context createDeviceEncryptedStorageContext();
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -8273,8 +8274,8 @@
method public deprecated int getWallpaperDesiredMinimumHeight();
method public deprecated int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
- method public boolean isCredentialEncrypted();
- method public boolean isDeviceEncrypted();
+ method public boolean isCredentialEncryptedStorage();
+ method public boolean isDeviceEncryptedStorage();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -11888,6 +11889,7 @@
field public static final int PRIVATE = 34; // 0x22
field public static final int RAW10 = 37; // 0x25
field public static final int RAW12 = 38; // 0x26
+ field public static final int RAW_PRIVATE = 36; // 0x24
field public static final int RAW_SENSOR = 32; // 0x20
field public static final int RGB_565 = 4; // 0x4
field public static final int UNKNOWN = 0; // 0x0
@@ -21415,7 +21417,6 @@
method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler);
method public void setPropertyByteArray(java.lang.String, byte[]);
method public void setPropertyString(java.lang.String, java.lang.String);
- method public void unprovisionDevice();
field public static final int EVENT_KEY_EXPIRED = 3; // 0x3
field public static final int EVENT_KEY_REQUIRED = 2; // 0x2
field public static final deprecated int EVENT_PROVISION_REQUIRED = 1; // 0x1
@@ -22026,6 +22027,7 @@
public static class MediaRouter.RouteInfo {
method public android.media.MediaRouter.RouteCategory getCategory();
method public java.lang.CharSequence getDescription();
+ method public int getDeviceType();
method public android.media.MediaRouter.RouteGroup getGroup();
method public android.graphics.drawable.Drawable getIconDrawable();
method public java.lang.CharSequence getName();
@@ -22044,6 +22046,10 @@
method public void requestSetVolume(int);
method public void requestUpdateVolume(int);
method public void setTag(java.lang.Object);
+ field public static final int DEVICE_TYPE_BLUETOOTH = 3; // 0x3
+ field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+ field public static final int DEVICE_TYPE_TV = 1; // 0x1
+ field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
@@ -30086,7 +30092,9 @@
method public boolean isSystemUser();
method public boolean isUserAGoat();
method public boolean isUserRunning(android.os.UserHandle);
+ method public boolean isUserRunningAndLocked();
method public boolean isUserRunningAndLocked(android.os.UserHandle);
+ method public boolean isUserRunningAndUnlocked();
method public boolean isUserRunningAndUnlocked(android.os.UserHandle);
method public boolean isUserRunningOrStopping(android.os.UserHandle);
method public deprecated boolean setRestrictionsChallenge(java.lang.String);
@@ -37488,6 +37496,7 @@
field public static final java.lang.String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+ field public static final java.lang.String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN = "vvm_cellular_data_required";
field public static final java.lang.String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
field public static final java.lang.String KEY_VVM_PORT_NUMBER_INT = "vvm_port_number_int";
field public static final java.lang.String KEY_VVM_TYPE_STRING = "vvm_type_string";
@@ -38572,8 +38581,8 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
- method public android.content.Context createCredentialEncryptedContext(android.content.Context);
- method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+ method public android.content.Context createCredentialEncryptedStorageContext();
+ method public android.content.Context createDeviceEncryptedStorageContext();
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -38622,8 +38631,8 @@
method public int getWallpaperDesiredMinimumHeight();
method public int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
- method public boolean isCredentialEncrypted();
- method public boolean isDeviceEncrypted();
+ method public boolean isCredentialEncryptedStorage();
+ method public boolean isDeviceEncryptedStorage();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -41034,9 +41043,9 @@
method public int describeContents();
method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
- method public java.util.Locale getBestMatch(java.lang.String[]);
method public static android.util.LocaleList getDefault();
method public static android.util.LocaleList getEmptyLocaleList();
+ method public java.util.Locale getFirstMatch(java.lang.String[]);
method public java.util.Locale getPrimary();
method public boolean isEmpty();
method public int size();
@@ -42578,6 +42587,7 @@
field public static final int AXIS_RX = 12; // 0xc
field public static final int AXIS_RY = 13; // 0xd
field public static final int AXIS_RZ = 14; // 0xe
+ field public static final int AXIS_SCROLL = 26; // 0x1a
field public static final int AXIS_SIZE = 3; // 0x3
field public static final int AXIS_THROTTLE = 19; // 0x13
field public static final int AXIS_TILT = 25; // 0x19
@@ -42886,7 +42896,6 @@
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
method public void createContextMenu(android.view.ContextMenu);
method public void destroyDrawingCache();
- method public final boolean didLayoutParamsChange();
method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
method public void dispatchConfigurationChanged(android.content.res.Configuration);
method public void dispatchDisplayHint(int);
@@ -43112,7 +43121,6 @@
method public boolean isOpaque();
method protected boolean isPaddingOffsetRequired();
method public boolean isPaddingRelative();
- method public final boolean isPartialLayoutRequested();
method public boolean isPressed();
method public boolean isSaveEnabled();
method public boolean isSaveFromParentEnabled();
@@ -43724,7 +43732,6 @@
method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>);
method protected boolean drawChild(android.graphics.Canvas, android.view.View, long);
method public void endViewTransition(android.view.View);
- method public int findDependentLayoutAxes(android.view.View, int);
method public android.view.View focusSearch(android.view.View, int);
method public void focusableViewAvailable(android.view.View);
method public boolean gatherTransparentRegion(android.graphics.Region);
@@ -43791,8 +43798,6 @@
method public void requestChildFocus(android.view.View, android.view.View);
method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
method public void requestDisallowInterceptTouchEvent(boolean);
- method public void requestLayoutForChild(android.view.View);
- method public void requestPartialLayoutForChild(android.view.View);
method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public void requestTransparentRegion(android.view.View);
method public void scheduleLayoutAnimation();
@@ -43907,7 +43912,6 @@
method public abstract void childHasTransientStateChanged(android.view.View, boolean);
method public abstract void clearChildFocus(android.view.View);
method public abstract void createContextMenu(android.view.ContextMenu);
- method public abstract int findDependentLayoutAxes(android.view.View, int);
method public abstract android.view.View focusSearch(android.view.View, int);
method public abstract void focusableViewAvailable(android.view.View);
method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
@@ -43937,16 +43941,12 @@
method public abstract void requestDisallowInterceptTouchEvent(boolean);
method public abstract void requestFitSystemWindows();
method public abstract void requestLayout();
- method public abstract void requestLayoutForChild(android.view.View);
method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public abstract void requestTransparentRegion(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View, float, float);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
- field public static final int FLAG_LAYOUT_AXIS_ANY = 3; // 0x3
- field public static final int FLAG_LAYOUT_AXIS_HORIZONTAL = 1; // 0x1
- field public static final int FLAG_LAYOUT_AXIS_VERTICAL = 2; // 0x2
}
public class ViewPropertyAnimator {
@@ -44177,6 +44177,7 @@
method public abstract void setContentView(int);
method public abstract void setContentView(android.view.View);
method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+ method public abstract void setDecorCaptionShade(int);
method protected void setDefaultWindowFormat(int);
method public void setDimAmount(float);
method public void setDisableWallpaperTouchEvents(boolean);
@@ -44198,6 +44199,8 @@
method public void setMediaController(android.media.session.MediaController);
method public abstract void setNavigationBarColor(int);
method public void setReenterTransition(android.transition.Transition);
+ method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
+ method public final void setRestrictedCaptionAreaListener(android.view.Window.RestrictedCaptionAreaListener);
method public void setReturnTransition(android.transition.Transition);
method public void setSharedElementEnterTransition(android.transition.Transition);
method public void setSharedElementExitTransition(android.transition.Transition);
@@ -44226,6 +44229,9 @@
method public abstract void takeKeyEvents(boolean);
method public abstract void takeSurface(android.view.SurfaceHolder.Callback2);
method public abstract void togglePanel(int, android.view.KeyEvent);
+ field public static final int DECOR_CAPTION_SHADE_AUTO = 0; // 0x0
+ field public static final int DECOR_CAPTION_SHADE_DARK = 2; // 0x2
+ field public static final int DECOR_CAPTION_SHADE_LIGHT = 1; // 0x1
field protected static final deprecated int DEFAULT_FEATURES = 65; // 0x41
field public static final int FEATURE_ACTION_BAR = 8; // 0x8
field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
@@ -44280,6 +44286,10 @@
method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
}
+ public static abstract interface Window.RestrictedCaptionAreaListener {
+ method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
+ }
+
public final class WindowAnimationFrameStats extends android.view.FrameStats implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -45546,7 +45556,8 @@
method public java.lang.String getExtraValue();
method public java.lang.String getExtraValueOf(java.lang.String);
method public int getIconResId();
- method public java.lang.String getLocale();
+ method public java.lang.String getLanguageTag();
+ method public deprecated java.lang.String getLocale();
method public java.lang.String getMode();
method public int getNameResId();
method public boolean isAsciiCapable();
@@ -45561,6 +45572,7 @@
method public android.view.inputmethod.InputMethodSubtype build();
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean);
+ method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int);
@@ -45624,7 +45636,8 @@
method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
method public java.lang.String getExtraValue();
method public java.lang.String getExtraValueOf(java.lang.String);
- method public java.lang.String getLocale();
+ method public java.lang.String getLanguageTag();
+ method public deprecated java.lang.String getLocale();
method public int getNameResId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR;
diff --git a/api/test-current.txt b/api/test-current.txt
index b06d4db..e343cfb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -728,6 +728,7 @@
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
field public static final int labelTextSize = 16843317; // 0x1010235
+ field public static final int languageTag = 16844041; // 0x1010509
field public static final int largeHeap = 16843610; // 0x101035a
field public static final int largeScreens = 16843398; // 0x1010286
field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -7780,8 +7781,7 @@
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;
method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
- method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
- method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
+ method public abstract android.content.Context createDeviceEncryptedStorageContext();
method public abstract android.content.Context createDisplayContext(android.view.Display);
method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String[] databaseList();
@@ -7837,8 +7837,7 @@
method public abstract deprecated int getWallpaperDesiredMinimumHeight();
method public abstract deprecated int getWallpaperDesiredMinimumWidth();
method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
- method public abstract boolean isCredentialEncrypted();
- method public abstract boolean isDeviceEncrypted();
+ method public abstract boolean isDeviceEncryptedStorage();
method public boolean isRestricted();
method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -7970,8 +7969,7 @@
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;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
- method public android.content.Context createCredentialEncryptedContext(android.content.Context);
- method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+ method public android.content.Context createDeviceEncryptedStorageContext();
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -8021,8 +8019,7 @@
method public deprecated int getWallpaperDesiredMinimumHeight();
method public deprecated int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
- method public boolean isCredentialEncrypted();
- method public boolean isDeviceEncrypted();
+ method public boolean isDeviceEncryptedStorage();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -11545,6 +11542,7 @@
field public static final int PRIVATE = 34; // 0x22
field public static final int RAW10 = 37; // 0x25
field public static final int RAW12 = 38; // 0x26
+ field public static final int RAW_PRIVATE = 36; // 0x24
field public static final int RAW_SENSOR = 32; // 0x20
field public static final int RGB_565 = 4; // 0x4
field public static final int UNKNOWN = 0; // 0x0
@@ -20736,6 +20734,7 @@
public static class MediaRouter.RouteInfo {
method public android.media.MediaRouter.RouteCategory getCategory();
method public java.lang.CharSequence getDescription();
+ method public int getDeviceType();
method public android.media.MediaRouter.RouteGroup getGroup();
method public android.graphics.drawable.Drawable getIconDrawable();
method public java.lang.CharSequence getName();
@@ -20754,6 +20753,10 @@
method public void requestSetVolume(int);
method public void requestUpdateVolume(int);
method public void setTag(java.lang.Object);
+ field public static final int DEVICE_TYPE_BLUETOOTH = 3; // 0x3
+ field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+ field public static final int DEVICE_TYPE_TV = 1; // 0x1
+ field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
@@ -28095,7 +28098,9 @@
method public boolean isSystemUser();
method public boolean isUserAGoat();
method public boolean isUserRunning(android.os.UserHandle);
+ method public boolean isUserRunningAndLocked();
method public boolean isUserRunningAndLocked(android.os.UserHandle);
+ method public boolean isUserRunningAndUnlocked();
method public boolean isUserRunningAndUnlocked(android.os.UserHandle);
method public boolean isUserRunningOrStopping(android.os.UserHandle);
method public deprecated boolean setRestrictionsChallenge(java.lang.String);
@@ -35220,6 +35225,7 @@
field public static final java.lang.String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+ field public static final java.lang.String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN = "vvm_cellular_data_required";
field public static final java.lang.String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
field public static final java.lang.String KEY_VVM_PORT_NUMBER_INT = "vvm_port_number_int";
field public static final java.lang.String KEY_VVM_TYPE_STRING = "vvm_type_string";
@@ -36250,8 +36256,7 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
- method public android.content.Context createCredentialEncryptedContext(android.content.Context);
- method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+ method public android.content.Context createDeviceEncryptedStorageContext();
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
@@ -36300,8 +36305,7 @@
method public int getWallpaperDesiredMinimumHeight();
method public int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
- method public boolean isCredentialEncrypted();
- method public boolean isDeviceEncrypted();
+ method public boolean isDeviceEncryptedStorage();
method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -38703,9 +38707,9 @@
method public int describeContents();
method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
- method public java.util.Locale getBestMatch(java.lang.String[]);
method public static android.util.LocaleList getDefault();
method public static android.util.LocaleList getEmptyLocaleList();
+ method public java.util.Locale getFirstMatch(java.lang.String[]);
method public java.util.Locale getPrimary();
method public boolean isEmpty();
method public int size();
@@ -40247,6 +40251,7 @@
field public static final int AXIS_RX = 12; // 0xc
field public static final int AXIS_RY = 13; // 0xd
field public static final int AXIS_RZ = 14; // 0xe
+ field public static final int AXIS_SCROLL = 26; // 0x1a
field public static final int AXIS_SIZE = 3; // 0x3
field public static final int AXIS_THROTTLE = 19; // 0x13
field public static final int AXIS_TILT = 25; // 0x19
@@ -40555,7 +40560,6 @@
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
method public void createContextMenu(android.view.ContextMenu);
method public void destroyDrawingCache();
- method public final boolean didLayoutParamsChange();
method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
method public void dispatchConfigurationChanged(android.content.res.Configuration);
method public void dispatchDisplayHint(int);
@@ -40781,7 +40785,6 @@
method public boolean isOpaque();
method protected boolean isPaddingOffsetRequired();
method public boolean isPaddingRelative();
- method public final boolean isPartialLayoutRequested();
method public boolean isPressed();
method public boolean isSaveEnabled();
method public boolean isSaveFromParentEnabled();
@@ -41393,7 +41396,6 @@
method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>);
method protected boolean drawChild(android.graphics.Canvas, android.view.View, long);
method public void endViewTransition(android.view.View);
- method public int findDependentLayoutAxes(android.view.View, int);
method public android.view.View focusSearch(android.view.View, int);
method public void focusableViewAvailable(android.view.View);
method public boolean gatherTransparentRegion(android.graphics.Region);
@@ -41460,8 +41462,6 @@
method public void requestChildFocus(android.view.View, android.view.View);
method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
method public void requestDisallowInterceptTouchEvent(boolean);
- method public void requestLayoutForChild(android.view.View);
- method public void requestPartialLayoutForChild(android.view.View);
method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public void requestTransparentRegion(android.view.View);
method public void scheduleLayoutAnimation();
@@ -41576,7 +41576,6 @@
method public abstract void childHasTransientStateChanged(android.view.View, boolean);
method public abstract void clearChildFocus(android.view.View);
method public abstract void createContextMenu(android.view.ContextMenu);
- method public abstract int findDependentLayoutAxes(android.view.View, int);
method public abstract android.view.View focusSearch(android.view.View, int);
method public abstract void focusableViewAvailable(android.view.View);
method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
@@ -41606,16 +41605,12 @@
method public abstract void requestDisallowInterceptTouchEvent(boolean);
method public abstract void requestFitSystemWindows();
method public abstract void requestLayout();
- method public abstract void requestLayoutForChild(android.view.View);
method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public abstract void requestTransparentRegion(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View);
method public abstract boolean showContextMenuForChild(android.view.View, float, float);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
- field public static final int FLAG_LAYOUT_AXIS_ANY = 3; // 0x3
- field public static final int FLAG_LAYOUT_AXIS_HORIZONTAL = 1; // 0x1
- field public static final int FLAG_LAYOUT_AXIS_VERTICAL = 2; // 0x2
}
public class ViewPropertyAnimator {
@@ -41846,6 +41841,7 @@
method public abstract void setContentView(int);
method public abstract void setContentView(android.view.View);
method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+ method public abstract void setDecorCaptionShade(int);
method protected void setDefaultWindowFormat(int);
method public void setDimAmount(float);
method public void setElevation(float);
@@ -41866,6 +41862,8 @@
method public void setMediaController(android.media.session.MediaController);
method public abstract void setNavigationBarColor(int);
method public void setReenterTransition(android.transition.Transition);
+ method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
+ method public final void setRestrictedCaptionAreaListener(android.view.Window.RestrictedCaptionAreaListener);
method public void setReturnTransition(android.transition.Transition);
method public void setSharedElementEnterTransition(android.transition.Transition);
method public void setSharedElementExitTransition(android.transition.Transition);
@@ -41894,6 +41892,9 @@
method public abstract void takeKeyEvents(boolean);
method public abstract void takeSurface(android.view.SurfaceHolder.Callback2);
method public abstract void togglePanel(int, android.view.KeyEvent);
+ field public static final int DECOR_CAPTION_SHADE_AUTO = 0; // 0x0
+ field public static final int DECOR_CAPTION_SHADE_DARK = 2; // 0x2
+ field public static final int DECOR_CAPTION_SHADE_LIGHT = 1; // 0x1
field protected static final deprecated int DEFAULT_FEATURES = 65; // 0x41
field public static final int FEATURE_ACTION_BAR = 8; // 0x8
field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
@@ -41948,6 +41949,10 @@
method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
}
+ public static abstract interface Window.RestrictedCaptionAreaListener {
+ method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
+ }
+
public final class WindowAnimationFrameStats extends android.view.FrameStats implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -43212,7 +43217,8 @@
method public java.lang.String getExtraValue();
method public java.lang.String getExtraValueOf(java.lang.String);
method public int getIconResId();
- method public java.lang.String getLocale();
+ method public java.lang.String getLanguageTag();
+ method public deprecated java.lang.String getLocale();
method public java.lang.String getMode();
method public int getNameResId();
method public boolean isAsciiCapable();
@@ -43227,6 +43233,7 @@
method public android.view.inputmethod.InputMethodSubtype build();
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean);
+ method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String);
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int);
@@ -43290,7 +43297,8 @@
method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
method public java.lang.String getExtraValue();
method public java.lang.String getExtraValueOf(java.lang.String);
- method public java.lang.String getLocale();
+ method public java.lang.String getLanguageTag();
+ method public deprecated java.lang.String getLocale();
method public int getNameResId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR;
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 1f378da..175b979 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.SystemApi;
+import android.os.Build;
import android.os.Bundle;
/**
@@ -28,15 +29,28 @@
@SystemApi
public class BroadcastOptions {
private long mTemporaryAppWhitelistDuration;
+ private int mMinManifestReceiverApiLevel = 0;
+ private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
/**
* How long to temporarily put an app on the power whitelist when executing this broadcast
* to it.
- * @hide
*/
- public static final String KEY_TEMPORARY_APP_WHITELIST_DURATION
+ static final String KEY_TEMPORARY_APP_WHITELIST_DURATION
= "android:broadcast.temporaryAppWhitelistDuration";
+ /**
+ * Corresponds to {@link #setMinManifestReceiverApiLevel}.
+ */
+ static final String KEY_MIN_MANIFEST_RECEIVER_API_LEVEL
+ = "android:broadcast.minManifestReceiverApiLevel";
+
+ /**
+ * Corresponds to {@link #setMaxManifestReceiverApiLevel}.
+ */
+ static final String KEY_MAX_MANIFEST_RECEIVER_API_LEVEL
+ = "android:broadcast.maxManifestReceiverApiLevel";
+
public static BroadcastOptions makeBasic() {
BroadcastOptions opts = new BroadcastOptions();
return opts;
@@ -48,6 +62,9 @@
/** @hide */
public BroadcastOptions(Bundle opts) {
mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION);
+ mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0);
+ mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL,
+ Build.VERSION_CODES.CUR_DEVELOPMENT);
}
/**
@@ -68,10 +85,46 @@
}
/**
+ * Set the minimum target API level of receivers of the broadcast. If an application
+ * is targeting an API level less than this, the broadcast will not be delivered to
+ * them. This only applies to receivers declared in the app's AndroidManifest.xml.
+ * @hide
+ */
+ public void setMinManifestReceiverApiLevel(int apiLevel) {
+ mMinManifestReceiverApiLevel = apiLevel;
+ }
+
+ /**
+ * Return {@link #setMinManifestReceiverApiLevel}.
+ * @hide
+ */
+ public int getMinManifestReceiverApiLevel() {
+ return mMinManifestReceiverApiLevel;
+ }
+
+ /**
+ * Set the maximum target API level of receivers of the broadcast. If an application
+ * is targeting an API level greater than this, the broadcast will not be delivered to
+ * them. This only applies to receivers declared in the app's AndroidManifest.xml.
+ * @hide
+ */
+ public void setMaxManifestReceiverApiLevel(int apiLevel) {
+ mMaxManifestReceiverApiLevel = apiLevel;
+ }
+
+ /**
+ * Return {@link #setMaxManifestReceiverApiLevel}.
+ * @hide
+ */
+ public int getMaxManifestReceiverApiLevel() {
+ return mMaxManifestReceiverApiLevel;
+ }
+
+ /**
* Returns the created options as a Bundle, which can be passed to
* {@link android.content.Context#sendBroadcast(android.content.Intent)
* Context.sendBroadcast(Intent)} and related methods.
- * Note that the returned Bundle is still owned by the ActivityOptions
+ * Note that the returned Bundle is still owned by the BroadcastOptions
* object; you must not modify it, but can supply it to the sendBroadcast
* methods that take an options Bundle.
*/
@@ -80,6 +133,12 @@
if (mTemporaryAppWhitelistDuration > 0) {
b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration);
}
+ if (mMinManifestReceiverApiLevel != 0) {
+ b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel);
+ }
+ if (mMaxManifestReceiverApiLevel != Build.VERSION_CODES.CUR_DEVELOPMENT) {
+ b.putInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, mMaxManifestReceiverApiLevel);
+ }
return b.isEmpty() ? null : b;
}
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 36e98f9..569ab11 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -61,6 +61,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.Log;
@@ -1092,7 +1093,23 @@
intent.prepareToLeaveProcess();
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true, user.getIdentifier());
+ Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
+ user.getIdentifier());
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failure from system", e);
+ }
+ }
+
+ @Override
+ @Deprecated
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ intent.prepareToLeaveProcess();
+ ActivityManagerNative.getDefault().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, null,
+ Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true,
+ user.getIdentifier());
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
@@ -1744,17 +1761,21 @@
}
@Override
- public Context createDeviceEncryptedContext(Context context) {
- final int flags = (mFlags & ~Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED)
- | Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED;
+ public Context createDeviceEncryptedStorageContext() {
+ if (!StorageManager.isFileBasedEncryptionEnabled()) {
+ return null;
+ }
+
+ final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE)
+ | Context.CONTEXT_DEVICE_ENCRYPTED_STORAGE;
return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
mUser, flags, mDisplay, null, Display.INVALID_DISPLAY);
}
@Override
- public Context createCredentialEncryptedContext(Context context) {
- final int flags = (mFlags & ~Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED)
- | Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+ public Context createCredentialEncryptedStorageContext() {
+ final int flags = (mFlags & ~Context.CONTEXT_DEVICE_ENCRYPTED_STORAGE)
+ | Context.CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE;
return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
mUser, flags, mDisplay, null, Display.INVALID_DISPLAY);
}
@@ -1765,13 +1786,13 @@
}
@Override
- public boolean isDeviceEncrypted() {
- return (mFlags & Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED) != 0;
+ public boolean isDeviceEncryptedStorage() {
+ return (mFlags & Context.CONTEXT_DEVICE_ENCRYPTED_STORAGE) != 0;
}
@Override
- public boolean isCredentialEncrypted() {
- return (mFlags & Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED) != 0;
+ public boolean isCredentialEncryptedStorage() {
+ return (mFlags & Context.CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE) != 0;
}
@Override
@@ -1781,13 +1802,12 @@
private File getDataDirFile() {
if (mPackageInfo != null) {
- if (isCredentialEncrypted()) {
+ if (isCredentialEncryptedStorage()) {
return mPackageInfo.getCredentialEncryptedDataDirFile();
- } else if (isDeviceEncrypted()) {
+ } else if (isDeviceEncryptedStorage()) {
return mPackageInfo.getDeviceEncryptedDataDirFile();
} else {
- throw new RuntimeException(
- "Storage location is neither credential nor device encrypted");
+ return mPackageInfo.getDataDirFile();
}
}
throw new RuntimeException("Not supported in system context");
@@ -1840,15 +1860,13 @@
// If creator didn't specify which storage to use, use the default
// location for application.
- if ((flags & Context.CONTEXT_STORAGE_MASK) == 0) {
+ if ((flags & (Context.CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE
+ | Context.CONTEXT_DEVICE_ENCRYPTED_STORAGE)) == 0) {
final File dataDir = packageInfo.getDataDirFile();
if (Objects.equals(dataDir, packageInfo.getCredentialEncryptedDataDirFile())) {
- flags |= Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+ flags |= Context.CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE;
} else if (Objects.equals(dataDir, packageInfo.getDeviceEncryptedDataDirFile())) {
- flags |= Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED;
- } else {
- throw new IllegalStateException("Storage location " + dataDir
- + " doesn't match either credential or device encrypted storage");
+ flags |= Context.CONTEXT_DEVICE_ENCRYPTED_STORAGE;
}
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 099a5fe..620ab50 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -46,6 +46,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.NotificationHeaderView;
@@ -64,6 +65,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* A class that represents how a persistent notification is to be presented to
@@ -1693,11 +1695,21 @@
bigContentView = null;
headsUpContentView = null;
mLargeIcon = null;
- if (extras != null) {
- extras.remove(Notification.EXTRA_LARGE_ICON);
- extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
- extras.remove(Notification.EXTRA_PICTURE);
- extras.remove(Notification.EXTRA_BIG_TEXT);
+ if (extras != null && !extras.isEmpty()) {
+ final Set<String> keyset = extras.keySet();
+ final int N = keyset.size();
+ final String[] keys = keyset.toArray(new String[N]);
+ for (int i=0; i<N; i++) {
+ final String key = keys[i];
+ final Object obj = extras.get(key);
+ if (obj != null &&
+ ( obj instanceof Parcelable
+ || obj instanceof Parcelable[]
+ || obj instanceof SparseArray
+ || obj instanceof ArrayList)) {
+ extras.remove(key);
+ }
+ }
}
}
@@ -3318,7 +3330,6 @@
if (mN.color != COLOR_DEFAULT) {
button.setTextColor(R.id.action0, mN.color);
}
- processLegacyAction(action, button);
return button;
}
@@ -3330,14 +3341,6 @@
return getColorUtil() != null;
}
- private void processLegacyAction(Action action, RemoteViews button) {
- if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, action.getIcon())) {
- button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
- mContext.getColor(R.color.notification_action_color_filter),
- PorterDuff.Mode.MULTIPLY);
- }
- }
-
private CharSequence processLegacyText(CharSequence charSequence) {
if (isLegacy()) {
return getColorUtil().invertCharSequenceColors(charSequence);
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
index 7b5a045..1fb7825 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
@@ -172,8 +172,12 @@
}
public String toString() {
+ return toString(false);
+ }
+
+ public String toString(boolean loggable) {
StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: ");
- builder.append(mDevice);
+ builder.append(loggable ? mDevice.hashCode() : mDevice);
builder.append(", mId: ");
builder.append(mId);
builder.append(", mState: ");
@@ -189,7 +193,7 @@
default: builder.append(mState); break;
}
builder.append(", mNumber: ");
- builder.append(mNumber);
+ builder.append(loggable ? mNumber.hashCode() : mNumber);
builder.append(", mMultiParty: ");
builder.append(mMultiParty);
builder.append(", mOutgoing: ");
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1f7fd9d..38a4475 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -53,8 +53,8 @@
import android.os.UserManager;
import android.provider.MediaStore;
import android.util.AttributeSet;
-import android.view.DisplayAdjustments;
import android.view.Display;
+import android.view.DisplayAdjustments;
import android.view.ViewDebug;
import android.view.WindowManager;
@@ -2116,6 +2116,14 @@
UserHandle user);
/**
+ * @hide
+ * This is just here for sending CONNECTIVITY_ACTION.
+ */
+ @Deprecated
+ public abstract void sendStickyBroadcastAsUser(@RequiresPermission Intent intent,
+ UserHandle user, Bundle options);
+
+ /**
* <p>Version of
* {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)}
* that allows you to specify the
@@ -3870,7 +3878,7 @@
*
* @hide
*/
- public static final int CONTEXT_STORAGE_DEVICE_ENCRYPTED = 0x00000008;
+ public static final int CONTEXT_DEVICE_ENCRYPTED_STORAGE = 0x00000008;
/**
* Flag for use with {@link #createPackageContext}: point all file APIs at
@@ -3878,11 +3886,7 @@
*
* @hide
*/
- public static final int CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED = 0x00000010;
-
- /** {@hide} */
- public static final int CONTEXT_STORAGE_MASK = CONTEXT_STORAGE_DEVICE_ENCRYPTED
- | CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+ public static final int CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE = 0x00000010;
/**
* @hide Used to indicate we should tell the activity manager about the process
@@ -3987,19 +3991,23 @@
* Return a new Context object for the current Context but whose storage
* APIs are backed by device-encrypted storage.
* <p>
- * Data stored in device-encrypted storage is typically encrypted with a
- * key tied to the physical device, and they can be accessed whenever the
- * device has booted successfully, both <em>before and after</em> the user
- * has entered their credentials (such as a lock pattern or PIN).
+ * Data stored in device-encrypted storage is typically encrypted with a key
+ * tied to the physical device, and it can be accessed when the device has
+ * booted successfully, both <em>before and after</em> the user has
+ * authenticated with their credentials (such as a lock pattern or PIN).
+ * Because device-encrypted data is available before user authentication,
+ * you should carefully consider what data you store using this Context.
* <p>
* Each call to this method returns a new instance of a Context object;
* Context objects are not shared, however common state (ClassLoader, other
* Resources for the same configuration) may be so the Context itself can be
* fairly lightweight.
*
- * @see #isDeviceEncrypted()
+ * @return new Context or {@code null} if device-encrypted storage is not
+ * supported or available on this device.
+ * @see #isDeviceEncryptedStorage()
*/
- public abstract Context createDeviceEncryptedContext(Context context);
+ public abstract Context createDeviceEncryptedStorageContext();
/**
* Return a new Context object for the current Context but whose storage
@@ -4015,9 +4023,11 @@
* Resources for the same configuration) may be so the Context itself can be
* fairly lightweight.
*
- * @see #isCredentialEncrypted()
+ * @see #isCredentialEncryptedStorage()
+ * @hide
*/
- public abstract Context createCredentialEncryptedContext(Context context);
+ @SystemApi
+ public abstract Context createCredentialEncryptedStorageContext();
/**
* Gets the display adjustments holder for this context. This information
@@ -4045,15 +4055,17 @@
* Indicates if the storage APIs of this Context are backed by
* device-encrypted storage.
*
- * @see #createDeviceEncryptedContext(Context)
+ * @see #createDeviceEncryptedStorageContext()
*/
- public abstract boolean isDeviceEncrypted();
+ public abstract boolean isDeviceEncryptedStorage();
/**
* Indicates if the storage APIs of this Context are backed by
* credential-encrypted storage.
*
- * @see #createCredentialEncryptedContext(Context)
+ * @see #createCredentialEncryptedStorageContext()
+ * @hide
*/
- public abstract boolean isCredentialEncrypted();
+ @SystemApi
+ public abstract boolean isCredentialEncryptedStorage();
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 73d0ddc..1a3d262 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -536,6 +536,13 @@
mBase.sendStickyBroadcastAsUser(intent, user);
}
+ /** @hide */
+ @Override
+ @Deprecated
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+ mBase.sendStickyBroadcastAsUser(intent, user, options);
+ }
+
@Override
@Deprecated
public void sendStickyOrderedBroadcastAsUser(Intent intent,
@@ -792,22 +799,26 @@
}
@Override
- public Context createDeviceEncryptedContext(Context context) {
- return mBase.createDeviceEncryptedContext(context);
+ public Context createDeviceEncryptedStorageContext() {
+ return mBase.createDeviceEncryptedStorageContext();
+ }
+
+ /** {@hide} */
+ @SystemApi
+ @Override
+ public Context createCredentialEncryptedStorageContext() {
+ return mBase.createCredentialEncryptedStorageContext();
}
@Override
- public Context createCredentialEncryptedContext(Context context) {
- return mBase.createCredentialEncryptedContext(context);
+ public boolean isDeviceEncryptedStorage() {
+ return mBase.isDeviceEncryptedStorage();
}
+ /** {@hide} */
+ @SystemApi
@Override
- public boolean isDeviceEncrypted() {
- return mBase.isDeviceEncrypted();
- }
-
- @Override
- public boolean isCredentialEncrypted() {
- return mBase.isCredentialEncrypted();
+ public boolean isCredentialEncryptedStorage() {
+ return mBase.isCredentialEncryptedStorage();
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bc7620c..a27d1cb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4471,6 +4471,22 @@
* @hide
*/
public static final int FLAG_RECEIVER_BOOT_UPGRADE = 0x02000000;
+ /**
+ * If set, the broadcast will always go to manifest receivers in background (cached
+ * or not running) apps, regardless of whether that would be done by default. By
+ * default they will only receive broadcasts if the broadcast has specified an
+ * explicit component or package name.
+ * @hide
+ */
+ public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
+ /**
+ * If set, the broadcast will never go to manifest receivers in background (cached
+ * or not running) apps, regardless of whether that would be done by default. By
+ * default they will receive broadcasts if the broadcast has specified an
+ * explicit component or package name.
+ * @hide
+ */
+ public static final int FLAG_RECEIVER_EXCLUDE_BACKGROUND = 0x00800000;
/**
* @hide Flags that can't be changed with PendingIntent.
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 4a3c59b..0cb0e9f 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -756,13 +756,20 @@
}
public void dump(Printer pw, String prefix) {
+ dump(pw, prefix, DUMP_FLAG_ALL);
+ }
+
+ /** @hide */
+ public void dump(Printer pw, String prefix, int flags) {
super.dumpFront(pw, prefix);
if (permission != null) {
pw.println(prefix + "permission=" + permission);
}
- pw.println(prefix + "taskAffinity=" + taskAffinity
- + " targetActivity=" + targetActivity
- + " persistableMode=" + persistableModeToString());
+ if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ pw.println(prefix + "taskAffinity=" + taskAffinity
+ + " targetActivity=" + targetActivity
+ + " persistableMode=" + persistableModeToString());
+ }
if (launchMode != 0 || flags != 0 || theme != 0) {
pw.println(prefix + "launchMode=" + launchMode
+ " flags=0x" + Integer.toHexString(flags)
@@ -777,14 +784,17 @@
if (uiOptions != 0) {
pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
}
- pw.println(prefix + "resizeable=" + resizeable + " supportsPip=" + supportsPip);
- pw.println(prefix + "lockTaskLaunchMode=" + lockTaskLaunchModeToString(lockTaskLaunchMode));
+ if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ pw.println(prefix + "resizeable=" + resizeable + " supportsPip=" + supportsPip);
+ pw.println(prefix + "lockTaskLaunchMode="
+ + lockTaskLaunchModeToString(lockTaskLaunchMode));
+ }
if (layout != null) {
pw.println(prefix + "initialLayout=" + layout.width + "|"
+ layout.widthFraction + ", " + layout.height + "|"
+ layout.heightFraction + ", " + layout.gravity);
}
- super.dumpBack(pw, prefix);
+ super.dumpBack(pw, prefix, flags);
}
public String toString() {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 4c5e766..0633bff 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -710,21 +710,30 @@
public int installLocation = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
public void dump(Printer pw, String prefix) {
+ dump(pw, prefix, DUMP_FLAG_ALL);
+ }
+
+ /** @hide */
+ public void dump(Printer pw, String prefix, int flags) {
super.dumpFront(pw, prefix);
- if (className != null) {
+ if ((flags&DUMP_FLAG_DETAILS) != 0 && className != null) {
pw.println(prefix + "className=" + className);
}
if (permission != null) {
pw.println(prefix + "permission=" + permission);
}
pw.println(prefix + "processName=" + processName);
- pw.println(prefix + "taskAffinity=" + taskAffinity);
+ if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ pw.println(prefix + "taskAffinity=" + taskAffinity);
+ }
pw.println(prefix + "uid=" + uid + " flags=0x" + Integer.toHexString(flags)
+ " privateFlags=0x" + Integer.toHexString(privateFlags)
+ " theme=0x" + Integer.toHexString(theme));
- pw.println(prefix + "requiresSmallestWidthDp=" + requiresSmallestWidthDp
- + " compatibleWidthLimitDp=" + compatibleWidthLimitDp
- + " largestWidthLimitDp=" + largestWidthLimitDp);
+ if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ pw.println(prefix + "requiresSmallestWidthDp=" + requiresSmallestWidthDp
+ + " compatibleWidthLimitDp=" + compatibleWidthLimitDp
+ + " largestWidthLimitDp=" + largestWidthLimitDp);
+ }
pw.println(prefix + "sourceDir=" + sourceDir);
if (!Objects.equals(sourceDir, publicSourceDir)) {
pw.println(prefix + "publicSourceDir=" + publicSourceDir);
@@ -739,31 +748,36 @@
if (resourceDirs != null) {
pw.println(prefix + "resourceDirs=" + resourceDirs);
}
- if (seinfo != null) {
+ if ((flags&DUMP_FLAG_DETAILS) != 0 && seinfo != null) {
pw.println(prefix + "seinfo=" + seinfo);
}
pw.println(prefix + "dataDir=" + dataDir);
- pw.println(prefix + "deviceEncryptedDataDir=" + deviceEncryptedDataDir);
- pw.println(prefix + "credentialEncryptedDataDir=" + credentialEncryptedDataDir);
- if (sharedLibraryFiles != null) {
- pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
+ if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ pw.println(prefix + "deviceEncryptedDataDir=" + deviceEncryptedDataDir);
+ pw.println(prefix + "credentialEncryptedDataDir=" + credentialEncryptedDataDir);
+ if (sharedLibraryFiles != null) {
+ pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
+ }
}
pw.println(prefix + "enabled=" + enabled + " targetSdkVersion=" + targetSdkVersion
+ " versionCode=" + versionCode);
- if (manageSpaceActivityName != null) {
- pw.println(prefix + "manageSpaceActivityName="+manageSpaceActivityName);
- }
- if (descriptionRes != 0) {
- pw.println(prefix + "description=0x"+Integer.toHexString(descriptionRes));
- }
- if (uiOptions != 0) {
- pw.println(prefix + "uiOptions=0x" + Integer.toHexString(uiOptions));
- }
- pw.println(prefix + "supportsRtl=" + (hasRtlSupport() ? "true" : "false"));
- if (fullBackupContent > 0) {
- pw.println(prefix + "fullBackupContent=@xml/" + fullBackupContent);
- } else {
- pw.println(prefix + "fullBackupContent=" + (fullBackupContent < 0 ? "false" : "true"));
+ if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ if (manageSpaceActivityName != null) {
+ pw.println(prefix + "manageSpaceActivityName=" + manageSpaceActivityName);
+ }
+ if (descriptionRes != 0) {
+ pw.println(prefix + "description=0x" + Integer.toHexString(descriptionRes));
+ }
+ if (uiOptions != 0) {
+ pw.println(prefix + "uiOptions=0x" + Integer.toHexString(uiOptions));
+ }
+ pw.println(prefix + "supportsRtl=" + (hasRtlSupport() ? "true" : "false"));
+ if (fullBackupContent > 0) {
+ pw.println(prefix + "fullBackupContent=@xml/" + fullBackupContent);
+ } else {
+ pw.println(prefix + "fullBackupContent="
+ + (fullBackupContent < 0 ? "false" : "true"));
+ }
}
super.dumpBack(pw, prefix);
}
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index ad7ebe5..a295cc5 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -150,23 +150,32 @@
protected void dumpFront(Printer pw, String prefix) {
super.dumpFront(pw, prefix);
+ if (processName != null && !packageName.equals(processName)) {
+ pw.println(prefix + "processName=" + processName);
+ }
pw.println(prefix + "enabled=" + enabled + " exported=" + exported
- + " encryptionAware=" + encryptionAware + " processName=" + processName);
+ + " encryptionAware=" + encryptionAware);
if (descriptionRes != 0) {
pw.println(prefix + "description=" + descriptionRes);
}
}
-
+
protected void dumpBack(Printer pw, String prefix) {
- if (applicationInfo != null) {
- pw.println(prefix + "ApplicationInfo:");
- applicationInfo.dump(pw, prefix + " ");
- } else {
- pw.println(prefix + "ApplicationInfo: null");
+ dumpBack(pw, prefix, DUMP_FLAG_ALL);
+ }
+
+ void dumpBack(Printer pw, String prefix, int flags) {
+ if ((flags&DUMP_FLAG_APPLICATION) != 0) {
+ if (applicationInfo != null) {
+ pw.println(prefix + "ApplicationInfo:");
+ applicationInfo.dump(pw, prefix + " ", flags);
+ } else {
+ pw.println(prefix + "ApplicationInfo: null");
+ }
}
super.dumpBack(pw, prefix);
}
-
+
public void writeToParcel(Parcel dest, int parcelableFlags) {
super.writeToParcel(dest, parcelableFlags);
if ((parcelableFlags & Parcelable.PARCELABLE_ELIDE_DUPLICATES) != 0) {
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 22a899c..4df83036 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -282,6 +282,21 @@
return null;
}
+ /**
+ * @hide Flag for dumping: include all details.
+ */
+ public static final int DUMP_FLAG_DETAILS = 1<<0;
+
+ /**
+ * @hide Flag for dumping: include nested ApplicationInfo.
+ */
+ public static final int DUMP_FLAG_APPLICATION = 1<<1;
+
+ /**
+ * @hide Flag for dumping: all flags to dump everything.
+ */
+ public static final int DUMP_FLAG_ALL = DUMP_FLAG_DETAILS | DUMP_FLAG_APPLICATION;
+
protected void dumpFront(Printer pw, String prefix) {
if (name != null) {
pw.println(prefix + "name=" + name);
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index f6ea058..7e7b32f 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -115,9 +115,15 @@
}
public void dump(Printer pw, String prefix) {
+ dump(pw, prefix, DUMP_FLAG_ALL);
+ }
+
+ /** @hide */
+ public void dump(Printer pw, String prefix, int flags) {
super.dumpFront(pw, prefix);
pw.println(prefix + "authority=" + authority);
pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
+ super.dumpBack(pw, prefix, flags);
}
public int describeContents() {
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 7bab35c..a5fb451 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -260,6 +260,11 @@
}
public void dump(Printer pw, String prefix) {
+ dump(pw, prefix, PackageItemInfo.DUMP_FLAG_ALL);
+ }
+
+ /** @hide */
+ public void dump(Printer pw, String prefix, int flags) {
if (filter != null) {
pw.println(prefix + "Filter:");
filter.dump(pw, prefix + " ");
@@ -279,16 +284,16 @@
}
if (activityInfo != null) {
pw.println(prefix + "ActivityInfo:");
- activityInfo.dump(pw, prefix + " ");
+ activityInfo.dump(pw, prefix + " ", flags);
} else if (serviceInfo != null) {
pw.println(prefix + "ServiceInfo:");
- serviceInfo.dump(pw, prefix + " ");
+ serviceInfo.dump(pw, prefix + " ", flags);
} else if (providerInfo != null) {
pw.println(prefix + "ProviderInfo:");
- providerInfo.dump(pw, prefix + " ");
+ providerInfo.dump(pw, prefix + " ", flags);
}
}
-
+
public ResolveInfo() {
targetUserId = UserHandle.USER_CURRENT;
}
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 796c2a4..74e5c2a 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -74,9 +74,15 @@
}
public void dump(Printer pw, String prefix) {
+ dump(pw, prefix, DUMP_FLAG_ALL);
+ }
+
+ /** @hide */
+ void dump(Printer pw, String prefix, int flags) {
super.dumpFront(pw, prefix);
pw.println(prefix + "permission=" + permission);
pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
+ super.dumpBack(pw, prefix, flags);
}
public String toString() {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 8a87bff..02d4e59 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -47,6 +47,8 @@
import java.util.LinkedHashMap;
import java.util.List;
+import static android.system.OsConstants.*;
+
/**
* The Camera class is used to set image capture settings, start/stop preview,
* snap pictures, and retrieve frames for encoding for video. This class is a
@@ -173,13 +175,6 @@
private final Object mAutoFocusCallbackLock = new Object();
private static final int NO_ERROR = 0;
- private static final int EACCESS = -13;
- private static final int ENODEV = -19;
- private static final int EBUSY = -16;
- private static final int EINVAL = -22;
- private static final int ENOSYS = -38;
- private static final int EUSERS = -87;
- private static final int EOPNOTSUPP = -95;
/**
* Broadcast Action: A new picture is taken by the camera, and the entry of
@@ -415,30 +410,28 @@
private Camera(int cameraId, int halVersion) {
int err = cameraInitVersion(cameraId, halVersion);
if (checkInitErrors(err)) {
- switch(err) {
- case EACCESS:
- throw new RuntimeException("Fail to connect to camera service");
- case ENODEV:
- throw new RuntimeException("Camera initialization failed");
- case ENOSYS:
- throw new RuntimeException("Camera initialization failed because some methods"
- + " are not implemented");
- case EOPNOTSUPP:
- throw new RuntimeException("Camera initialization failed because the hal"
- + " version is not supported by this device");
- case EINVAL:
- throw new RuntimeException("Camera initialization failed because the input"
- + " arugments are invalid");
- case EBUSY:
- throw new RuntimeException("Camera initialization failed because the camera"
- + " device was already opened");
- case EUSERS:
- throw new RuntimeException("Camera initialization failed because the max"
- + " number of camera devices were already opened");
- default:
- // Should never hit this.
- throw new RuntimeException("Unknown camera error");
+ if (err == -EACCES) {
+ throw new RuntimeException("Fail to connect to camera service");
+ } else if (err == -ENODEV) {
+ throw new RuntimeException("Camera initialization failed");
+ } else if (err == -ENOSYS) {
+ throw new RuntimeException("Camera initialization failed because some methods"
+ + " are not implemented");
+ } else if (err == -EOPNOTSUPP) {
+ throw new RuntimeException("Camera initialization failed because the hal"
+ + " version is not supported by this device");
+ } else if (err == -EINVAL) {
+ throw new RuntimeException("Camera initialization failed because the input"
+ + " arugments are invalid");
+ } else if (err == -EBUSY) {
+ throw new RuntimeException("Camera initialization failed because the camera"
+ + " device was already opened");
+ } else if (err == -EUSERS) {
+ throw new RuntimeException("Camera initialization failed because the max"
+ + " number of camera devices were already opened");
}
+ // Should never hit this.
+ throw new RuntimeException("Unknown camera error");
}
}
@@ -490,15 +483,13 @@
Camera(int cameraId) {
int err = cameraInitNormal(cameraId);
if (checkInitErrors(err)) {
- switch(err) {
- case EACCESS:
- throw new RuntimeException("Fail to connect to camera service");
- case ENODEV:
- throw new RuntimeException("Camera initialization failed");
- default:
- // Should never hit this.
- throw new RuntimeException("Unknown camera error");
+ if (err == -EACCES) {
+ throw new RuntimeException("Fail to connect to camera service");
+ } else if (err == -ENODEV) {
+ throw new RuntimeException("Camera initialization failed");
}
+ // Should never hit this.
+ throw new RuntimeException("Unknown camera error");
}
}
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 6b8e113..798c941 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -43,6 +43,9 @@
import java.util.ArrayList;
import java.util.List;
+import static android.system.OsConstants.EACCES;
+import static android.system.OsConstants.ENODEV;
+
/**
* Compatibility implementation of the Camera2 API binder interface.
*
@@ -88,6 +91,14 @@
mSurfaceIdCounter = 0;
}
+ private static int translateErrorsFromCamera1(int errorCode) {
+ if (errorCode == -EACCES) {
+ return CameraBinderDecorator.PERMISSION_DENIED;
+ }
+
+ return errorCode;
+ }
+
/**
* Create a separate looper/thread for the camera to run on; open the camera.
*
@@ -382,7 +393,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot submit request, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
synchronized(mConfigureLock) {
@@ -402,7 +413,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot submit request list, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
synchronized(mConfigureLock) {
@@ -421,7 +432,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot cancel request, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
synchronized(mConfigureLock) {
@@ -442,7 +453,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot begin configure, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
synchronized(mConfigureLock) {
@@ -462,7 +473,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot end configure, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
ArrayList<Surface> surfaces = null;
@@ -490,7 +501,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot delete stream, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
synchronized(mConfigureLock) {
@@ -515,7 +526,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot create stream, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
synchronized(mConfigureLock) {
@@ -552,7 +563,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot create default request, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
CameraMetadataNative template;
@@ -585,7 +596,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot wait until idle, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
synchronized(mConfigureLock) {
@@ -605,7 +616,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot flush, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
synchronized(mConfigureLock) {
@@ -627,7 +638,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot prepare stream, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
// LEGACY doesn't support actual prepare, just signal success right away
@@ -647,7 +658,7 @@
}
if (mLegacyDevice.isClosed()) {
Log.e(TAG, "Cannot tear down stream, device has been closed.");
- return CameraBinderDecorator.ENODEV;
+ return -ENODEV;
}
// LEGACY doesn't support actual teardown, so just a no-op
diff --git a/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java b/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java
index 4b7cfbf..4501e81 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java
@@ -19,6 +19,8 @@
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.util.AndroidException;
+import static android.system.OsConstants.ENODEV;
+
/**
* Utility class containing exception handling used solely by the compatibility mode shim.
*/
@@ -51,18 +53,15 @@
* exceptions.</p>
*
* @param errorFlag error to throw as an exception.
- * @throws {@link BufferQueueAbandonedException} for {@link CameraBinderDecorator#ENODEV}.
+ * @throws {@link BufferQueueAbandonedException} for -ENODEV.
* @throws {@link UnsupportedOperationException} for an unknown negative error code.
* @return {@code errorFlag} if the value was non-negative, throws otherwise.
*/
public static int throwOnError(int errorFlag) throws BufferQueueAbandonedException {
- switch (errorFlag) {
- case CameraBinderDecorator.NO_ERROR: {
- return CameraBinderDecorator.NO_ERROR;
- }
- case CameraBinderDecorator.BAD_VALUE: {
- throw new BufferQueueAbandonedException();
- }
+ if (errorFlag == CameraBinderDecorator.NO_ERROR) {
+ return CameraBinderDecorator.NO_ERROR;
+ } else if (errorFlag == -ENODEV) {
+ throw new BufferQueueAbandonedException();
}
if (errorFlag < 0) {
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index b8d6960..8be49e8 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -834,6 +834,7 @@
* <ul>
* <li>{@link ImageFormat#JPEG JPEG}
* <li>{@link ImageFormat#RAW_SENSOR RAW16}
+ * <li>{@link ImageFormat#RAW_PRIVATE RAW_PRIVATE}
* </ul>
* </p>
*
@@ -1328,9 +1329,7 @@
SparseIntArray map = getFormatsMap(output);
for (int j = 0; j < map.size(); j++) {
int format = map.keyAt(j);
- if (format != HAL_PIXEL_FORMAT_RAW_OPAQUE) {
- formats[i++] = imageFormatToPublic(format);
- }
+ formats[i++] = imageFormatToPublic(format);
}
if (output) {
for (int j = 0; j < mDepthOutputFormats.size(); j++) {
@@ -1392,9 +1391,6 @@
private int getPublicFormatCount(boolean output) {
SparseIntArray formatsMap = getFormatsMap(output);
int size = formatsMap.size();
- if (formatsMap.indexOfKey(HAL_PIXEL_FORMAT_RAW_OPAQUE) >= 0) {
- size -= 1;
- }
if (output) {
size += mDepthOutputFormats.size();
}
@@ -1603,6 +1599,8 @@
return "Y16";
case ImageFormat.RAW_SENSOR:
return "RAW_SENSOR";
+ case ImageFormat.RAW_PRIVATE:
+ return "RAW_PRIVATE";
case ImageFormat.RAW10:
return "RAW10";
case ImageFormat.DEPTH16:
diff --git a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
index 1aee794..162edc9 100644
--- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
+++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
@@ -22,6 +22,7 @@
import static android.hardware.camera2.CameraAccessException.CAMERA_ERROR;
import static android.hardware.camera2.CameraAccessException.MAX_CAMERAS_IN_USE;
import static android.hardware.camera2.CameraAccessException.CAMERA_DEPRECATED_HAL;
+import static android.system.OsConstants.*;
import android.os.DeadObjectException;
import android.os.RemoteException;
@@ -37,12 +38,12 @@
public class CameraBinderDecorator {
public static final int NO_ERROR = 0;
- public static final int PERMISSION_DENIED = -1;
- public static final int ALREADY_EXISTS = -17;
- public static final int BAD_VALUE = -22;
- public static final int DEAD_OBJECT = -32;
- public static final int INVALID_OPERATION = -38;
- public static final int TIMED_OUT = -110;
+ public static final int PERMISSION_DENIED = -EPERM;
+ public static final int ALREADY_EXISTS = -EEXIST;
+ public static final int BAD_VALUE = -EINVAL;
+ public static final int DEAD_OBJECT = -ENOSYS;
+ public static final int INVALID_OPERATION = -EPIPE;
+ public static final int TIMED_OUT = -ETIMEDOUT;
/**
* TODO: add as error codes in Errors.h
@@ -52,12 +53,6 @@
* - NOT_SUPPORTED
* - TOO_MANY_USERS
*/
- public static final int EACCES = -13;
- public static final int EBUSY = -16;
- public static final int ENODEV = -19;
- public static final int EOPNOTSUPP = -95;
- public static final int EUSERS = -87;
-
static class CameraBinderDecoratorListener implements Decorator.DecoratorListener {
@@ -101,35 +96,34 @@
* @param errorFlag error to throw as an exception.
*/
public static void throwOnError(int errorFlag) {
- switch (errorFlag) {
- case NO_ERROR:
- return;
- case PERMISSION_DENIED:
- throw new SecurityException("Lacking privileges to access camera service");
- case ALREADY_EXISTS:
- // This should be handled at the call site. Typically this isn't bad,
- // just means we tried to do an operation that already completed.
- return;
- case BAD_VALUE:
- throw new IllegalArgumentException("Bad argument passed to camera service");
- case DEAD_OBJECT:
- throw new CameraRuntimeException(CAMERA_DISCONNECTED);
- case TIMED_OUT:
- throw new CameraRuntimeException(CAMERA_ERROR,
- "Operation timed out in camera service");
- case EACCES:
- throw new CameraRuntimeException(CAMERA_DISABLED);
- case EBUSY:
- throw new CameraRuntimeException(CAMERA_IN_USE);
- case EUSERS:
- throw new CameraRuntimeException(MAX_CAMERAS_IN_USE);
- case ENODEV:
- throw new CameraRuntimeException(CAMERA_DISCONNECTED);
- case EOPNOTSUPP:
- throw new CameraRuntimeException(CAMERA_DEPRECATED_HAL);
- case INVALID_OPERATION:
- throw new CameraRuntimeException(CAMERA_ERROR,
- "Illegal state encountered in camera service.");
+ if (errorFlag == NO_ERROR) {
+ return;
+ } else if (errorFlag == PERMISSION_DENIED) {
+ throw new SecurityException("Lacking privileges to access camera service");
+ } else if (errorFlag == ALREADY_EXISTS) {
+ // This should be handled at the call site. Typically this isn't bad,
+ // just means we tried to do an operation that already completed.
+ return;
+ } else if (errorFlag == BAD_VALUE) {
+ throw new IllegalArgumentException("Bad argument passed to camera service");
+ } else if (errorFlag == DEAD_OBJECT) {
+ throw new CameraRuntimeException(CAMERA_DISCONNECTED);
+ } else if (errorFlag == TIMED_OUT) {
+ throw new CameraRuntimeException(CAMERA_ERROR,
+ "Operation timed out in camera service");
+ } else if (errorFlag == -EACCES) {
+ throw new CameraRuntimeException(CAMERA_DISABLED);
+ } else if (errorFlag == -EBUSY) {
+ throw new CameraRuntimeException(CAMERA_IN_USE);
+ } else if (errorFlag == -EUSERS) {
+ throw new CameraRuntimeException(MAX_CAMERAS_IN_USE);
+ } else if (errorFlag == -ENODEV) {
+ throw new CameraRuntimeException(CAMERA_DISCONNECTED);
+ } else if (errorFlag == -EOPNOTSUPP) {
+ throw new CameraRuntimeException(CAMERA_DEPRECATED_HAL);
+ } else if (errorFlag == INVALID_OPERATION) {
+ throw new CameraRuntimeException(CAMERA_ERROR,
+ "Illegal state encountered in camera service.");
}
/**
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index c85e97b..d490409 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -25,6 +25,8 @@
import java.util.Arrays;
import java.util.UUID;
+import static android.system.OsConstants.*;
+
/**
* The SoundTrigger class provides access via JNI to the native service managing
* the sound trigger HAL.
@@ -35,11 +37,11 @@
public static final int STATUS_OK = 0;
public static final int STATUS_ERROR = Integer.MIN_VALUE;
- public static final int STATUS_PERMISSION_DENIED = -1;
- public static final int STATUS_NO_INIT = -19;
- public static final int STATUS_BAD_VALUE = -22;
- public static final int STATUS_DEAD_OBJECT = -32;
- public static final int STATUS_INVALID_OPERATION = -38;
+ public static final int STATUS_PERMISSION_DENIED = -EPERM;
+ public static final int STATUS_NO_INIT = -ENODEV;
+ public static final int STATUS_BAD_VALUE = -EINVAL;
+ public static final int STATUS_DEAD_OBJECT = -EPIPE;
+ public static final int STATUS_INVALID_OPERATION = -ENOSYS;
/*****************************************************************************
* A ModuleProperties describes a given sound trigger hardware module
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 1346a39..00a874b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -776,6 +776,16 @@
}
/**
+ * Return whether the calling user is running in a "locked" state. A user is
+ * unlocked only after they've entered their credentials (such as a lock
+ * pattern or PIN), and credential-encrypted private app data storage is
+ * available.
+ */
+ public boolean isUserRunningAndLocked() {
+ return isUserRunningAndLocked(Process.myUserHandle());
+ }
+
+ /**
* Return whether the given user is running in a "locked" state. A user
* is unlocked only after they've entered their credentials (such as a lock
* pattern or PIN), and credential-encrypted private app data storage is
@@ -793,6 +803,16 @@
}
/**
+ * Return whether the calling user is running in an "unlocked" state. A user
+ * is unlocked only after they've entered their credentials (such as a lock
+ * pattern or PIN), and credential-encrypted private app data storage is
+ * available.
+ */
+ public boolean isUserRunningAndUnlocked() {
+ return isUserRunningAndUnlocked(Process.myUserHandle());
+ }
+
+ /**
* Return whether the given user is running in an "unlocked" state. A user
* is unlocked only after they've entered their credentials (such as a lock
* pattern or PIN), and credential-encrypted private app data storage is
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 521aa3c..74fd8cd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3323,6 +3323,7 @@
PUBLIC_SETTINGS.add(SOUND_EFFECTS_ENABLED);
PUBLIC_SETTINGS.add(HAPTIC_FEEDBACK_ENABLED);
PUBLIC_SETTINGS.add(SHOW_WEB_SUGGESTIONS);
+ PUBLIC_SETTINGS.add(VIBRATE_WHEN_RINGING);
}
/**
@@ -3344,7 +3345,6 @@
PRIVATE_SETTINGS.add(VIBRATE_IN_SILENT);
PRIVATE_SETTINGS.add(MEDIA_BUTTON_RECEIVER);
PRIVATE_SETTINGS.add(HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY);
- PRIVATE_SETTINGS.add(VIBRATE_WHEN_RINGING);
PRIVATE_SETTINGS.add(DTMF_TONE_TYPE_WHEN_DIALING);
PRIVATE_SETTINGS.add(HEARING_AID);
PRIVATE_SETTINGS.add(TTY_MODE);
@@ -4912,6 +4912,15 @@
"accessibility_display_daltonizer";
/**
+ * Float list that specifies the color matrix to apply to
+ * the display. Valid values are defined in AccessibilityManager.
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_DISPLAY_COLOR_MATRIX =
+ "accessibility_display_color_matrix";
+
+ /**
* Setting that specifies whether automatic click when the mouse pointer stops moving is
* enabled.
*
diff --git a/core/java/android/security/FrameworkNetworkSecurityPolicy.java b/core/java/android/security/FrameworkNetworkSecurityPolicy.java
new file mode 100644
index 0000000..e3dac5e
--- /dev/null
+++ b/core/java/android/security/FrameworkNetworkSecurityPolicy.java
@@ -0,0 +1,35 @@
+/**
+ * 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.security;
+
+/**
+ * Android framework's implementation of {@link libcore.net.NetworkSecurityPolicy}.
+ *
+ * @hide
+ */
+public class FrameworkNetworkSecurityPolicy extends libcore.net.NetworkSecurityPolicy {
+ private final boolean mCleartextTrafficPermitted;
+
+ public FrameworkNetworkSecurityPolicy(boolean cleartextTrafficPermitted) {
+ mCleartextTrafficPermitted = cleartextTrafficPermitted;
+ }
+
+ @Override
+ public boolean isCleartextTrafficPermitted() {
+ return mCleartextTrafficPermitted;
+ }
+}
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index 7e87717..7991d37 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -60,7 +60,7 @@
* <p>NOTE: {@link android.webkit.WebView} does not honor this flag.
*/
public boolean isCleartextTrafficPermitted() {
- return libcore.net.NetworkSecurityPolicy.isCleartextTrafficPermitted();
+ return libcore.net.NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted();
}
/**
@@ -72,6 +72,7 @@
* @hide
*/
public void setCleartextTrafficPermitted(boolean permitted) {
- libcore.net.NetworkSecurityPolicy.setCleartextTrafficPermitted(permitted);
+ FrameworkNetworkSecurityPolicy policy = new FrameworkNetworkSecurityPolicy(permitted);
+ libcore.net.NetworkSecurityPolicy.setInstance(policy);
}
}
diff --git a/core/java/android/security/net/config/CertificateSource.java b/core/java/android/security/net/config/CertificateSource.java
index 2b7829e..7e3601e 100644
--- a/core/java/android/security/net/config/CertificateSource.java
+++ b/core/java/android/security/net/config/CertificateSource.java
@@ -23,4 +23,5 @@
public interface CertificateSource {
Set<X509Certificate> getCertificates();
X509Certificate findBySubjectAndPublicKey(X509Certificate cert);
+ X509Certificate findByIssuerAndSignature(X509Certificate cert);
}
diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java
index 1d15e19..ff728ef 100644
--- a/core/java/android/security/net/config/CertificatesEntryRef.java
+++ b/core/java/android/security/net/config/CertificatesEntryRef.java
@@ -51,4 +51,13 @@
return new TrustAnchor(foundCert, mOverridesPins);
}
+
+ public TrustAnchor findByIssuerAndSignature(X509Certificate cert) {
+ X509Certificate foundCert = mSource.findByIssuerAndSignature(cert);
+ if (foundCert == null) {
+ return null;
+ }
+
+ return new TrustAnchor(foundCert, mOverridesPins);
+ }
}
diff --git a/core/java/android/security/net/config/DirectoryCertificateSource.java b/core/java/android/security/net/config/DirectoryCertificateSource.java
index a261e06..bf29efa 100644
--- a/core/java/android/security/net/config/DirectoryCertificateSource.java
+++ b/core/java/android/security/net/config/DirectoryCertificateSource.java
@@ -95,6 +95,21 @@
});
}
+ @Override
+ public X509Certificate findByIssuerAndSignature(final X509Certificate cert) {
+ return findCert(cert.getIssuerX500Principal(), new CertSelector() {
+ @Override
+ public boolean match(X509Certificate ca) {
+ try {
+ cert.verify(ca.getPublicKey());
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+ });
+ }
+
private static interface CertSelector {
boolean match(X509Certificate cert);
}
diff --git a/core/java/android/security/net/config/KeyStoreCertificateSource.java b/core/java/android/security/net/config/KeyStoreCertificateSource.java
index 7a01a64..b6105cd 100644
--- a/core/java/android/security/net/config/KeyStoreCertificateSource.java
+++ b/core/java/android/security/net/config/KeyStoreCertificateSource.java
@@ -80,4 +80,14 @@
}
return anchor.getTrustedCert();
}
+
+ @Override
+ public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
+ ensureInitialized();
+ java.security.cert.TrustAnchor anchor = mIndex.findByIssuerAndSignature(cert);
+ if (anchor == null) {
+ return null;
+ }
+ return anchor.getTrustedCert();
+ }
}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 2ab07b5..0a2edff 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -134,6 +134,17 @@
return null;
}
+ /** @hide */
+ public TrustAnchor findTrustAnchorByIssuerAndSignature(X509Certificate cert) {
+ for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
+ TrustAnchor anchor = ref.findByIssuerAndSignature(cert);
+ if (anchor != null) {
+ return anchor;
+ }
+ }
+ return null;
+ }
+
/**
* Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
*
diff --git a/core/java/android/security/net/config/NetworkSecurityTrustManager.java b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
index 6013c1e..982ed68 100644
--- a/core/java/android/security/net/config/NetworkSecurityTrustManager.java
+++ b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
@@ -46,17 +46,13 @@
throw new NullPointerException("config must not be null");
}
mNetworkSecurityConfig = config;
- // TODO: Create our own better KeyStoreImpl
try {
+ TrustedCertificateStoreAdapter certStore = new TrustedCertificateStoreAdapter(config);
+ // Provide an empty KeyStore since TrustManagerImpl doesn't support null KeyStores.
+ // TrustManagerImpl will use certStore to lookup certificates.
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
store.load(null);
- int certNum = 0;
- for (TrustAnchor anchor : mNetworkSecurityConfig.getTrustAnchors()) {
- store.setEntry(String.valueOf(certNum++),
- new KeyStore.TrustedCertificateEntry(anchor.certificate),
- null);
- }
- mDelegate = new TrustManagerImpl(store);
+ mDelegate = new TrustManagerImpl(store, null, certStore);
} catch (GeneralSecurityException | IOException e) {
throw new RuntimeException(e);
}
diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java
index b007f8f..e489c2c 100644
--- a/core/java/android/security/net/config/ResourceCertificateSource.java
+++ b/core/java/android/security/net/config/ResourceCertificateSource.java
@@ -90,4 +90,14 @@
}
return anchor.getTrustedCert();
}
+
+ @Override
+ public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
+ ensureInitialized();
+ java.security.cert.TrustAnchor anchor = mIndex.findByIssuerAndSignature(cert);
+ if (anchor == null) {
+ return null;
+ }
+ return anchor.getTrustedCert();
+ }
}
diff --git a/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java b/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
new file mode 100644
index 0000000..4a90f82
--- /dev/null
+++ b/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
@@ -0,0 +1,116 @@
+/*
+ * 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.security.net.config;
+
+import java.io.File;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Set;
+
+import com.android.org.conscrypt.TrustedCertificateStore;
+
+/** @hide */
+public class TrustedCertificateStoreAdapter extends TrustedCertificateStore {
+ private final NetworkSecurityConfig mConfig;
+
+ public TrustedCertificateStoreAdapter(NetworkSecurityConfig config) {
+ mConfig = config;
+ }
+
+ @Override
+ public X509Certificate findIssuer(X509Certificate cert) {
+ TrustAnchor anchor = mConfig.findTrustAnchorByIssuerAndSignature(cert);
+ if (anchor == null) {
+ return null;
+ }
+ return anchor.certificate;
+ }
+
+ @Override
+ public X509Certificate getTrustAnchor(X509Certificate cert) {
+ TrustAnchor anchor = mConfig.findTrustAnchorBySubjectAndPublicKey(cert);
+ if (anchor == null) {
+ return null;
+ }
+ return anchor.certificate;
+ }
+
+ @Override
+ public boolean isUserAddedCertificate(X509Certificate cert) {
+ // isUserAddedCertificate is used only for pinning overrides, so use overridesPins here.
+ TrustAnchor anchor = mConfig.findTrustAnchorBySubjectAndPublicKey(cert);
+ if (anchor == null) {
+ return false;
+ }
+ return anchor.overridesPins;
+ }
+
+ @Override
+ public File getCertificateFile(File dir, X509Certificate x) {
+ // getCertificateFile is only used for tests, do not support it here.
+ throw new UnsupportedOperationException();
+ }
+
+ // The methods below are exposed in TrustedCertificateStore but not used by conscrypt, do not
+ // support them.
+
+ @Override
+ public Certificate getCertificate(String alias) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Certificate getCertificate(String alias, boolean includeDeletedSystem) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Date getCreationDate(String alias) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<String> aliases() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<String> userAliases() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<String> allSystemAliases() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean containsAlias(String alias) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getCertificateAlias(Certificate c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getCertificateAlias(Certificate c, boolean includeDeletedSystem) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 2459cfa..57fe131 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -20,7 +20,6 @@
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
-import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -222,34 +221,26 @@
return lineEnd(widget, buffer);
}
- private static boolean isTouchSelecting(boolean isMouse, Spannable buffer) {
- return isMouse ? Touch.isActivelySelecting(buffer) : isSelecting(buffer);
- }
-
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
int initialScrollX = -1;
int initialScrollY = -1;
final int action = event.getAction();
- final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
if (action == MotionEvent.ACTION_UP) {
initialScrollX = Touch.getInitialScrollX(widget, buffer);
initialScrollY = Touch.getInitialScrollY(widget, buffer);
}
- boolean wasTouchSelecting = isTouchSelecting(isMouse, buffer);
+ boolean wasTouchSelecting = isSelecting(buffer);
boolean handled = Touch.onTouchEvent(widget, buffer, event);
- if (widget.didTouchFocusSelect() && !isMouse) {
+ if (widget.didTouchFocusSelect()) {
return handled;
}
if (action == MotionEvent.ACTION_DOWN) {
- // Capture the mouse pointer down location to ensure selection starts
- // right under the mouse (and is not influenced by cursor location).
- // The code below needs to run for mouse events.
// For touch events, the code should run only when selection is active.
- if (isMouse || isTouchSelecting(isMouse, buffer)) {
+ if (isSelecting(buffer)) {
if (!widget.isFocused()) {
if (!widget.requestFocus()) {
return handled;
@@ -265,15 +256,8 @@
}
} else if (widget.isFocused()) {
if (action == MotionEvent.ACTION_MOVE) {
- // Cursor can be active at any location in the text while mouse pointer can start
- // selection from a totally different location. Use LAST_TAP_DOWN span to ensure
- // text selection will start from mouse pointer location.
- final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN);
- if (isMouse && Touch.isSelectionStarted(buffer)) {
- Selection.setSelection(buffer, startOffset);
- }
-
- if (isTouchSelecting(isMouse, buffer) && handled) {
+ if (isSelecting(buffer) && handled) {
+ final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN);
// Before selecting, make sure we've moved out of the "slop".
// handled will be true, if we're in select mode AND we're
// OUT of the slop
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index fee7377..d9068dc 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -119,18 +119,12 @@
ds = buffer.getSpans(0, buffer.length(), DragState.class);
if (ds.length > 0) {
- ds[0].mIsSelectionStarted = false;
-
if (ds[0].mFarEnough == false) {
int slop = ViewConfiguration.get(widget.getContext()).getScaledTouchSlop();
if (Math.abs(event.getX() - ds[0].mX) >= slop ||
Math.abs(event.getY() - ds[0].mY) >= slop) {
ds[0].mFarEnough = true;
- if (event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
- ds[0].mIsActivelySelecting = true;
- ds[0].mIsSelectionStarted = true;
- }
}
}
@@ -142,13 +136,9 @@
|| MetaKeyKeyListener.getMetaState(buffer,
MetaKeyKeyListener.META_SELECTING) != 0;
- if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
- ds[0].mIsActivelySelecting = false;
- }
-
float dx;
float dy;
- if (cap && event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+ if (cap) {
// if we're selecting, we want the scroll to go in
// the direction of the drag
dx = event.getX() - ds[0].mX;
@@ -160,7 +150,6 @@
ds[0].mX = event.getX();
ds[0].mY = event.getY();
- int nx = widget.getScrollX() + (int) dx;
int ny = widget.getScrollY() + (int) dy;
int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom();
@@ -172,10 +161,6 @@
int oldX = widget.getScrollX();
int oldY = widget.getScrollY();
- if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
- scrollTo(widget, layout, nx, ny);
- }
-
// If we actually scrolled, then cancel the up action.
if (oldX != widget.getScrollX() || oldY != widget.getScrollY()) {
widget.cancelLongPress();
@@ -207,37 +192,6 @@
return ds.length > 0 ? ds[0].mScrollY : -1;
}
- /**
- * Checks if selection is still active.
- * This is useful for extending Selection span on buffer.
- * @param buffer The text buffer.
- * @return true if buffer has been marked for selection.
- *
- * @hide
- */
- static boolean isActivelySelecting(Spannable buffer) {
- DragState[] ds;
- ds = buffer.getSpans(0, buffer.length(), DragState.class);
-
- return ds.length > 0 && ds[0].mIsActivelySelecting;
- }
-
- /**
- * Checks if selection has begun (are we out of slop?).
- * Note: DragState.mIsSelectionStarted goes back to false with the very next event.
- * This is useful for starting Selection span on buffer.
- * @param buffer The text buffer.
- * @return true if selection has started on the buffer.
- *
- * @hide
- */
- static boolean isSelectionStarted(Spannable buffer) {
- DragState[] ds;
- ds = buffer.getSpans(0, buffer.length(), DragState.class);
-
- return ds.length > 0 && ds[0].mIsSelectionStarted;
- }
-
private static class DragState implements NoCopySpan {
public float mX;
public float mY;
@@ -245,8 +199,6 @@
public int mScrollY;
public boolean mFarEnough;
public boolean mUsed;
- public boolean mIsActivelySelecting;
- public boolean mIsSelectionStarted;
public DragState(float x, float y, int scrollX, int scrollY) {
mX = x;
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index 3688cfa..664d1ea 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -20,7 +20,7 @@
import android.text.Selection;
import android.text.SpannableStringBuilder;
-import java.text.BreakIterator;
+import android.icu.text.BreakIterator;
import java.util.Locale;
/**
diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java
index b2ee045..1becfb4 100644
--- a/core/java/android/util/LocaleList.java
+++ b/core/java/android/util/LocaleList.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.icu.util.ULocale;
import android.os.Parcel;
import android.os.Parcelable;
@@ -208,10 +209,66 @@
}
}
+ private static String getLikelyScript(Locale locale) {
+ final String script = locale.getScript();
+ if (!script.isEmpty()) {
+ return script;
+ } else {
+ // TODO: Cache the results if this proves to be too slow
+ return ULocale.addLikelySubtags(ULocale.forLocale(locale)).getScript();
+ }
+ }
+
+ private static int matchScore(Locale supported, Locale desired) {
+ if (supported.equals(desired)) {
+ return 1; // return early so we don't do unnecessary computation
+ }
+ if (!supported.getLanguage().equals(desired.getLanguage())) {
+ return 0;
+ }
+ // There is no match if the two locales use different scripts. This will most imporantly
+ // take care of traditional vs simplified Chinese.
+ final String supportedScr = getLikelyScript(supported);
+ final String desiredScr = getLikelyScript(desired);
+ return supportedScr.equals(desiredScr) ? 1 : 0;
+ }
+
+ /**
+ * Returns the first match in the locale list given an unordered array of supported locales
+ * in BCP47 format.
+ *
+ * If the locale list is empty, null would be returned.
+ */
@Nullable
- public Locale getBestMatch(String[] locales) {
- // TODO: Fix this to actually do locale negotiation and choose the best match
- return getPrimary();
+ public Locale getFirstMatch(String[] supportedLocales) {
+ if (mList.length == 1) { // just one locale, perhaps the most common scenario
+ return mList[0];
+ }
+ if (mList.length == 0) { // empty locale list
+ return null;
+ }
+ // TODO: Figure out what to if en-XA or ar-XB are in the locale list
+ int bestIndex = Integer.MAX_VALUE;
+ for (String tag : supportedLocales) {
+ final Locale supportedLocale = Locale.forLanguageTag(tag);
+ // We expect the average length of locale lists used for locale resolution to be
+ // smaller than three, so it's OK to do this as an O(mn) algorithm.
+ for (int idx = 0; idx < mList.length; idx++) {
+ final int score = matchScore(supportedLocale, mList[idx]);
+ if (score > 0) {
+ if (idx == 0) { // We have a match on the first locale, which is good enough
+ return mList[0];
+ } else if (idx < bestIndex) {
+ bestIndex = idx;
+ }
+ }
+ }
+ }
+ if (bestIndex == Integer.MAX_VALUE) { // no match was found
+ return mList[0];
+ } else {
+ return mList[bestIndex];
+ }
}
private final static Object sLock = new Object();
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 527d7e5..0195dec 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -973,7 +973,6 @@
* </p>
*
* @see #getAxisValue(int, int)
- * {@hide}
*/
public static final int AXIS_SCROLL = 26;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6dd87a0..183ccf3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -83,7 +83,6 @@
import android.view.AccessibilityIterators.CharacterTextSegmentIterator;
import android.view.AccessibilityIterators.WordTextSegmentIterator;
import android.view.AccessibilityIterators.ParagraphTextSegmentIterator;
-import android.view.ViewGroup.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEventSource;
import android.view.accessibility.AccessibilityManager;
@@ -2423,8 +2422,6 @@
* 1 PFLAG3_SCROLL_INDICATOR_END
* 1 PFLAG3_ASSIST_BLOCKED
* 1111111 PFLAG3_POINTER_ICON_MASK
- * 1 PFLAG3_PARTIAL_LAYOUT_REQUESTED
- * 1 PFLAG3_LAYOUT_PARAMS_CHANGED
* |-------|-------|-------|-------|
*/
@@ -2513,7 +2510,6 @@
*/
static final int PFLAG3_SCROLL_INDICATOR_END = 0x2000;
-
/* End of masks for mPrivateFlags3 */
static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
@@ -2652,19 +2648,6 @@
private static final int PFLAG3_POINTER_ICON_VALUE_START = 3 << PFLAG3_POINTER_ICON_LSHIFT;
/**
- * Flag indicating that this view has requested a partial layout and
- * is added to the AttachInfo's list of views that need a partial layout
- * request handled on the next traversal.
- */
- static final int PFLAG3_PARTIAL_LAYOUT_REQUESTED = 0x800000;
-
- /**
- * Flag indicating that this view's LayoutParams have been explicitly changed
- * since the last layout pass.
- */
- static final int PFLAG3_LAYOUT_PARAMS_CHANGED = 0x1000000;
-
- /**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
*
@@ -12671,14 +12654,10 @@
* ViewGroup.LayoutParams, and these correspond to the different subclasses
* of ViewGroup that are responsible for arranging their children.
*
- * <p>This method may return null if this View is not attached to a parent
+ * This method may return null if this View is not attached to a parent
* ViewGroup or {@link #setLayoutParams(android.view.ViewGroup.LayoutParams)}
* was not invoked successfully. When a View is attached to a parent
- * ViewGroup, this method must not return null.</p>
- *
- * <p>Callers that modify the returned LayoutParams object should call
- * {@link #setLayoutParams(LayoutParams)} to explicitly inform the view that
- * LayoutParams have changed.</p>
+ * ViewGroup, this method must not return null.
*
* @return The LayoutParams associated with this view, or null if no
* parameters have been set yet
@@ -12695,9 +12674,6 @@
* correspond to the different subclasses of ViewGroup that are responsible
* for arranging their children.
*
- * <p>If the View's existing LayoutParams object as obtained by {@link #getLayoutParams()} is
- * modified, you should call this method to inform the view that it has changed.</p>
- *
* @param params The layout parameters for this view, cannot be null
*/
public void setLayoutParams(ViewGroup.LayoutParams params) {
@@ -12705,7 +12681,6 @@
throw new NullPointerException("Layout parameters cannot be null");
}
mLayoutParams = params;
- mPrivateFlags3 |= PFLAG3_LAYOUT_PARAMS_CHANGED;
resolveLayoutParams();
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onSetLayoutParams(this, params);
@@ -14381,12 +14356,7 @@
mParent.requestTransparentRegion(this);
}
- if ((mPrivateFlags & PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH) != 0) {
- initialAwakenScrollBars();
- mPrivateFlags &= ~PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH;
- }
-
- mPrivateFlags3 &= ~(PFLAG3_IS_LAID_OUT | PFLAG3_PARTIAL_LAYOUT_REQUESTED);
+ mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
jumpDrawablesToCurrentState();
@@ -14724,13 +14694,8 @@
*/
@CallSuper
protected void onDetachedFromWindowInternal() {
- if (mAttachInfo != null && isPartialLayoutRequested()) {
- mAttachInfo.mPartialLayoutViews.remove(this);
- }
-
mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
- mPrivateFlags3 &= ~(PFLAG3_IS_LAID_OUT | PFLAG3_PARTIAL_LAYOUT_REQUESTED
- | PFLAG3_LAYOUT_PARAMS_CHANGED);
+ mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
removeUnsetPressCallback();
removeLongPressCallback();
@@ -16917,32 +16882,6 @@
}
/**
- * Indicates whether or not this view has requested a partial layout that
- * may not affect its size or position within its parent. This state will be reset
- * the next time this view is laid out.
- *
- * @return true if partial layout has been requested
- */
- public final boolean isPartialLayoutRequested() {
- return (mPrivateFlags3 & PFLAG3_PARTIAL_LAYOUT_REQUESTED)
- == PFLAG3_PARTIAL_LAYOUT_REQUESTED;
- }
-
- /**
- * Returns true if this view's {@link ViewGroup.LayoutParams LayoutParams} changed
- * since the last time this view was successfully laid out. Typically this happens as a
- * result of a call to {@link #setLayoutParams(LayoutParams)}.
- *
- * @return true if this view's LayoutParams changed since last layout.
- */
- public final boolean didLayoutParamsChange() {
- if (sLayoutParamsAlwaysChanged) {
- return true;
- }
- return (mPrivateFlags3 & PFLAG3_LAYOUT_PARAMS_CHANGED) == PFLAG3_LAYOUT_PARAMS_CHANGED;
- }
-
- /**
* Return true if o is a ViewGroup that is laying out using optical bounds.
* @hide
*/
@@ -16999,7 +16938,6 @@
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
- mPrivateFlags3 &= ~PFLAG3_LAYOUT_PARAMS_CHANGED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
@@ -17013,7 +16951,6 @@
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
- mPrivateFlags3 &= ~PFLAG3_PARTIAL_LAYOUT_REQUESTED;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
@@ -19111,7 +19048,7 @@
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
- mParent.requestLayoutForChild(this);
+ mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
@@ -19130,11 +19067,6 @@
mPrivateFlags |= PFLAG_INVALIDATED;
}
- void forcePartialLayout() {
- forceLayout();
- mPrivateFlags3 |= PFLAG3_PARTIAL_LAYOUT_REQUESTED;
- }
-
/**
* <p>
* This is called to find out how big a view should be. The parent
@@ -22021,7 +21953,6 @@
interface Callbacks {
void playSoundEffect(int effectId);
boolean performHapticFeedback(int effectId, boolean always);
- void schedulePartialLayout();
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 488063b..0f7d296 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -60,8 +60,6 @@
import java.util.List;
import java.util.Map;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
/**
* <p>
@@ -5531,172 +5529,6 @@
int l, int t, int r, int b);
/**
- * {@inheritDoc}
- *
- * <p>Most subclasses should not need to override this method. The default implementation
- * will call {@link #findDependentLayoutAxes(View, int)} to determine how
- * to optimally proceed. If neither horizontal nor vertical layout depends on the given
- * child, this method will call {@link #requestPartialLayoutForChild(View)}. If one or both
- * do, it will call {@link #requestLayout()}.</p>
- *
- * @param child Child requesting a layout
- */
- @Override
- public void requestLayoutForChild(View child) {
- if (child == null || child.getParent() != this) {
- throw new IllegalArgumentException(
- "child parameter must be a direct child view of this ViewGroup");
- }
-
- // If we don't have a parent ourselves, record that we need a full layout.
- // Our whole subtree is detached.
- final ViewParent parent = getParent();
- if (parent == null) {
- requestLayout();
- return;
- }
-
- // We can optimize the layout request for this child into a partial layout
- // if the child has already been laid out at least once and neither horizontal nor
- // vertical layout within ourselves is dependent on pending layout changes within
- // this child. Otherwise we need to request a full layout for ourselves and continue
- // to recurse up the view hierarchy.
- if (child.isLaidOut() && findDependentLayoutAxes(child, FLAG_LAYOUT_AXIS_ANY) == 0) {
- requestPartialLayoutForChild(child);
- } else {
- requestLayout();
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * <p>The default implementation returns {@link #FLAG_LAYOUT_AXIS_ANY}.
- * Optimized implementations for specific ViewGroup subclasses may check if the child's
- * {@link View#didLayoutParamsChange() LayoutParams changed} and in what ways.</p>
- *
- * @param child Direct child of this ViewParent to check
- * @param axisFilter Which axes to check for dependencies. Can be
- * {@link #FLAG_LAYOUT_AXIS_HORIZONTAL}, {@link #FLAG_LAYOUT_AXIS_VERTICAL}
- * or {@link #FLAG_LAYOUT_AXIS_ANY}.
- * @return Axes of this ViewParent that depend on the given child's layout changes
- */
- @Override
- public int findDependentLayoutAxes(View child, int axisFilter) {
- return FLAG_LAYOUT_AXIS_ANY;
- }
-
- /**
- * This is a helper implementation for {@link #findDependentLayoutAxes(View, int)} that
- * is not the default implementation in ViewGroup. This is to preserve compatibility with
- * existing app-side ViewGroup subclasses that existed before the partial layout system was
- * added to Android. It explicitly checks that the LayoutParams of the child are of the
- * expected type so that subclasses of standard framework layouts do not erroneously
- * start believing that it's safe to do a partial layout when that assertion can't
- * reasonably be confirmed.
- *
- * <p>If you're reading this as an author of a custom ViewGroup's findDependentLayoutAxes
- * method you might be frustrated to discover that it is not a part of the Android public API.
- * Many ViewGroup implementations will need to make small but important modifications
- * to an implementation like this one in order to be correct. Instead of encouraging
- * view authors to call this method, then make their own redundant recursive calls to
- * <code>getParent().findDependentLayoutAxes(...)</code> in addition to the one
- * that can happen here, this method is hidden and only used internally.</p>
- *
- * <p>Do feel free to copy this implementation and adapt it to suit your own purposes.</p>
- *
- * @hide
- */
- protected final int findDependentLayoutAxesHelper(View child, int axisFilter,
- Class<?> layoutParamsClass) {
- if (!checkPartialLayoutParams(child, layoutParamsClass)) return axisFilter;
- if (child.didLayoutParamsChange()) {
- // Anything could have changed about our previous assumptions.
- return axisFilter;
- }
-
- final LayoutParams lp = child.getLayoutParams();
-
- // Our layout can always end up depending on a WRAP_CONTENT child.
- final int wrapAxisFilter = ((lp.width == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
- | (lp.height == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
-
- if (wrapAxisFilter == axisFilter) {
- // We know all queried axes are affected, just return early.
- return wrapAxisFilter;
- }
-
- // Our layout *may* depend on a MATCH_PARENT child, depending on whether we can determine
- // that our layout will remain stable within our parent. We need to ask.
- final int matchAxisFilter = ((lp.width == MATCH_PARENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
- | (lp.height == MATCH_PARENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
-
- if (matchAxisFilter != 0 || wrapAxisFilter != 0) {
- final ViewParent parent = getParent();
- if (parent != null) {
- // If our parent depends on us for an axis, then our layout can also be affected
- // by a MATCH_PARENT child along that axis.
- return getParent().findDependentLayoutAxes(this, matchAxisFilter)
- | wrapAxisFilter;
- }
-
- // If we don't have a parent, assume we're affected
- // in any determined affected direction.
- return matchAxisFilter | wrapAxisFilter;
- }
-
- // Two exact sizes and LayoutParams didn't change. We're safe.
- return 0;
- }
-
- /**
- * Throw an IllegalArgumentException if the supplied view is not a direct child of
- * this ViewGroup and return false if this view's LayoutParams is not of class lpClass.
- * Implementations of {@link ViewGroup#findDependentLayoutAxes(View, int)} use this
- * to check input parameters and defensively return the full axis filter mask themselves
- * if the LayoutParams class is not of the exact expected type; e.g. it is a subclass
- * of one of the standard framework layouts and we can't make assumptions.
- * @hide
- */
- protected final boolean checkPartialLayoutParams(View child, Class<?> lpClass) {
- if (child.getParent() != this) {
- throw new IllegalArgumentException("View " + child
- + " is not a direct child of " + this);
- }
- final ViewGroup.LayoutParams lp = child.getLayoutParams();
- return lp != null || lp.getClass() == lpClass;
- }
-
- /**
- * Called when a child of this ViewParent requires a relayout before the next frame
- * is drawn, but the caller can guarantee that the size of the child will not change
- * during a measure and layout pass.
- *
- * <p>A call to this method will schedule a partial layout for the supplied view as long as
- * it is a direct child of this ViewGroup and this ViewGroup is attached to a window.
- * On the next scheduled view hierarchy traversal the given child view will be re-measured
- * at its current measured size and re-laid out at its current position within its parent.</p>
- *
- * @param child Child that requires a partial layout
- */
- public void requestPartialLayoutForChild(View child) {
- if (!child.isPartialLayoutRequested()) {
- child.forcePartialLayout();
- if (mAttachInfo != null) {
- final List<View> partialLayoutViews = mAttachInfo.mPartialLayoutViews;
- final boolean schedule = partialLayoutViews.isEmpty();
- partialLayoutViews.add(child);
- if (schedule) {
- mAttachInfo.mRootCallbacks.schedulePartialLayout();
- }
- child.invalidate();
- } else {
- requestLayout();
- }
- }
- }
-
- /**
* Indicates whether the view group has the ability to animate its children
* after the first layout.
*
@@ -6042,7 +5874,7 @@
* of its descendants
*/
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return new LayoutParams(p);
+ return p;
}
/**
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index f2ab35e..e9b123b5 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -26,11 +26,6 @@
*
*/
public interface ViewParent {
- public static final int FLAG_LAYOUT_AXIS_HORIZONTAL = 1;
- public static final int FLAG_LAYOUT_AXIS_VERTICAL = 2;
- public static final int FLAG_LAYOUT_AXIS_ANY
- = FLAG_LAYOUT_AXIS_HORIZONTAL | FLAG_LAYOUT_AXIS_VERTICAL;
-
/**
* Called when something has changed which has invalidated the layout of a
* child of this view parent. This will schedule a layout pass of the view
@@ -618,48 +613,4 @@
* @return true if the action was consumed by this ViewParent
*/
public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle arguments);
-
- /**
- * Called when a child of this ViewParent requires a relayout before
- * the next frame is drawn. A call to {@link View#requestLayout() child.requestLayout()}
- * will implicitly result in a call to
- * <code>child.getParent().requestLayoutForChild(child)</code>. App code should not call this
- * method directly. Call <code>child.requestLayout()</code> instead.
- *
- * <p>On versions of Android from API 23 and older, a call to {@link View#requestLayout()}
- * would cause a matching call to <code>requestLayout</code> on each parent view up to
- * the root. With the addition of <code>requestLayoutForChild</code> a view's parent may
- * explicitly decide how to handle a layout request. This allows for optimizations when
- * a view parent knows that a layout-altering change in a child will not affect its own
- * measurement.</p>
- *
- * @param child Child requesting a layout
- */
- public void requestLayoutForChild(View child);
-
- /**
- * Determine which axes of this ViewParent's layout are dependent on the given
- * direct child view. The returned value is a flag set that may contain
- * {@link #FLAG_LAYOUT_AXIS_HORIZONTAL} and/or {@link #FLAG_LAYOUT_AXIS_VERTICAL}.
- * {@link #FLAG_LAYOUT_AXIS_ANY} is provided as a shortcut for
- * <code>FLAG_LAYOUT_AXIS_HORIZONTAL | FLAG_LAYOUT_AXIS_VERTICAL</code>.
- *
- * <p>The given child must be a direct child view. Implementations should throw
- * {@link IllegalArgumentException} otherwise.</p>
- *
- * <p>The caller may specify which axes it cares about. This should be treated as a filter.
- * Implementations should never return a result that would be different from
- * <code>result & axisFilter</code>.</p>
- *
- * @param child Direct child of this ViewParent to check
- * @param axisFilter Which axes to check for dependencies. Can be
- * {@link #FLAG_LAYOUT_AXIS_HORIZONTAL}, {@link #FLAG_LAYOUT_AXIS_VERTICAL}
- * or {@link #FLAG_LAYOUT_AXIS_ANY}.
- * @return Axes of this ViewParent that depend on the given child's layout changes
- *
- * @see #FLAG_LAYOUT_AXIS_HORIZONTAL
- * @see #FLAG_LAYOUT_AXIS_VERTICAL
- * @see #FLAG_LAYOUT_AXIS_ANY
- */
- public int findDependentLayoutAxes(View child, int axisFilter);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b7bb9a3..12cf66e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -91,7 +91,6 @@
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.HashSet;
-import java.util.List;
/**
* The top of a view hierarchy, implementing the needed protocol between View
@@ -953,25 +952,6 @@
}
@Override
- public void requestLayoutForChild(View child) {
- requestLayout();
- }
-
- @Override
- public int findDependentLayoutAxes(View child, int axisFilter) {
- if (child != mView) {
- return 0;
- }
-
- final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) child.getLayoutParams();
- final int horizontal = (lp.width == WindowManager.LayoutParams.WRAP_CONTENT
- || lp.horizontalWeight != 0) ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0;
- final int vertical = (lp.height == WindowManager.LayoutParams.WRAP_CONTENT
- || lp.verticalWeight != 0) ? FLAG_LAYOUT_AXIS_VERTICAL : 0;
- return (horizontal | vertical) & axisFilter;
- }
-
- @Override
public boolean isLayoutRequested() {
return mLayoutRequested;
}
@@ -1115,10 +1095,6 @@
}
}
- public void schedulePartialLayout() {
- scheduleTraversals();
- }
-
/**
* Notifies the HardwareRenderer that a new frame will be coming soon.
* Currently only {@link ThreadedRenderer} cares about this, and uses
@@ -1958,60 +1934,7 @@
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
- }
- /*
- * Handle partial layouts.
- *
- * Views that have requested partial layouts will not change size or position
- * within their parent view, therefore we will re-measure and re-layout each one
- * after any regularly scheduled layout pass. Any view that already had its
- * isLayoutRequested bit cleared will be skipped, since this means the view has already
- * been measured and laid out on this traversal pass naturally. Views won't be added
- * to this list if layout was already requested when a partial layout is requested
- * for a view, so there should not be duplicates in the list.
- */
- final List<View> partialLayoutViews = mAttachInfo.mPartialLayoutViews;
- final boolean didPartialLayout;
- if (!partialLayoutViews.isEmpty()) {
- // Measurement or layout of views may result in changes to the list
- // of partial-layout views. Swap in an "empty" list to prevent
- // concurrent modification of the list being traversed.
- if (mAttachInfo.mEmptyPartialLayoutViews == null) {
- mAttachInfo.mPartialLayoutViews = new ArrayList<>();
- } else {
- mAttachInfo.mPartialLayoutViews = mAttachInfo.mEmptyPartialLayoutViews;
- }
-
- final int count = partialLayoutViews.size();
- mInLayout = true;
- for (int i = 0; i < count; i++) {
- final View view = partialLayoutViews.get(i);
-
- // Make sure the view is still attached and that it still has layout requested.
- // We might have already serviced the layout request through the standard full-tree
- // layout pass above or even through a previous partial layout view in this list.
- if (view.isAttachedToWindow() && view.isLayoutRequested()) {
- final int widthSpec = MeasureSpec.makeMeasureSpec(view.getMeasuredWidth(),
- MeasureSpec.EXACTLY);
- final int heightSpec = MeasureSpec.makeMeasureSpec(view.getMeasuredHeight(),
- MeasureSpec.EXACTLY);
- view.measure(widthSpec, heightSpec);
- view.layout(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
- }
- }
- mInLayout = false;
- didPartialLayout = true;
- triggerGlobalLayoutListener = true;
-
- // The traversal list becomes the new empty list.
- partialLayoutViews.clear();
- mAttachInfo.mEmptyPartialLayoutViews = partialLayoutViews;
- } else {
- didPartialLayout = false;
- }
-
- if (didLayout || didPartialLayout) {
// By this point all views have been sized and positioned
// We can compute the transparent area
@@ -2041,7 +1964,7 @@
if (DBG) {
System.out.println("======================================");
- System.out.println("performTraversals -- after performLayout/partial layout");
+ System.out.println("performTraversals -- after setFrame");
host.debug();
}
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 7a359e7..53490b4 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -29,6 +29,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.media.session.MediaController;
import android.net.Uri;
@@ -247,12 +248,30 @@
private static final String PROPERTY_HARDWARE_UI = "persist.sys.ui.hw";
+ /**
+ * Flag for letting the theme drive the color of the window caption controls. Use with
+ * {@link #setDecorCaptionShade(int)}. This is the default value.
+ */
+ public static final int DECOR_CAPTION_SHADE_AUTO = 0;
+ /**
+ * Flag for setting light-color controls on the window caption. Use with
+ * {@link #setDecorCaptionShade(int)}.
+ */
+ public static final int DECOR_CAPTION_SHADE_LIGHT = 1;
+ /**
+ * Flag for setting dark-color controls on the window caption. Use with
+ * {@link #setDecorCaptionShade(int)}.
+ */
+ public static final int DECOR_CAPTION_SHADE_DARK = 2;
+
private final Context mContext;
private TypedArray mWindowStyle;
private Callback mCallback;
private OnWindowDismissedCallback mOnWindowDismissedCallback;
private WindowControllerCallback mWindowControllerCallback;
+ private RestrictedCaptionAreaListener mRestrictedCaptionAreaListener;
+ private Rect mRestrictedCaptionAreaRect;
private WindowManager mWindowManager;
private IBinder mAppToken;
private String mAppName;
@@ -565,6 +584,18 @@
int getWindowStackId() throws RemoteException;
}
+ /**
+ * Callback for clients that want to be aware of where caption draws content.
+ */
+ public interface RestrictedCaptionAreaListener {
+ /**
+ * Called when the area where caption draws content changes.
+ *
+ * @param rect The area where caption content is positioned, relative to the top view.
+ */
+ void onRestrictedCaptionAreaChanged(Rect rect);
+ }
+
public Window(Context context) {
mContext = context;
mFeatures = mLocalFeatures = getDefaultFeatures(context);
@@ -778,6 +809,16 @@
}
/**
+ * Set a callback for changes of area where caption will draw its content.
+ *
+ * @param listener Callback that will be called when the area changes.
+ */
+ public final void setRestrictedCaptionAreaListener(RestrictedCaptionAreaListener listener) {
+ mRestrictedCaptionAreaListener = listener;
+ mRestrictedCaptionAreaRect = listener != null ? new Rect() : null;
+ }
+
+ /**
* Take ownership of this window's surface. The window's view hierarchy
* will no longer draw into the surface, though it will otherwise continue
* to operate (such as for receiving input events). The given SurfaceHolder
@@ -2040,5 +2081,29 @@
return mOverlayWithDecorCaption;
}
+ /** @hide */
+ public void notifyRestrictedCaptionAreaCallback(int left, int top, int right, int bottom) {
+ if (mRestrictedCaptionAreaListener != null) {
+ mRestrictedCaptionAreaRect.set(left, top, right, bottom);
+ mRestrictedCaptionAreaListener.onRestrictedCaptionAreaChanged(
+ mRestrictedCaptionAreaRect);
+ }
+ }
+ /**
+ * Set what color should the caption controls be. By default the system will try to determine
+ * the color from the theme. You can overwrite this by using {@link #DECOR_CAPTION_SHADE_DARK}
+ * or {@link #DECOR_CAPTION_SHADE_DARK}.
+ */
+ public abstract void setDecorCaptionShade(int decorCaptionShade);
+
+ /**
+ * Set the drawable that is drawn underneath the caption during the resizing.
+ *
+ * During the resizing the caption might not be drawn fast enough to match the new dimensions.
+ * There is a second caption drawn underneath it that will be fast enough. By default the
+ * caption is constructed from the theme. You can provide a drawable, that will be drawn instead
+ * to better match your application.
+ */
+ public abstract void setResizingCaptionDrawable(Drawable drawable);
}
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index fbaf51c..a42f4d9 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -50,6 +51,7 @@
*
* @attr ref android.R.styleable#InputMethod_Subtype_label
* @attr ref android.R.styleable#InputMethod_Subtype_icon
+ * @attr ref android.R.styleable#InputMethod_Subtype_languageTag
* @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeLocale
* @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeMode
* @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeExtraValue
@@ -60,6 +62,7 @@
*/
public final class InputMethodSubtype implements Parcelable {
private static final String TAG = InputMethodSubtype.class.getSimpleName();
+ private static final String LANGUAGE_TAG_NONE = "";
private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
// TODO: remove this
@@ -74,6 +77,7 @@
private final int mSubtypeNameResId;
private final int mSubtypeId;
private final String mSubtypeLocale;
+ private final String mSubtypeLanguageTag;
private final String mSubtypeMode;
private final String mSubtypeExtraValue;
private volatile HashMap<String, String> mExtraValueHashMapCache;
@@ -171,6 +175,15 @@
private String mSubtypeLocale = "";
/**
+ * @param languageTag is the BCP-47 Language Tag supported by this subtype.
+ */
+ public InputMethodSubtypeBuilder setLanguageTag(String languageTag) {
+ mSubtypeLanguageTag = languageTag == null ? LANGUAGE_TAG_NONE : languageTag;
+ return this;
+ }
+ private String mSubtypeLanguageTag = LANGUAGE_TAG_NONE;
+
+ /**
* @param subtypeMode is the mode supported by this subtype.
*/
public InputMethodSubtypeBuilder setSubtypeMode(String subtypeMode) {
@@ -271,6 +284,7 @@
mSubtypeNameResId = builder.mSubtypeNameResId;
mSubtypeIconResId = builder.mSubtypeIconResId;
mSubtypeLocale = builder.mSubtypeLocale;
+ mSubtypeLanguageTag = builder.mSubtypeLanguageTag;
mSubtypeMode = builder.mSubtypeMode;
mSubtypeExtraValue = builder.mSubtypeExtraValue;
mIsAuxiliary = builder.mIsAuxiliary;
@@ -291,6 +305,8 @@
s = source.readString();
mSubtypeLocale = s != null ? s : "";
s = source.readString();
+ mSubtypeLanguageTag = s != null ? s : LANGUAGE_TAG_NONE;
+ s = source.readString();
mSubtypeMode = s != null ? s : "";
s = source.readString();
mSubtypeExtraValue = s != null ? s : "";
@@ -318,21 +334,38 @@
/**
* @return The locale of the subtype. This method returns the "locale" string parameter passed
* to the constructor.
+ *
+ * @deprecated Use {@link #getLanguageTag()} instead.
*/
+ @Deprecated
+ @NonNull
public String getLocale() {
return mSubtypeLocale;
}
/**
- * @return The normalized {@link Locale} object of the subtype. The returned locale may or may
- * not equal to "locale" string parameter passed to the constructor.
+ * @return the BCP-47 Language Tag of the subtype. Returns an empty string when no Language Tag
+ * is specified.
*
- * <p>TODO: Consider to make this a public API.</p>
+ * @see Locale#forLanguageTag(String)
+ */
+ @NonNull
+ public String getLanguageTag() {
+ return mSubtypeLanguageTag;
+ }
+
+ /**
+ * @return {@link Locale} constructed from {@link #getLanguageTag()}. If the Language Tag is not
+ * specified, then try to construct from {@link #getLocale()}
+ *
+ * <p>TODO: Consider to make this a public API, or move this to support lib.</p>
* @hide
*/
@Nullable
public Locale getLocaleObject() {
- // TODO: Move the following method from InputMethodUtils to InputMethodSubtype.
+ if (!TextUtils.isEmpty(mSubtypeLanguageTag)) {
+ return Locale.forLanguageTag(mSubtypeLanguageTag);
+ }
return InputMethodUtils.constructLocaleFromString(mSubtypeLocale);
}
@@ -476,13 +509,14 @@
return (subtype.hashCode() == hashCode());
}
return (subtype.hashCode() == hashCode())
- && (subtype.getLocale().equals(getLocale()))
- && (subtype.getMode().equals(getMode()))
- && (subtype.getExtraValue().equals(getExtraValue()))
- && (subtype.isAuxiliary() == isAuxiliary())
- && (subtype.overridesImplicitlyEnabledSubtype()
- == overridesImplicitlyEnabledSubtype())
- && (subtype.isAsciiCapable() == isAsciiCapable());
+ && (subtype.getLocale().equals(getLocale()))
+ && (subtype.getLanguageTag().equals(getLanguageTag()))
+ && (subtype.getMode().equals(getMode()))
+ && (subtype.getExtraValue().equals(getExtraValue()))
+ && (subtype.isAuxiliary() == isAuxiliary())
+ && (subtype.overridesImplicitlyEnabledSubtype()
+ == overridesImplicitlyEnabledSubtype())
+ && (subtype.isAsciiCapable() == isAsciiCapable());
}
return false;
}
@@ -497,6 +531,7 @@
dest.writeInt(mSubtypeNameResId);
dest.writeInt(mSubtypeIconResId);
dest.writeString(mSubtypeLocale);
+ dest.writeString(mSubtypeLanguageTag);
dest.writeString(mSubtypeMode);
dest.writeString(mSubtypeExtraValue);
dest.writeInt(mIsAuxiliary ? 1 : 0);
diff --git a/core/java/android/view/textservice/SpellCheckerInfo.java b/core/java/android/view/textservice/SpellCheckerInfo.java
index 491de78..471b6d4 100644
--- a/core/java/android/view/textservice/SpellCheckerInfo.java
+++ b/core/java/android/view/textservice/SpellCheckerInfo.java
@@ -117,6 +117,8 @@
a.getString(com.android.internal.R.styleable
.SpellChecker_Subtype_subtypeLocale),
a.getString(com.android.internal.R.styleable
+ .SpellChecker_Subtype_languageTag),
+ a.getString(com.android.internal.R.styleable
.SpellChecker_Subtype_subtypeExtraValue),
a.getInt(com.android.internal.R.styleable
.SpellChecker_Subtype_subtypeId, 0));
diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java
index f2b03cc..df33698 100644
--- a/core/java/android/view/textservice/SpellCheckerSubtype.java
+++ b/core/java/android/view/textservice/SpellCheckerSubtype.java
@@ -18,6 +18,7 @@
import com.android.internal.inputmethod.InputMethodUtils;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -40,6 +41,7 @@
* @see SpellCheckerInfo
*
* @attr ref android.R.styleable#SpellChecker_Subtype_label
+ * @attr ref android.R.styleable#SpellChecker_Subtype_languageTag
* @attr ref android.R.styleable#SpellChecker_Subtype_subtypeLocale
* @attr ref android.R.styleable#SpellChecker_Subtype_subtypeExtraValue
* @attr ref android.R.styleable#SpellChecker_Subtype_subtypeId
@@ -49,11 +51,13 @@
private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
private static final int SUBTYPE_ID_NONE = 0;
+ private static final String SUBTYPE_LANGUAGE_TAG_NONE = "";
private final int mSubtypeId;
private final int mSubtypeHashCode;
private final int mSubtypeNameResId;
private final String mSubtypeLocale;
+ private final String mSubtypeLanguageTag;
private final String mSubtypeExtraValue;
private HashMap<String, String> mExtraValueHashMapCache;
@@ -66,14 +70,17 @@
*
* @param nameId The name of the subtype
* @param locale The locale supported by the subtype
+ * @param languageTag The BCP-47 Language Tag associated with this subtype.
* @param extraValue The extra value of the subtype
* @param subtypeId The subtype ID that is supposed to be stable during package update.
*
* @hide
*/
- public SpellCheckerSubtype(int nameId, String locale, String extraValue, int subtypeId) {
+ public SpellCheckerSubtype(int nameId, String locale, String languageTag, String extraValue,
+ int subtypeId) {
mSubtypeNameResId = nameId;
mSubtypeLocale = locale != null ? locale : "";
+ mSubtypeLanguageTag = languageTag != null ? languageTag : SUBTYPE_LANGUAGE_TAG_NONE;
mSubtypeExtraValue = extraValue != null ? extraValue : "";
mSubtypeId = subtypeId;
mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ?
@@ -91,7 +98,7 @@
* to instantiate {@link SpellCheckerSubtype} object.
*/
public SpellCheckerSubtype(int nameId, String locale, String extraValue) {
- this(nameId, locale, extraValue, SUBTYPE_ID_NONE);
+ this(nameId, locale, SUBTYPE_LANGUAGE_TAG_NONE, extraValue, SUBTYPE_ID_NONE);
}
SpellCheckerSubtype(Parcel source) {
@@ -100,6 +107,8 @@
s = source.readString();
mSubtypeLocale = s != null ? s : "";
s = source.readString();
+ mSubtypeLanguageTag = s != null ? s : "";
+ s = source.readString();
mSubtypeExtraValue = s != null ? s : "";
mSubtypeId = source.readInt();
mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ?
@@ -115,12 +124,27 @@
/**
* @return the locale of the subtype
+ *
+ * @deprecated Use {@link #getLanguageTag()} instead.
*/
+ @Deprecated
+ @NonNull
public String getLocale() {
return mSubtypeLocale;
}
/**
+ * @return the BCP-47 Language Tag of the subtype. Returns an empty string when no Language Tag
+ * is specified.
+ *
+ * @see Locale#forLanguageTag(String)
+ */
+ @NonNull
+ public String getLanguageTag() {
+ return mSubtypeLanguageTag;
+ }
+
+ /**
* @return the extra value of the subtype
*/
public String getExtraValue() {
@@ -182,20 +206,24 @@
return (subtype.hashCode() == hashCode())
&& (subtype.getNameResId() == getNameResId())
&& (subtype.getLocale().equals(getLocale()))
+ && (subtype.getLanguageTag().equals(getLanguageTag()))
&& (subtype.getExtraValue().equals(getExtraValue()));
}
return false;
}
/**
- * @return The normalized {@link Locale} object of the subtype. The returned locale may or may
- * not equal to "locale" string parameter passed to the constructor.
+ * @return {@link Locale} constructed from {@link #getLanguageTag()}. If the Language Tag is not
+ * specified, then try to construct from {@link #getLocale()}
*
- * <p>TODO: Consider to make this a public API.</p>
+ * <p>TODO: Consider to make this a public API, or move this to support lib.</p>
* @hide
*/
@Nullable
public Locale getLocaleObject() {
+ if (!TextUtils.isEmpty(mSubtypeLanguageTag)) {
+ return Locale.forLanguageTag(mSubtypeLanguageTag);
+ }
return InputMethodUtils.constructLocaleFromString(mSubtypeLocale);
}
@@ -234,6 +262,7 @@
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeInt(mSubtypeNameResId);
dest.writeString(mSubtypeLocale);
+ dest.writeString(mSubtypeLanguageTag);
dest.writeString(mSubtypeExtraValue);
dest.writeInt(mSubtypeId);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index f050e49..e1ce9fe 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2109,11 +2109,6 @@
}
@Override
- public int findDependentLayoutAxes(View child, int axisFilter) {
- return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mSelector == null) {
useDefaultSelector();
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 10aefe4..cf4587d 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -16,6 +16,8 @@
package android.widget;
+import com.android.internal.R;
+
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -33,8 +35,6 @@
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityNodeInfo;
-import com.android.internal.R;
-
public abstract class AbsSeekBar extends ProgressBar {
private final Rect mTempRect = new Rect();
@@ -424,10 +424,10 @@
if (thumbHeight > trackHeight) {
final int offsetHeight = (paddedHeight - thumbHeight) / 2;
trackOffset = offsetHeight + (thumbHeight - trackHeight) / 2;
- thumbOffset = offsetHeight + 0;
+ thumbOffset = offsetHeight;
} else {
final int offsetHeight = (paddedHeight - trackHeight) / 2;
- trackOffset = offsetHeight + 0;
+ trackOffset = offsetHeight;
thumbOffset = offsetHeight + (trackHeight - thumbHeight) / 2;
}
@@ -574,13 +574,7 @@
if (isInScrollingContainer()) {
mTouchDownX = event.getX();
} else {
- setPressed(true);
- if (mThumb != null) {
- invalidate(mThumb.getBounds()); // This may be within the padding region
- }
- onStartTrackingTouch();
- trackTouchEvent(event);
- attemptClaimDrag();
+ startDrag(event);
}
break;
@@ -590,13 +584,7 @@
} else {
final float x = event.getX();
if (Math.abs(x - mTouchDownX) > mScaledTouchSlop) {
- setPressed(true);
- if (mThumb != null) {
- invalidate(mThumb.getBounds()); // This may be within the padding region
- }
- onStartTrackingTouch();
- trackTouchEvent(event);
- attemptClaimDrag();
+ startDrag(event);
}
}
break;
@@ -630,6 +618,19 @@
return true;
}
+ private void startDrag(MotionEvent event) {
+ setPressed(true);
+
+ if (mThumb != null) {
+ // This may be within the padding region.
+ invalidate(mThumb.getBounds());
+ }
+
+ onStartTrackingTouch();
+ trackTouchEvent(event);
+ attemptClaimDrag();
+ }
+
private void setHotspot(float x, float y) {
final Drawable bg = getBackground();
if (bg != null) {
@@ -638,18 +639,20 @@
}
private void trackTouchEvent(MotionEvent event) {
+ final int x = Math.round(event.getX());
+ final int y = Math.round(event.getY());
final int width = getWidth();
- final int available = width - mPaddingLeft - mPaddingRight;
- final int x = (int) event.getX();
- float scale;
- float progress = 0;
+ final int availableWidth = width - mPaddingLeft - mPaddingRight;
+
+ final float scale;
+ float progress = 0.0f;
if (isLayoutRtl() && mMirrorForRtl) {
if (x > width - mPaddingRight) {
scale = 0.0f;
} else if (x < mPaddingLeft) {
scale = 1.0f;
} else {
- scale = (float)(available - x + mPaddingLeft) / (float)available;
+ scale = (availableWidth - x + mPaddingLeft) / (float) availableWidth;
progress = mTouchProgressOffset;
}
} else {
@@ -658,15 +661,16 @@
} else if (x > width - mPaddingRight) {
scale = 1.0f;
} else {
- scale = (float)(x - mPaddingLeft) / (float)available;
+ scale = (x - mPaddingLeft) / (float) availableWidth;
progress = mTouchProgressOffset;
}
}
+
final int max = getMax();
progress += scale * max;
- setHotspot(x, (int) event.getY());
- setProgressInternal((int) progress, true, false);
+ setHotspot(x, y);
+ setProgressInternal(Math.round(progress), true, false);
}
/**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5146bc6..2d1f855 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -75,6 +75,7 @@
import android.view.DisplayListCanvas;
import android.view.DragEvent;
import android.view.Gravity;
+import android.view.InputDevice;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -229,7 +230,14 @@
// Set when this TextView gained focus with some text selected. Will start selection mode.
boolean mCreatedWithASelection;
- boolean mDoubleTap = false;
+ // Indicates the current tap state (first tap, double tap, or triple click).
+ private int mTapState = TAP_STATE_INITIAL;
+ private long mLastTouchUpTime = 0;
+ private static final int TAP_STATE_INITIAL = 0;
+ private static final int TAP_STATE_FIRST_TAP = 1;
+ private static final int TAP_STATE_DOUBLE_TAP = 2;
+ // Only for mouse input.
+ private static final int TAP_STATE_TRIPLE_CLICK = 3;
private Runnable mInsertionActionModeRunnable;
@@ -769,20 +777,12 @@
return retOffset;
}
- /**
- * Adjusts selection to the word under last touch offset. Return true if the operation was
- * successfully performed.
- */
- private boolean selectCurrentWord() {
- if (!mTextView.canSelectText()) {
- return false;
- }
-
+ private boolean needsToSelectAllToSelectWordOrParagraph() {
if (mTextView.hasPasswordTransformationMethod()) {
// Always select all on a password field.
// Cut/copy menu entries are not available for passwords, but being able to select all
// is however useful to delete or paste to replace the entire content.
- return mTextView.selectAllText();
+ return true;
}
int inputType = mTextView.getInputType();
@@ -797,6 +797,21 @@
variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS ||
variation == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS ||
variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Adjusts selection to the word under last touch offset. Return true if the operation was
+ * successfully performed.
+ */
+ private boolean selectCurrentWord() {
+ if (!mTextView.canSelectText()) {
+ return false;
+ }
+
+ if (needsToSelectAllToSelectWordOrParagraph()) {
return mTextView.selectAllText();
}
@@ -805,8 +820,8 @@
final int maxOffset = TextUtils.unpackRangeEndFromLong(lastTouchOffsets);
// Safety check in case standard touch event handling has been bypassed
- if (minOffset < 0 || minOffset >= mTextView.getText().length()) return false;
- if (maxOffset < 0 || maxOffset >= mTextView.getText().length()) return false;
+ if (minOffset < 0 || minOffset > mTextView.getText().length()) return false;
+ if (maxOffset < 0 || maxOffset > mTextView.getText().length()) return false;
int selectionStart, selectionEnd;
@@ -839,6 +854,63 @@
return selectionEnd > selectionStart;
}
+ /**
+ * Adjusts selection to the paragraph under last touch offset. Return true if the operation was
+ * successfully performed.
+ */
+ private boolean selectCurrentParagraph() {
+ if (!mTextView.canSelectText()) {
+ return false;
+ }
+
+ if (needsToSelectAllToSelectWordOrParagraph()) {
+ return mTextView.selectAllText();
+ }
+
+ long lastTouchOffsets = getLastTouchOffsets();
+ final int minLastTouchOffset = TextUtils.unpackRangeStartFromLong(lastTouchOffsets);
+ final int maxLastTouchOffset = TextUtils.unpackRangeEndFromLong(lastTouchOffsets);
+
+ final long paragraphsRange = getParagraphsRange(minLastTouchOffset, maxLastTouchOffset);
+ final int start = TextUtils.unpackRangeStartFromLong(paragraphsRange);
+ final int end = TextUtils.unpackRangeEndFromLong(paragraphsRange);
+ if (start < end) {
+ Selection.setSelection((Spannable) mTextView.getText(), start, end);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get the minimum range of paragraphs that contains startOffset and endOffset.
+ */
+ private long getParagraphsRange(int startOffset, int endOffset) {
+ final Layout layout = mTextView.getLayout();
+ if (layout == null) {
+ return TextUtils.packRangeInLong(-1, -1);
+ }
+ final CharSequence text = mTextView.getText();
+ int minLine = layout.getLineForOffset(startOffset);
+ // Search paragraph start.
+ while (minLine > 0) {
+ final int prevLineEndOffset = layout.getLineEnd(minLine - 1);
+ if (text.charAt(prevLineEndOffset - 1) == '\n') {
+ break;
+ }
+ minLine--;
+ }
+ int maxLine = layout.getLineForOffset(endOffset);
+ // Search paragraph end.
+ while (maxLine < layout.getLineCount() - 1) {
+ final int lineEndOffset = layout.getLineEnd(maxLine);
+ if (text.charAt(lineEndOffset - 1) == '\n') {
+ break;
+ }
+ maxLine++;
+ }
+ return TextUtils.packRangeInLong(layout.getLineStart(minLine), layout.getLineEnd(maxLine));
+ }
+
void onLocaleChanged() {
// Will be re-created on demand in getWordIterator with the proper new locale
mWordIterator = null;
@@ -1219,7 +1291,31 @@
}
}
+ private void updateTapState(MotionEvent event) {
+ final int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
+ // Detect double tap and triple click.
+ if (((mTapState == TAP_STATE_FIRST_TAP)
+ || ((mTapState == TAP_STATE_DOUBLE_TAP) && isMouse))
+ && (SystemClock.uptimeMillis() - mLastTouchUpTime) <=
+ ViewConfiguration.getDoubleTapTimeout()) {
+ if (mTapState == TAP_STATE_FIRST_TAP) {
+ mTapState = TAP_STATE_DOUBLE_TAP;
+ } else {
+ mTapState = TAP_STATE_TRIPLE_CLICK;
+ }
+ } else {
+ mTapState = TAP_STATE_FIRST_TAP;
+ }
+ }
+ if (action == MotionEvent.ACTION_UP) {
+ mLastTouchUpTime = SystemClock.uptimeMillis();
+ }
+ }
+
void onTouchEvent(MotionEvent event) {
+ updateTapState(event);
updateFloatingToolbarVisibility(event);
if (hasSelectionController()) {
@@ -1773,7 +1869,8 @@
stopTextActionMode();
mPreserveDetachedSelection = false;
- getSelectionController().enterDrag();
+ getSelectionController().enterDrag(
+ SelectionModifierCursorController.DRAG_ACCELERATOR_MODE_WORD);
return true;
}
@@ -3777,30 +3874,6 @@
}
}
- public void showAtLocation(int offset) {
- // TODO - investigate if there's a better way to show the handles
- // after the drag accelerator has occured.
- int[] tmpCords = new int[2];
- mTextView.getLocationInWindow(tmpCords);
-
- Layout layout = mTextView.getLayout();
- int posX = tmpCords[0];
- int posY = tmpCords[1];
-
- final int line = layout.getLineForOffset(offset);
-
- int startX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f
- - mHotspotX - getHorizontalOffset() + getCursorOffset());
- int startY = layout.getLineBottom(line);
-
- // Take TextView's padding and scroll into account.
- startX += mTextView.viewportToContentHorizontalOffset();
- startY += mTextView.viewportToContentVerticalOffset();
-
- mContainer.showAtLocation(mTextView, Gravity.NO_GRAVITY,
- startX + posX, startY + posY);
- }
-
@Override
protected void onDraw(Canvas c) {
final int drawWidth = mDrawable.getIntrinsicWidth();
@@ -3933,13 +4006,16 @@
// Cancel the single tap delayed runnable.
if (mInsertionActionModeRunnable != null
- && (mDoubleTap || isCursorInsideEasyCorrectionSpan())) {
+ && ((mTapState == TAP_STATE_DOUBLE_TAP)
+ || (mTapState == TAP_STATE_TRIPLE_CLICK)
+ || isCursorInsideEasyCorrectionSpan())) {
mTextView.removeCallbacks(mInsertionActionModeRunnable);
}
// Prepare and schedule the single tap runnable to run exactly after the double tap
// timeout has passed.
- if (!mDoubleTap && !isCursorInsideEasyCorrectionSpan()
+ if ((mTapState != TAP_STATE_DOUBLE_TAP) && (mTapState != TAP_STATE_TRIPLE_CLICK)
+ && !isCursorInsideEasyCorrectionSpan()
&& (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)) {
if (mTextActionMode == null) {
if (mInsertionActionModeRunnable == null) {
@@ -4223,10 +4299,15 @@
boolean isExpanding;
final float xDiff = x - mPrevX;
- if (atRtl == isStartHandle()) {
- isExpanding = xDiff > 0 || currLine > mPreviousLineTouched;
+ if (isStartHandle()) {
+ isExpanding = currLine < mPreviousLineTouched;
} else {
- isExpanding = xDiff < 0 || currLine < mPreviousLineTouched;
+ isExpanding = currLine > mPreviousLineTouched;
+ }
+ if (atRtl == isStartHandle()) {
+ isExpanding |= xDiff > 0;
+ } else {
+ isExpanding |= xDiff < 0;
}
if (mTextView.getHorizontallyScrolling()) {
@@ -4492,14 +4573,24 @@
// Where the user first starts the drag motion.
private int mStartOffset = -1;
- // Indicates whether the user is selecting text and using the drag accelerator.
- private boolean mDragAcceleratorActive;
+
private boolean mHaventMovedEnoughToStartDrag;
// The line that a selection happened most recently with the drag accelerator.
private int mLineSelectionIsOn = -1;
// Whether the drag accelerator has selected past the initial line.
private boolean mSwitchedLines = false;
+ // Indicates the drag accelerator mode that the user is currently using.
+ private int mDragAcceleratorMode = DRAG_ACCELERATOR_MODE_INACTIVE;
+ // Drag accelerator is inactive.
+ private static final int DRAG_ACCELERATOR_MODE_INACTIVE = 0;
+ // Character based selection by dragging. Only for mouse.
+ private static final int DRAG_ACCELERATOR_MODE_CHARACTER = 1;
+ // Word based selection by dragging. Enabled after long pressing or double tapping.
+ private static final int DRAG_ACCELERATOR_MODE_WORD = 2;
+ // Paragraph based selection by dragging. Enabled after mouse triple click.
+ private static final int DRAG_ACCELERATOR_MODE_PARAGRAPH = 3;
+
SelectionModifierCursorController() {
resetTouchOffsets();
}
@@ -4510,7 +4601,6 @@
}
initDrawables();
initHandles();
- hideInsertionPointCursorController();
}
private void initDrawables() {
@@ -4548,10 +4638,10 @@
if (mEndHandle != null) mEndHandle.hide();
}
- public void enterDrag() {
+ public void enterDrag(int dragAcceleratorMode) {
// Just need to init the handles / hide insertion cursor.
show();
- mDragAcceleratorActive = true;
+ mDragAcceleratorMode = dragAcceleratorMode;
// Start location of selection.
mStartOffset = mTextView.getOffsetForPosition(mLastDownPositionX,
mLastDownPositionY);
@@ -4563,6 +4653,7 @@
// the user to continue dragging across the screen to select text; TextView will
// scroll as necessary.
mTextView.getParent().requestDisallowInterceptTouchEvent(true);
+ mTextView.cancelLongPress();
}
public void onTouchEvent(MotionEvent event) {
@@ -4570,6 +4661,7 @@
// selection and tap can move cursor from this tap position.
final float eventX = event.getX();
final float eventY = event.getY();
+ final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
if (extractedTextModeWillBeStarted()) {
@@ -4582,7 +4674,8 @@
// Double tap detection
if (mGestureStayedInTapRegion) {
- if (mDoubleTap) {
+ if (mTapState == TAP_STATE_DOUBLE_TAP
+ || mTapState == TAP_STATE_TRIPLE_CLICK) {
final float deltaX = eventX - mDownPositionX;
final float deltaY = eventY - mDownPositionY;
final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
@@ -4593,8 +4686,12 @@
boolean stayedInArea =
distanceSquared < doubleTapSlop * doubleTapSlop;
- if (stayedInArea && isPositionOnText(eventX, eventY)) {
- selectCurrentWordAndStartDrag();
+ if (stayedInArea && (isMouse || isPositionOnText(eventX, eventY))) {
+ if (mTapState == TAP_STATE_DOUBLE_TAP) {
+ selectCurrentWordAndStartDrag();
+ } else if (mTapState == TAP_STATE_TRIPLE_CLICK) {
+ selectCurrentParagraphAndStartDrag();
+ }
mDiscardNextActionUp = true;
}
}
@@ -4639,94 +4736,168 @@
}
}
+ if (isMouse && !isDragAcceleratorActive()) {
+ final int offset = mTextView.getOffsetForPosition(eventX, eventY);
+ if (mStartOffset != offset) {
+ // Start character based drag accelerator.
+ if (mTextActionMode != null) {
+ mTextActionMode.finish();
+ }
+ enterDrag(DRAG_ACCELERATOR_MODE_CHARACTER);
+ mDiscardNextActionUp = true;
+ mHaventMovedEnoughToStartDrag = false;
+ }
+ }
+
if (mStartHandle != null && mStartHandle.isShowing()) {
// Don't do the drag if the handles are showing already.
break;
}
- if (mStartOffset != -1 && mTextView.getLayout() != null) {
- if (!mHaventMovedEnoughToStartDrag) {
-
- float y = eventY;
- if (mSwitchedLines) {
- // Offset the finger by the same vertical offset as the handles.
- // This improves visibility of the content being selected by
- // shifting the finger below the content, this is applied once
- // the user has switched lines.
- final float fingerOffset = (mStartHandle != null)
- ? mStartHandle.getIdealVerticalOffset()
- : touchSlop;
- y = eventY - fingerOffset;
- }
-
- final int currLine = getCurrentLineAdjustedForSlop(
- mTextView.getLayout(),
- mLineSelectionIsOn, y);
- if (!mSwitchedLines && currLine != mLineSelectionIsOn) {
- // Break early here, we want to offset the finger position from
- // the selection highlight, once the user moved their finger
- // to a different line we should apply the offset and *not* switch
- // lines until recomputing the position with the finger offset.
- mSwitchedLines = true;
- break;
- }
-
- int startOffset;
- int offset = mTextView.getOffsetAtCoordinate(currLine, eventX);
- // Snap to word boundaries.
- if (mStartOffset < offset) {
- // Expanding with end handle.
- offset = getWordEnd(offset);
- startOffset = getWordStart(mStartOffset);
- } else {
- // Expanding with start handle.
- offset = getWordStart(offset);
- startOffset = getWordEnd(mStartOffset);
- }
- mLineSelectionIsOn = currLine;
- Selection.setSelection((Spannable) mTextView.getText(),
- startOffset, offset);
- }
- }
+ updateSelection(event);
break;
case MotionEvent.ACTION_UP:
- if (mDragAcceleratorActive) {
- // No longer dragging to select text, let the parent intercept events.
- mTextView.getParent().requestDisallowInterceptTouchEvent(false);
-
- show();
- int startOffset = mTextView.getSelectionStart();
- int endOffset = mTextView.getSelectionEnd();
-
- // Since we don't let drag handles pass once they're visible, we need to
- // make sure the start / end locations are correct because the user *can*
- // switch directions during the initial drag.
- if (endOffset < startOffset) {
- int tmp = endOffset;
- endOffset = startOffset;
- startOffset = tmp;
-
- // Also update the selection with the right offsets in this case.
- Selection.setSelection((Spannable) mTextView.getText(),
- startOffset, endOffset);
- }
-
- // Need to do this to display the handles.
- mStartHandle.showAtLocation(startOffset);
- mEndHandle.showAtLocation(endOffset);
-
- // No longer the first dragging motion, reset.
- startSelectionActionMode();
-
- mDragAcceleratorActive = false;
- mStartOffset = -1;
- mSwitchedLines = false;
+ if (!isDragAcceleratorActive()) {
+ break;
}
+ updateSelection(event);
+
+ // No longer dragging to select text, let the parent intercept events.
+ mTextView.getParent().requestDisallowInterceptTouchEvent(false);
+
+ int startOffset = mTextView.getSelectionStart();
+ int endOffset = mTextView.getSelectionEnd();
+
+ // Since we don't let drag handles pass once they're visible, we need to
+ // make sure the start / end locations are correct because the user *can*
+ // switch directions during the initial drag.
+ if (endOffset < startOffset) {
+ int tmp = endOffset;
+ endOffset = startOffset;
+ startOffset = tmp;
+
+ // Also update the selection with the right offsets in this case.
+ Selection.setSelection((Spannable) mTextView.getText(),
+ startOffset, endOffset);
+ }
+ if (startOffset != endOffset) {
+ startSelectionActionMode();
+ }
+
+ // No longer the first dragging motion, reset.
+ resetDragAcceleratorState();
break;
}
}
+ private void updateSelection(MotionEvent event) {
+ if (mTextView.getLayout() != null) {
+ switch (mDragAcceleratorMode) {
+ case DRAG_ACCELERATOR_MODE_CHARACTER:
+ updateCharacterBasedSelection(event);
+ break;
+ case DRAG_ACCELERATOR_MODE_WORD:
+ updateWordBasedSelection(event);
+ break;
+ case DRAG_ACCELERATOR_MODE_PARAGRAPH:
+ updateParagraphBasedSelection(event);
+ break;
+ }
+ }
+ }
+
+ /**
+ * If the TextView allows text selection, selects the current paragraph and starts a drag.
+ *
+ * @return true if the drag was started.
+ */
+ private boolean selectCurrentParagraphAndStartDrag() {
+ if (mInsertionActionModeRunnable != null) {
+ mTextView.removeCallbacks(mInsertionActionModeRunnable);
+ }
+ if (mTextActionMode != null) {
+ mTextActionMode.finish();
+ }
+ if (!selectCurrentParagraph()) {
+ return false;
+ }
+ enterDrag(SelectionModifierCursorController.DRAG_ACCELERATOR_MODE_PARAGRAPH);
+ return true;
+ }
+
+ private void updateCharacterBasedSelection(MotionEvent event) {
+ final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
+ Selection.setSelection((Spannable) mTextView.getText(), mStartOffset, offset);
+ }
+
+ private void updateWordBasedSelection(MotionEvent event) {
+ if (mHaventMovedEnoughToStartDrag) {
+ return;
+ }
+ final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
+ final ViewConfiguration viewConfig = ViewConfiguration.get(
+ mTextView.getContext());
+ final float eventX = event.getX();
+ final float eventY = event.getY();
+ final int currLine;
+ if (isMouse) {
+ // No need to offset the y coordinate for mouse input.
+ currLine = mTextView.getLineAtCoordinate(eventY);
+ } else {
+ float y = eventY;
+ if (mSwitchedLines) {
+ // Offset the finger by the same vertical offset as the handles.
+ // This improves visibility of the content being selected by
+ // shifting the finger below the content, this is applied once
+ // the user has switched lines.
+ final int touchSlop = viewConfig.getScaledTouchSlop();
+ final float fingerOffset = (mStartHandle != null)
+ ? mStartHandle.getIdealVerticalOffset()
+ : touchSlop;
+ y = eventY - fingerOffset;
+ }
+
+ currLine = getCurrentLineAdjustedForSlop(mTextView.getLayout(), mLineSelectionIsOn,
+ y);
+ if (!mSwitchedLines && currLine != mLineSelectionIsOn) {
+ // Break early here, we want to offset the finger position from
+ // the selection highlight, once the user moved their finger
+ // to a different line we should apply the offset and *not* switch
+ // lines until recomputing the position with the finger offset.
+ mSwitchedLines = true;
+ return;
+ }
+ }
+
+ int startOffset;
+ int offset = mTextView.getOffsetAtCoordinate(currLine, eventX);
+ // Snap to word boundaries.
+ if (mStartOffset < offset) {
+ // Expanding with end handle.
+ offset = getWordEnd(offset);
+ startOffset = getWordStart(mStartOffset);
+ } else {
+ // Expanding with start handle.
+ offset = getWordStart(offset);
+ startOffset = getWordEnd(mStartOffset);
+ }
+ mLineSelectionIsOn = currLine;
+ Selection.setSelection((Spannable) mTextView.getText(),
+ startOffset, offset);
+ }
+
+ private void updateParagraphBasedSelection(MotionEvent event) {
+ final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
+
+ final int start = Math.min(offset, mStartOffset);
+ final int end = Math.max(offset, mStartOffset);
+ final long paragraphsRange = getParagraphsRange(start, end);
+ final int selectionStart = TextUtils.unpackRangeStartFromLong(paragraphsRange);
+ final int selectionEnd = TextUtils.unpackRangeEndFromLong(paragraphsRange);
+ Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd);
+ }
+
/**
* @param event
*/
@@ -4749,8 +4920,12 @@
public void resetTouchOffsets() {
mMinTouchOffset = mMaxTouchOffset = -1;
+ resetDragAcceleratorState();
+ }
+
+ private void resetDragAcceleratorState() {
mStartOffset = -1;
- mDragAcceleratorActive = false;
+ mDragAcceleratorMode = DRAG_ACCELERATOR_MODE_INACTIVE;
mSwitchedLines = false;
}
@@ -4765,7 +4940,7 @@
* @return true if the user is selecting text using the drag accelerator.
*/
public boolean isDragAcceleratorActive() {
- return mDragAcceleratorActive;
+ return mDragAcceleratorMode != DRAG_ACCELERATOR_MODE_INACTIVE;
}
public void onTouchModeChanged(boolean isInTouchMode) {
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 4d9f55c..280ff15 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -21,8 +21,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
+import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -32,6 +36,9 @@
import android.view.ViewHierarchyEncoder;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.R;
+
+
/**
* FrameLayout is designed to block out an area on the screen to display
* a single item. Generally, FrameLayout should be used to hold a single child view, because it can
@@ -164,10 +171,6 @@
mPaddingBottom + mForegroundPaddingBottom;
}
- @Override
- public int findDependentLayoutAxes(View child, int axisFilter) {
- return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
- }
/**
* {@inheritDoc}
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index bdb1e83..ad939be 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -16,7 +16,6 @@
package android.widget;
-import android.view.ViewParent;
import com.android.internal.R;
import android.annotation.IntDef;
@@ -38,8 +37,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
/**
* A Layout that arranges its children in a single column or a single row. The direction of
@@ -647,60 +644,6 @@
}
}
- @Override
- public int findDependentLayoutAxes(View child, int axisFilter) {
- // This implementation is almost exactly equivalent to the default implementation
- // offered to the rest of the framework in ViewGroup, but we treat weight to be
- // functionally equivalent to MATCH_PARENT along the orientation axis.
-
- if (!checkPartialLayoutParams(child, LayoutParams.class)) return axisFilter;
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (child.didLayoutParamsChange()) {
- // Anything could have changed about our previous assumptions.
- return axisFilter;
- }
-
- // Our layout can always end up depending on a WRAP_CONTENT child.
- final int wrapAxisFilter = ((lp.width == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
- | (lp.height == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
-
- if (wrapAxisFilter == axisFilter) {
- // We know all queried axes are affected, just return early.
- return wrapAxisFilter;
- }
-
- // Our layout *may* depend on a MATCH_PARENT child, depending on whether we can determine
- // that our layout will remain stable within our parent. We need to ask.
- int matchAxisFilter = ((lp.width == MATCH_PARENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
- | (lp.height == MATCH_PARENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
-
- // For LinearLayout, a nonzero weight is equivalent to MATCH_PARENT for this purpose.
- if (lp.weight > 0) {
- if (mOrientation == HORIZONTAL) {
- matchAxisFilter |= FLAG_LAYOUT_AXIS_HORIZONTAL & axisFilter;
- } else {
- matchAxisFilter |= FLAG_LAYOUT_AXIS_VERTICAL & axisFilter;
- }
- }
-
- if (matchAxisFilter != 0 || wrapAxisFilter != 0) {
- final ViewParent parent = getParent();
- if (parent != null) {
- // If our parent depends on us for an axis, then our layout can also be affected
- // by a MATCH_PARENT child along that axis.
- return getParent().findDependentLayoutAxes(this, matchAxisFilter)
- | wrapAxisFilter;
- }
-
- // If we don't have a parent, assume we're affected
- // in any determined affected direction.
- return matchAxisFilter | wrapAxisFilter;
- }
-
- // Two exact sizes and LayoutParams didn't change. We're safe.
- return 0;
- }
-
/**
* Determines where to position dividers between children.
*
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index b43ea76..53ca6d1 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1158,7 +1158,7 @@
final View child = obtainView(0, mIsScrap);
// Lay out child directly against the parent measure spec so that
- // we can obtain expected minimum width and height.
+ // we can obtain exected minimum width and height.
measureScrapChild(child, 0, widthMeasureSpec, heightSize);
childWidth = child.getMeasuredWidth();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 56513a3..5574f86 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,7 +17,6 @@
package android.widget;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-
import android.R;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
@@ -115,6 +114,7 @@
import android.view.DragEvent;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
+import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -618,9 +618,6 @@
private final Paint mHighlightPaint;
private boolean mHighlightPathBogus = true;
- private boolean mFirstTouch = false;
- private long mLastTouchUpTime = 0;
-
// Although these fields are specific to editable text, they are not added to Editor because
// they are defined by the TextView's style and are theme-dependent.
int mCursorDrawableRes;
@@ -6814,11 +6811,10 @@
if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
if (!compressText(ellipsisWidth)) {
+ final int height = mLayoutParams.height;
// If the size of the view does not depend on the size of the text, try to
// start the marquee immediately
- final ViewParent parent = getParent();
- if (parent != null && parent.findDependentLayoutAxes(this,
- ViewParent.FLAG_LAYOUT_AXIS_VERTICAL) == 0) {
+ if (height != LayoutParams.WRAP_CONTENT && height != LayoutParams.MATCH_PARENT) {
startMarquee();
} else {
// Defer the start of the marquee until we know our width (see setFrame())
@@ -7217,9 +7213,37 @@
* new view layout.
*/
private void checkForResize() {
- // Always request a layout. The parent will perform the correct version
- // of the intended optimizations as part of requestLayoutForChild.
- requestLayout();
+ boolean sizeChanged = false;
+
+ if (mLayout != null) {
+ // Check if our width changed
+ if (mLayoutParams.width == LayoutParams.WRAP_CONTENT) {
+ sizeChanged = true;
+ invalidate();
+ }
+
+ // Check if our height changed
+ if (mLayoutParams.height == LayoutParams.WRAP_CONTENT) {
+ int desiredHeight = getDesiredHeight();
+
+ if (desiredHeight != this.getHeight()) {
+ sizeChanged = true;
+ }
+ } else if (mLayoutParams.height == LayoutParams.MATCH_PARENT) {
+ if (mDesiredHeightAtMeasure >= 0) {
+ int desiredHeight = getDesiredHeight();
+
+ if (desiredHeight != mDesiredHeightAtMeasure) {
+ sizeChanged = true;
+ }
+ }
+ }
+ }
+
+ if (sizeChanged) {
+ requestLayout();
+ // caller will have already invalidated
+ }
}
/**
@@ -7227,11 +7251,56 @@
* or merely a new text layout.
*/
private void checkForRelayout() {
- // Always request a layout. The parent will perform the correct version
- // of the intended optimizations as part of requestLayoutForChild.
- nullLayouts();
- requestLayout();
- invalidate();
+ // If we have a fixed width, we can just swap in a new text layout
+ // if the text height stays the same or if the view height is fixed.
+
+ if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT ||
+ (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth)) &&
+ (mHint == null || mHintLayout != null) &&
+ (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
+ // Static width, so try making a new text layout.
+
+ int oldht = mLayout.getHeight();
+ int want = mLayout.getWidth();
+ int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();
+
+ /*
+ * No need to bring the text into view, since the size is not
+ * changing (unless we do the requestLayout(), in which case it
+ * will happen at measure).
+ */
+ makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
+ mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
+ false);
+
+ if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
+ // In a fixed-height view, so use our new text layout.
+ if (mLayoutParams.height != LayoutParams.WRAP_CONTENT &&
+ mLayoutParams.height != LayoutParams.MATCH_PARENT) {
+ invalidate();
+ return;
+ }
+
+ // Dynamic height, but height has stayed the same,
+ // so use our new text layout.
+ if (mLayout.getHeight() == oldht &&
+ (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
+ invalidate();
+ return;
+ }
+ }
+
+ // We lose: the height has changed and we have a dynamic height.
+ // Request a new view layout using our new text layout.
+ requestLayout();
+ invalidate();
+ } else {
+ // Dynamic width, so we have no choice but to request a new
+ // view layout with a new text layout.
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
}
@Override
@@ -8334,23 +8403,6 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getActionMasked();
-
- if (mEditor != null && action == MotionEvent.ACTION_DOWN) {
- // Detect double tap and inform the Editor.
- if (mFirstTouch && (SystemClock.uptimeMillis() - mLastTouchUpTime) <=
- ViewConfiguration.getDoubleTapTimeout()) {
- mEditor.mDoubleTap = true;
- mFirstTouch = false;
- } else {
- mEditor.mDoubleTap = false;
- mFirstTouch = true;
- }
- }
-
- if (action == MotionEvent.ACTION_UP) {
- mLastTouchUpTime = SystemClock.uptimeMillis();
- }
-
if (mEditor != null) {
mEditor.onTouchEvent(event);
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 6e56513..acbf5eb 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -1368,11 +1368,6 @@
}
@Override
- public int findDependentLayoutAxes(View child, int axisFilter) {
- return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 0;
int height = 0;
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index e607a3f..85cc841 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -175,7 +175,7 @@
}
private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis =
- new TreeMap<InputMethodInfo, List<InputMethodSubtype>>(
+ new TreeMap<>(
new Comparator<InputMethodInfo>() {
@Override
public int compare(InputMethodInfo imi1, InputMethodInfo imi2) {
@@ -192,14 +192,9 @@
}
});
- public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() {
- return getSortedInputMethodAndSubtypeList(true, false, false);
- }
-
public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
- boolean showSubtypes, boolean includeAuxiliarySubtypes, boolean isScreenLocked) {
- final ArrayList<ImeSubtypeListItem> imList =
- new ArrayList<ImeSubtypeListItem>();
+ boolean includeAuxiliarySubtypes, boolean isScreenLocked) {
+ final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
mContext);
@@ -219,12 +214,12 @@
continue;
}
List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
- HashSet<String> enabledSubtypeSet = new HashSet<String>();
+ HashSet<String> enabledSubtypeSet = new HashSet<>();
for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
}
final CharSequence imeLabel = imi.loadLabel(mPm);
- if (showSubtypes && enabledSubtypeSet.size() > 0) {
+ if (enabledSubtypeSet.size() > 0) {
final int subtypeCount = imi.getSubtypeCount();
if (DEBUG) {
Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
@@ -532,7 +527,8 @@
public void resetCircularListLocked(Context context) {
mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
mController = ControllerImpl.createFrom(mController,
- mSubtypeList.getSortedInputMethodAndSubtypeList());
+ mSubtypeList.getSortedInputMethodAndSubtypeList(
+ false /* includeAuxiliarySubtypes */, false /* isScreenLocked */));
}
public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
@@ -546,10 +542,10 @@
return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
}
- public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes,
+ public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(
boolean includingAuxiliarySubtypes, boolean isScreenLocked) {
return mSubtypeList.getSortedInputMethodAndSubtypeList(
- showSubtypes, includingAuxiliarySubtypes, isScreenLocked);
+ includingAuxiliarySubtypes, isScreenLocked);
}
public void dump(final Printer pw) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4a969b2..cc815c4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -7732,26 +7732,35 @@
}
final Uid u = getUidStatsLocked(mapUid(entry.uid));
- u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
- entry.rxPackets);
- u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
- entry.txPackets);
- rxPackets.put(u.getUid(), entry.rxPackets);
- txPackets.put(u.getUid(), entry.txPackets);
+ if (entry.rxBytes != 0) {
+ u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
+ entry.rxPackets);
+ mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+ entry.rxBytes);
+ mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+ entry.rxPackets);
- // Sum the total number of packets so that the Rx Power and Tx Power can
- // be evenly distributed amongst the apps.
- totalRxPackets += entry.rxPackets;
- totalTxPackets += entry.txPackets;
+ rxPackets.put(u.getUid(), entry.rxPackets);
- mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
- entry.rxBytes);
- mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
- entry.txBytes);
- mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
- entry.rxPackets);
- mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
- entry.txPackets);
+ // Sum the total number of packets so that the Rx Power can
+ // be evenly distributed amongst the apps.
+ totalRxPackets += entry.rxPackets;
+ }
+
+ if (entry.txBytes != 0) {
+ u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
+ entry.txPackets);
+ mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+ entry.txBytes);
+ mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+ entry.txPackets);
+
+ txPackets.put(u.getUid(), entry.txPackets);
+
+ // Sum the total number of packets so that the Tx Power can
+ // be evenly distributed amongst the apps.
+ totalTxPackets += entry.txPackets;
+ }
}
}
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 75ca639..1b44ff3 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -63,17 +63,18 @@
private boolean mReportNextDraw;
private Drawable mCaptionBackgroundDrawable;
+ private Drawable mUserCaptionBackgroundDrawable;
private Drawable mResizingBackgroundDrawable;
private ColorDrawable mStatusBarColor;
public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
- int statusBarColor) {
+ Drawable userCaptionBackgroundDrawable, int statusBarColor) {
setName("ResizeFrame");
mRenderer = renderer;
onResourcesLoaded(decorView, resizingBackgroundDrawable, captionBackgroundDrawable,
- statusBarColor);
+ userCaptionBackgroundDrawable, statusBarColor);
// Create a render node for the content and frame backdrop
// which can be resized independently from the content.
@@ -92,10 +93,12 @@
}
void onResourcesLoaded(DecorView decorView, Drawable resizingBackgroundDrawable,
- Drawable captionBackgroundDrawableDrawable, int statusBarColor) {
+ Drawable captionBackgroundDrawableDrawable, Drawable userCaptionBackgroundDrawable,
+ int statusBarColor) {
mDecorView = decorView;
mResizingBackgroundDrawable = resizingBackgroundDrawable;
mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable;
+ mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable;
if (statusBarColor != 0) {
mStatusBarColor = new ColorDrawable(statusBarColor);
addSystemBarNodeIfNeeded();
@@ -281,8 +284,10 @@
// Draw the caption and content backdrops in to our render node.
DisplayListCanvas canvas = mFrameAndBackdropNode.start(width, height);
- mCaptionBackgroundDrawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
- mCaptionBackgroundDrawable.draw(canvas);
+ final Drawable drawable = mUserCaptionBackgroundDrawable != null
+ ? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable;
+ drawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
+ drawable.draw(canvas);
// The backdrop: clear everything with the background. Clipping is done elsewhere.
mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
@@ -324,4 +329,8 @@
mChoreographer.postFrameCallback(this);
}
}
+
+ void setUserCaptionBackgroundDrawable(Drawable userCaptionBackgroundDrawable) {
+ mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable;
+ }
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 531ba2f..e405564 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -74,6 +74,8 @@
import static android.view.View.MeasureSpec.getMode;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
+import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
@@ -184,9 +186,10 @@
private boolean mWindowResizeCallbacksAdded = false;
- BackdropFrameRenderer mBackdropFrameRenderer = null;
+ private BackdropFrameRenderer mBackdropFrameRenderer = null;
private Drawable mResizingBackgroundDrawable;
private Drawable mCaptionBackgroundDrawable;
+ private Drawable mUserCaptionBackgroundDrawable;
DecorView(Context context, int featureId, PhoneWindow window) {
super(context);
@@ -1580,18 +1583,20 @@
initializeElevation();
}
- View onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
+ void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
mStackId = getStackId();
mResizingBackgroundDrawable = getResizingBackgroundDrawable(
mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
- mCaptionBackgroundDrawable =
- getContext().getDrawable(R.drawable.decor_caption_title_focused);
+ if (mCaptionBackgroundDrawable == null) {
+ mCaptionBackgroundDrawable = getContext().getDrawable(
+ R.drawable.decor_caption_title_focused);
+ }
if (mBackdropFrameRenderer != null) {
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
- getCurrentColor(mStatusColorViewState));
+ mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
}
mDecorCaptionView = createDecorCaptionView(inflater);
@@ -1608,17 +1613,16 @@
}
mContentRoot = (ViewGroup) root;
initializeElevation();
- return root;
}
// Free floating overlapping windows require a caption.
private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
- DecorCaptionView DecorCaptionView = null;
- for (int i = getChildCount() - 1; i >= 0 && DecorCaptionView == null; i--) {
+ DecorCaptionView decorCaptionView = null;
+ for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
View view = getChildAt(i);
if (view instanceof DecorCaptionView) {
// The decor was most likely saved from a relaunch - so reuse it.
- DecorCaptionView = (DecorCaptionView) view;
+ decorCaptionView = (DecorCaptionView) view;
removeViewAt(i);
}
}
@@ -1630,27 +1634,72 @@
&& ActivityManager.StackId.hasWindowDecor(mStackId)) {
// Dependent on the brightness of the used title we either use the
// dark or the light button frame.
- if (DecorCaptionView == null) {
- Context context = getContext();
- TypedValue value = new TypedValue();
- context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
- inflater = inflater.from(context);
- if (Color.luminance(value.data) < 0.5) {
- DecorCaptionView = (DecorCaptionView) inflater.inflate(
- R.layout.decor_caption_dark, null);
- } else {
- DecorCaptionView = (DecorCaptionView) inflater.inflate(
- R.layout.decor_caption_light, null);
- }
+ if (decorCaptionView == null) {
+ decorCaptionView = inflateDecorCaptionView(inflater);
}
- DecorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
+ decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
} else {
- DecorCaptionView = null;
+ decorCaptionView = null;
}
// Tell the decor if it has a visible caption.
- enableCaption(DecorCaptionView != null);
- return DecorCaptionView;
+ enableCaption(decorCaptionView != null);
+ return decorCaptionView;
+ }
+
+ private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
+ final Context context = getContext();
+ // We make a copy of the inflater, so it has the right context associated with it.
+ inflater = inflater.from(context);
+ final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
+ null);
+ setDecorCaptionShade(context, view);
+ return view;
+ }
+
+ private void setDecorCaptionShade(Context context, DecorCaptionView view) {
+ final int shade = mWindow.getDecorCaptionShade();
+ switch (shade) {
+ case DECOR_CAPTION_SHADE_LIGHT:
+ setLightDecorCaptionShade(view);
+ break;
+ case DECOR_CAPTION_SHADE_DARK:
+ setDarkDecorCaptionShade(view);
+ break;
+ default: {
+ TypedValue value = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
+ // We invert the shade depending on brightness of the theme. Dark shade for light
+ // theme and vice versa. Thanks to this the buttons should be visible on the
+ // background.
+ if (Color.luminance(value.data) < 0.5) {
+ setLightDecorCaptionShade(view);
+ } else {
+ setDarkDecorCaptionShade(view);
+ }
+ break;
+ }
+ }
+ }
+
+ void updateDecorCaptionShade() {
+ if (mDecorCaptionView != null) {
+ setDecorCaptionShade(getContext(), mDecorCaptionView);
+ }
+ }
+
+ private void setLightDecorCaptionShade(DecorCaptionView view) {
+ view.findViewById(R.id.maximize_window).setBackgroundResource(
+ R.drawable.decor_maximize_button_light);
+ view.findViewById(R.id.close_window).setBackgroundResource(
+ R.drawable.decor_close_button_light);
+ }
+
+ private void setDarkDecorCaptionShade(DecorCaptionView view) {
+ view.findViewById(R.id.maximize_window).setBackgroundResource(
+ R.drawable.decor_maximize_button_dark);
+ view.findViewById(R.id.close_window).setBackgroundResource(
+ R.drawable.decor_close_button_dark);
}
/**
@@ -1735,11 +1784,11 @@
if (mBackdropFrameRenderer != null) {
return;
}
- final ThreadedRenderer renderer = (ThreadedRenderer) getHardwareRenderer();
+ final ThreadedRenderer renderer = getHardwareRenderer();
if (renderer != null) {
mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
- getCurrentColor(mStatusColorViewState));
+ mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
// Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
// If we want to get the shadow shown while resizing, we would need to elevate a new
@@ -1849,6 +1898,16 @@
getResources().getDisplayMetrics());
}
+ /**
+ * Provide an override of the caption background drawable.
+ */
+ void setUserCaptionBackgroundDrawable(Drawable drawable) {
+ mUserCaptionBackgroundDrawable = drawable;
+ if (mBackdropFrameRenderer != null) {
+ mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
+ }
+ }
+
private static class ColorViewState {
View view = null;
int targetVisibility = View.INVISIBLE;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 337bb69..86bd782 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -94,7 +94,6 @@
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -273,6 +272,8 @@
private boolean mIsStartingWindow;
private int mTheme = -1;
+ private int mDecorCaptionShade = DECOR_CAPTION_SHADE_AUTO;
+
static class WindowManagerHolder {
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
@@ -2527,7 +2528,7 @@
}
mDecor.startChanging();
- final View in = mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
+ mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
@@ -3720,4 +3721,21 @@
}
}
}
+
+ @Override
+ public void setResizingCaptionDrawable(Drawable drawable) {
+ mDecor.setUserCaptionBackgroundDrawable(drawable);
+ }
+
+ @Override
+ public void setDecorCaptionShade(int decorCaptionShade) {
+ mDecorCaptionShade = decorCaptionShade;
+ if (mDecor != null) {
+ mDecor.updateDecorCaptionShade();
+ }
+ }
+
+ int getDecorCaptionShade() {
+ return mDecorCaptionShade;
+ }
}
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index 3e65320..c3a7460 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -360,11 +360,6 @@
}
@Override
- public int findDependentLayoutAxes(View child, int axisFilter) {
- return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
pullChildren();
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index d747686..c3fe9e7 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -319,6 +319,10 @@
captionHeight + mContent.getMeasuredHeight());
}
}
+
+ // This assumes that the caption bar is at the top.
+ mOwner.notifyRestrictedCaptionAreaCallback(mMaximize.getLeft(), mMaximize.getTop(),
+ mClose.getRight(), mClose.getBottom());
}
/**
* Determine if the workspace is entirely covered by the window.
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index dac6d96..ab0df55 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -56,10 +56,11 @@
return result;
}
-static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path) {
+static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path,
+ jint ttcIndex) {
NPE_CHECK_RETURN_ZERO(env, path);
ScopedUtfChars str(env, path);
- SkTypeface* face = SkTypeface::CreateFromFile(str.c_str());
+ SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
if (face == NULL) {
ALOGE("addFont failed to create font %s", str.c_str());
return false;
@@ -69,10 +70,10 @@
}
static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr,
- jstring path, jint weight, jboolean isItalic) {
+ jstring path, jint ttcIndex, jint weight, jboolean isItalic) {
NPE_CHECK_RETURN_ZERO(env, path);
ScopedUtfChars str(env, path);
- SkTypeface* face = SkTypeface::CreateFromFile(str.c_str());
+ SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
if (face == NULL) {
ALOGE("addFont failed to create font %s", str.c_str());
return false;
@@ -127,8 +128,8 @@
static const JNINativeMethod gFontFamilyMethods[] = {
{ "nCreateFamily", "(Ljava/lang/String;I)J", (void*)FontFamily_create },
{ "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
- { "nAddFont", "(JLjava/lang/String;)Z", (void*)FontFamily_addFont },
- { "nAddFontWeightStyle", "(JLjava/lang/String;IZ)Z", (void*)FontFamily_addFontWeightStyle },
+ { "nAddFont", "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont },
+ { "nAddFontWeightStyle", "(JLjava/lang/String;IIZ)Z", (void*)FontFamily_addFontWeightStyle },
{ "nAddFontFromAsset", "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
(void*)FontFamily_addFontFromAsset },
};
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 1ee7ea8..2488111 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -595,6 +595,36 @@
MEMINFO_COUNT
};
+static long long get_zram_mem_used()
+{
+#define ZRAM_SYSFS "/sys/block/zram0/"
+ FILE *f = fopen(ZRAM_SYSFS "mm_stat", "r");
+ if (f) {
+ long long mem_used_total = 0;
+
+ int matched = fscanf(f, "%*d %*d %lld %*d %*d %*d %*d", &mem_used_total);
+ if (matched != 1)
+ ALOGW("failed to parse " ZRAM_SYSFS "mm_stat");
+
+ fclose(f);
+ return mem_used_total;
+ }
+
+ f = fopen(ZRAM_SYSFS "mem_used_total", "r");
+ if (f) {
+ long long mem_used_total = 0;
+
+ int matched = fscanf(f, "%lld", &mem_used_total);
+ if (matched != 1)
+ ALOGW("failed to parse " ZRAM_SYSFS "mem_used_total");
+
+ fclose(f);
+ return mem_used_total;
+ }
+
+ return 0;
+}
+
static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
{
char buffer[1024];
@@ -680,15 +710,7 @@
if (*p) p++;
}
- fd = open("/sys/block/zram0/mem_used_total", O_RDONLY);
- if (fd >= 0) {
- len = read(fd, buffer, sizeof(buffer)-1);
- close(fd);
- if (len > 0) {
- buffer[len] = 0;
- mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer)/1024;
- }
- }
+ mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used() / 1024;
// Recompute Vmalloc Used since the value in meminfo
// doesn't account for I/O remapping which doesn't use RAM.
mem[MEMINFO_VMALLOC_USED] = get_allocated_vmalloc_memory() / 1024;
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index ff51e4e..f6e68c4 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -149,7 +149,9 @@
case PublicFormat::DEPTH16:
return HAL_DATASPACE_DEPTH;
case PublicFormat::RAW_SENSOR:
+ case PublicFormat::RAW_PRIVATE:
case PublicFormat::RAW10:
+ case PublicFormat::RAW12:
return HAL_DATASPACE_ARBITRARY;
case PublicFormat::YUV_420_888:
case PublicFormat::NV21:
@@ -170,6 +172,7 @@
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_Y8:
case HAL_PIXEL_FORMAT_RAW10:
+ case HAL_PIXEL_FORMAT_RAW12:
case HAL_PIXEL_FORMAT_YCbCr_420_888:
case HAL_PIXEL_FORMAT_YV12:
// Enums overlap in both name and value
@@ -177,6 +180,9 @@
case HAL_PIXEL_FORMAT_RAW16:
// Name differs, though value is the same
return PublicFormat::RAW_SENSOR;
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ // Name differs, though value is the same
+ return PublicFormat::RAW_PRIVATE;
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
// Name differs, though the value is the same
return PublicFormat::NV16;
@@ -212,7 +218,6 @@
}
break;
case HAL_PIXEL_FORMAT_BGRA_8888:
- case HAL_PIXEL_FORMAT_RAW_OPAQUE:
// Not defined in public API
return PublicFormat::UNKNOWN;
diff --git a/core/res/Android.mk b/core/res/Android.mk
index cfc791d..a1bef83 100644
--- a/core/res/Android.mk
+++ b/core/res/Android.mk
@@ -23,6 +23,7 @@
# Tell aapt to create "extending (non-application)" resource IDs,
# since these resources will be used by many apps.
LOCAL_AAPT_FLAGS := -x
+LOCAL_AAPT_FLAGS += --private-symbols com.android.internal
LOCAL_MODULE_TAGS := optional
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5251b20..1127197 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -277,6 +277,7 @@
<protected-broadcast android:name="android.intent.action.ADVANCED_SETTINGS" />
<protected-broadcast android:name="android.intent.action.APPLICATION_RESTRICTIONS_CHANGED" />
<protected-broadcast android:name="android.intent.action.BUGREPORT_FINISHED" />
+ <protected-broadcast android:name="android.intent.action.BUGREPORT_STARTED" />
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
diff --git a/core/res/res/drawable/ic_decor_close_button_dark_focused.xml b/core/res/res/drawable/ic_decor_close_button_dark_focused.xml
index d7b167dd..0794ed3 100644
--- a/core/res/res/drawable/ic_decor_close_button_dark_focused.xml
+++ b/core/res/res/drawable/ic_decor_close_button_dark_focused.xml
@@ -23,7 +23,7 @@
android:translateX="8.0"
android:translateY="8.0" >
<path
- android:fillColor="#FFFFFFFF"
+ android:fillColor="#ff000000"
android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
</group>
</vector>
diff --git a/core/res/res/drawable/ic_decor_close_button_dark_unfocused.xml b/core/res/res/drawable/ic_decor_close_button_dark_unfocused.xml
index e2e81b9..bd1db51 100644
--- a/core/res/res/drawable/ic_decor_close_button_dark_unfocused.xml
+++ b/core/res/res/drawable/ic_decor_close_button_dark_unfocused.xml
@@ -23,7 +23,7 @@
android:translateX="8.0"
android:translateY="8.0" >
<path
- android:fillColor="#33FFFFFF"
+ android:fillColor="#33000000"
android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
</group>
</vector>
diff --git a/core/res/res/drawable/ic_decor_close_button_light_focused.xml b/core/res/res/drawable/ic_decor_close_button_light_focused.xml
index 0794ed3..d7b167dd 100644
--- a/core/res/res/drawable/ic_decor_close_button_light_focused.xml
+++ b/core/res/res/drawable/ic_decor_close_button_light_focused.xml
@@ -23,7 +23,7 @@
android:translateX="8.0"
android:translateY="8.0" >
<path
- android:fillColor="#ff000000"
+ android:fillColor="#FFFFFFFF"
android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
</group>
</vector>
diff --git a/core/res/res/drawable/ic_decor_close_button_light_unfocused.xml b/core/res/res/drawable/ic_decor_close_button_light_unfocused.xml
index bd1db51..e2e81b9 100644
--- a/core/res/res/drawable/ic_decor_close_button_light_unfocused.xml
+++ b/core/res/res/drawable/ic_decor_close_button_light_unfocused.xml
@@ -23,7 +23,7 @@
android:translateX="8.0"
android:translateY="8.0" >
<path
- android:fillColor="#33000000"
+ android:fillColor="#33FFFFFF"
android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
</group>
</vector>
diff --git a/core/res/res/drawable/ic_decor_maximize_button_dark_focused.xml b/core/res/res/drawable/ic_decor_maximize_button_dark_focused.xml
index 73d808b..c23390e 100644
--- a/core/res/res/drawable/ic_decor_maximize_button_dark_focused.xml
+++ b/core/res/res/drawable/ic_decor_maximize_button_dark_focused.xml
@@ -23,10 +23,10 @@
android:translateX="8.0"
android:translateY="8.0" >
<path
- android:fillColor="#FFFFFFFF"
+ android:fillColor="#FF000000"
android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
<path
- android:fillColor="#B2FFFFFF"
+ android:fillColor="#B2000000"
android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
</group>
</vector>
diff --git a/core/res/res/drawable/ic_decor_maximize_button_dark_unfocused.xml b/core/res/res/drawable/ic_decor_maximize_button_dark_unfocused.xml
index dc79e10..a194a39 100644
--- a/core/res/res/drawable/ic_decor_maximize_button_dark_unfocused.xml
+++ b/core/res/res/drawable/ic_decor_maximize_button_dark_unfocused.xml
@@ -23,10 +23,10 @@
android:translateX="8.0"
android:translateY="8.0" >
<path
- android:fillColor="#33FFFFFF"
+ android:fillColor="#33000000"
android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
<path
- android:fillColor="#33FFFFFF"
+ android:fillColor="#33000000"
android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
</group>
</vector>
diff --git a/core/res/res/drawable/ic_decor_maximize_button_light_focused.xml b/core/res/res/drawable/ic_decor_maximize_button_light_focused.xml
index c23390e..73d808b 100644
--- a/core/res/res/drawable/ic_decor_maximize_button_light_focused.xml
+++ b/core/res/res/drawable/ic_decor_maximize_button_light_focused.xml
@@ -23,10 +23,10 @@
android:translateX="8.0"
android:translateY="8.0" >
<path
- android:fillColor="#FF000000"
+ android:fillColor="#FFFFFFFF"
android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
<path
- android:fillColor="#B2000000"
+ android:fillColor="#B2FFFFFF"
android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
</group>
</vector>
diff --git a/core/res/res/drawable/ic_decor_maximize_button_light_unfocused.xml b/core/res/res/drawable/ic_decor_maximize_button_light_unfocused.xml
index a194a39..dc79e10 100644
--- a/core/res/res/drawable/ic_decor_maximize_button_light_unfocused.xml
+++ b/core/res/res/drawable/ic_decor_maximize_button_light_unfocused.xml
@@ -23,10 +23,10 @@
android:translateX="8.0"
android:translateY="8.0" >
<path
- android:fillColor="#33000000"
+ android:fillColor="#33FFFFFF"
android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
<path
- android:fillColor="#33000000"
+ android:fillColor="#33FFFFFF"
android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
</group>
</vector>
diff --git a/core/res/res/layout/decor_caption.xml b/core/res/res/layout/decor_caption.xml
new file mode 100644
index 0000000..0246736
--- /dev/null
+++ b/core/res/res/layout/decor_caption.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<com.android.internal.widget.DecorCaptionView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:descendantFocusability="beforeDescendants" >
+ <LinearLayout
+ android:id="@+id/caption"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="end"
+ android:background="@drawable/decor_caption_title"
+ android:focusable="false"
+ android:descendantFocusability="blocksDescendants" >
+ <Button
+ android:id="@+id/maximize_window"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_margin="5dp"
+ android:padding="4dp"
+ android:layout_gravity="center_vertical|end"
+ android:contentDescription="@string/maximize_button_text"
+ android:background="@drawable/decor_maximize_button_dark" />
+ <Button
+ android:id="@+id/close_window"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_margin="5dp"
+ android:padding="4dp"
+ android:layout_gravity="center_vertical|end"
+ android:contentDescription="@string/close_button_text"
+ android:background="@drawable/decor_close_button_dark" />
+ </LinearLayout>
+</com.android.internal.widget.DecorCaptionView>
diff --git a/core/res/res/layout/decor_caption_dark.xml b/core/res/res/layout/decor_caption_dark.xml
deleted file mode 100644
index 95d2289..0000000
--- a/core/res/res/layout/decor_caption_dark.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<com.android.internal.widget.DecorCaptionView xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:descendantFocusability="beforeDescendants" >
- <LinearLayout
- android:id="@+id/caption"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="end"
- android:background="@drawable/decor_caption_title"
- android:focusable="false"
- android:descendantFocusability="blocksDescendants" >
- <Button
- android:id="@+id/maximize_window"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_margin="5dp"
- android:padding="4dp"
- android:layout_gravity="center_vertical|end"
- android:contentDescription="@string/maximize_button_text"
- android:background="@drawable/decor_maximize_button_dark" />
- <Button
- android:id="@+id/close_window"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_margin="5dp"
- android:padding="4dp"
- android:layout_gravity="center_vertical|end"
- android:contentDescription="@string/close_button_text"
- android:background="@drawable/decor_close_button_dark" />
- </LinearLayout>
-</com.android.internal.widget.DecorCaptionView>
diff --git a/core/res/res/layout/decor_caption_light.xml b/core/res/res/layout/decor_caption_light.xml
deleted file mode 100644
index f0f661e..0000000
--- a/core/res/res/layout/decor_caption_light.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<com.android.internal.widget.DecorCaptionView xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:descendantFocusability="beforeDescendants" >
- <LinearLayout
- android:id="@+id/caption"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="end"
- android:background="@drawable/decor_caption_title"
- android:focusable="false"
- android:descendantFocusability="blocksDescendants" >
- <Button
- android:id="@+id/maximize_window"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_margin="5dp"
- android:padding="4dp"
- android:layout_gravity="center_vertical|end"
- android:contentDescription="@string/maximize_button_text"
- android:background="@drawable/decor_maximize_button_light" />
- <Button
- android:id="@+id/close_window"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_margin="5dp"
- android:padding="4dp"
- android:layout_gravity="center_vertical|end"
- android:contentDescription="@string/close_button_text"
- android:background="@drawable/decor_close_button_light" />
- </LinearLayout>
-</com.android.internal.widget.DecorCaptionView>
diff --git a/core/res/res/layout/notification_material_action.xml b/core/res/res/layout/notification_material_action.xml
index 62602d8..398f52d 100644
--- a/core/res/res/layout/notification_material_action.xml
+++ b/core/res/res/layout/notification_material_action.xml
@@ -22,7 +22,7 @@
android:layout_height="48dp"
android:layout_gravity="center"
android:layout_marginStart="4dp"
- android:textColor="@color/secondary_text_material_light"
+ android:textColor="@color/notification_default_color"
android:singleLine="true"
android:ellipsize="end"
android:background="@drawable/notification_material_action_background"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index b5ba695..eaf82fb 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Opgedateer deur jou administrateur"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Deur jou administrateur uitgevee"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Om batterylewe te help verbeter, verminder batterybespaarder jou toestel se werkverrigting en beperk vibrasie, liggingdienste en die meeste agtergronddata. E-pos, boodskappe en ander programme wat op sinkronisering staatmaak, sal dalk nie opdateer tensy jy hulle oopmaak nie.\n\nBatterybespaarder skakel outomaties af wanneer jou toestel besig is om te laai."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Geblokkeer: Moet nooit hierdie kennisgewings wys nie"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Laag: Wys sonder klank aan die onderkant van die kennisgewinglys"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normaal: Wys hierdie kennisgewings sonder klank"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Hoog: Wys aan die bokant van die kennisgewingslys en maak \'n geluid"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Dringend: Verskyn vlugtig op die skerm en maak \'n geluid"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d minute lank (tot <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">Een minuut lank (tot <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 3f0c977..91244e6 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"আপনার প্রশাসক দ্বারা আপডেট করা হয়েছে"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"আপনার প্রশাসক দ্বারা মুছে ফেলা হয়েছে"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"ব্যাটরির লাইফ উন্নত করতে সহায়তা করতে, ব্যাটারি সাশ্রয়কারী আপনার ডিভাইসের কার্যসম্পাদনা হ্রাস করে এবং কম্পন, অবস্থান পরিষেবাসমূহ এবং অধিকাংশ ব্যাকগ্রাউন্ড ডেটা সীমিত করে৷ ইমেল, বার্তাপ্রেরণ এবং অন্যান্য অ্যাপ্লিকেশানগুলিকে যেগুলি সিঙ্কের উপর নির্ভর করে সেগুলিকে আপনি না খোলা পর্যন্ত নাও আপডেট হতে পারে৷\n\nআপনার ডিভাইসটিকে যখন চার্জ করা হয় তখন ব্যাটারি সাশ্রয়কারী স্বয়ংক্রিয়ভাবে বন্ধ হয়ে যায়৷"</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"অবরুদ্ধ: এই বিজ্ঞপ্তিগুলি কখনই দেখানো হবে না"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"নিম্ন: বিজ্ঞপ্তি তালিকার নীচের অংশে নিঃশব্দে প্রদর্শন করা হয়"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"সাধারন: এই বিজ্ঞপ্তিগুলি নিঃশব্দে প্রদর্শন করা হয়"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"উচ্চ: বিজ্ঞপ্তি তালিকার শীর্ষে দেখানো হয় এবং শব্দ করে"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"জরুরী: স্ক্রীনের উপরে প্রদর্শিত হয় এবং শব্দ করে"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">%1$d মিনিটের জন্য (<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g> পর্যন্ত)</item>
<item quantity="other">%1$d মিনিটের জন্য (<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g> পর্যন্ত)</item>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index d3bb987..0d2b46c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"L\'administrador l\'ha actualitzat"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"L\'administrador ho ha suprimit"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Per tal d\'augmentar la durada de la bateria, la funció d\'estalvi de bateria redueix el rendiment del dispositiu i en limita la vibració i la majoria de dades en segon pla. És possible que el correu electrònic, la missatgeria i la resta d\'aplicacions que se sincronitzen amb freqüència no s\'actualitzin llevat que les obris.\n\nL\'estalvi de bateria es desactiva automàticament mentre el dispositiu s\'està carregant."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Bloquejada: no mostra mai aquestes notificacions"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Baixa: mostra de manera silenciosa a la part inferior de la llista de notificacions"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normal: mostra aquestes notificacions de manera silenciosa"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Alta: mostra a la part superior de la llista de notificacions i emet un so"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Urgent: mostra a la pantalla i emet un so"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">Durant %1$d minuts (fins a les <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">Durant 1 minut (fins a les <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index e803ae4..4c554bc 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1458,16 +1458,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Aktualizováno administrátorem"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Smazáno administrátorem"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Spořič baterie za účelem prodloužení výdrže baterie snižuje výkon zařízení a omezuje vibrace, služby určování polohy a většinu dat na pozadí. E-mail, aplikace pro zasílání zpráv a další aplikace, které používají synchronizaci, se nemusejí aktualizovat, dokud je neotevřete.\n\nPři nabíjení zařízení se spořič baterie automaticky vypne."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Blokováno: Tato oznámení nikdy nezobrazovat"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Nízká: Tato oznámení zobrazovat na konci seznamu bez zvukového upozornění"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normální: Tato oznámení zobrazovat bez zvukového upozornění"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Vysoká: Tato oznámení zobrazovat na začátku seznamu a upozornit na ně zvukem"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Urgentní: Tato oznámení zobrazovat přímo na obrazovce a upozornit na ně zvukem"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="few">%1$d minuty (do <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="many">%1$d minuty (do <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index cba5494..ab45504 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Von Ihrem Administrator aktualisiert"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Von Ihrem Administrator gelöscht"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Der Energiesparmodus schont den Akku, indem er die Leistung des Geräts reduziert und die Vibrationsfunktion sowie die meisten Hintergrunddatenaktivitäten einschränkt. E-Mail, SMS/MMS und andere Apps, die auf Ihrem Gerät synchronisiert werden, werden möglicherweise erst nach dem Öffnen aktualisiert.\n\nDer Energiesparmodus wird automatisch deaktiviert, wenn Ihr Gerät aufgeladen wird."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Blockiert: Keine Benachrichtigungen anzeigen"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Niedrig: Benachrichtigungen ganz unten in der Benachrichtigungsliste und ohne Ton anzeigen"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normal: Benachrichtigungen ohne Ton anzeigen"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Hoch: Benachrichtigungen ganz oben in der Benachrichtigungsliste und mit Ton anzeigen"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Dringend: Mit Ton auf dem Display einblenden"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d Minuten (bis <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">1 Minute (bis <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index e3842b2..d5fdd29 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Ενημερώθηκε από το διαχειριστή σας"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Διαγράφηκε από το διαχειριστή σας"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Προκειμένου να βελτιώσει τη διάρκεια ζωής της μπαταρίας σας, η Εξοικονόμηση μπαταρίας μειώνει την απόδοση της συσκευής σας και περιορίζει λειτουργίες όπως η δόνηση, οι υπηρεσίες τοποθεσίας και τα περισσότερα δεδομένα παρασκηνίου. Το ηλεκτρονικό ταχυδρομείο, η ανταλλαγή μηνυμάτων και άλλες εφαρμογές που βασίζονται στο συγχρονισμό ενδέχεται να μην ενημερώνονται έως ότου τις ανοίξετε.\n\nΗ Εξοικονόμηση μπαταρίας απενεργοποιείται αυτόματα όταν η συσκευή σας φορτίζει."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Αποκλεισμένες: Να μην εμφανίζονται ποτέ αυτές οι ειδοποιήσεις"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Χαμηλής βαρύτητας: Να εμφανίζονται στο κάτω τμήμα της λίστας ειδοποιήσεων χωρίς τη συνοδεία ήχου"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Κανονική βαρύτητα: Να εμφανίζονται αυτές οι ειδοποιήσεις χωρίς ήχο"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Υψηλής βαρύτητας: Να εμφανίζονται στην κορυφή της λίστας ειδοποιήσεων συνοδευόμενες από κάποιον ήχο"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Επείγουσες: Να προβάλλονται στην οθόνη και να συνοδεύονται από κάποιον ήχο"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">Για %1$d λεπτά (έως τις <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">Για ένα λεπτό (έως τις <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 807e36f..4e2029f 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Actualizado por el administrador"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Lo eliminó el administrador."</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Para ayudar a mejorar la duración de la batería, el ahorro de batería reduce el rendimiento del dispositivo y limita la vibración, los servicios de ubicación y la mayoría de los datos en segundo plano. Es posible que el correo electrónico, la mensajería y otras aplicaciones que se basan en la sincronización no puedan actualizarse, a menos que los abras.\n\nEl ahorro de batería se desactiva de forma automática cuando el dispositivo se está cargando."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Bloqueada: no mostrar nunca estas notificaciones"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Baja: mostrar en la parte inferior de la lista de notificación sin emitir sonido"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normal: mostrar estas notificaciones de manera silenciosa"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Alta: mostrar en la parte superior de la lista de notificaciones y emitir sonido"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Urgente: mostrar en la pantalla y emitir sonido"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">Durante %1$d minutos hasta la(s) <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g></item>
<item quantity="one">Durante 1 minuto; hasta la(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g></item>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index c209669..3bbdcdf 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Värskendas administraator"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Kustutas teie administraator"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Aku kestuse parandamiseks vähendab akusäästja teie seadme toimivust ning piirab vibratsiooni, asukohateenuseid ja suuremat osa taustaandmetest. E-posti, sõnumsidet ja muid sünkroonimisele tuginevaid rakendusi võidakse värskendada ainult siis, kui te need avate.\n\nAkusäästja lülitatakse seadme laadimise ajal automaatselt välja."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Blokeeritud: ära kunagi näita neid märguandeid"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Madal: kuva vaikselt märguannete loendi allosas"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Tavaline: kuva need märguanded vaikselt"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Kõrge: kuva märguannete loendi ülaosas koos heliga"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Kiireloomuline: kuva ekraani servas koos heliga"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d minutiks (kuni <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">Üheks minutiks (kuni <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 7b11238..9cbb29b 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"توسط سرپرست شما بهروزرسانی شد"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"توسط سرپرستتان حذف شد"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"برای کمک به بهبود عمر باتری، بهینهسازی باتری عملکرد دستگاهتان را کاهش میدهد و لرزش، سرویسهای مبتنی بر مکان، و دسترسی به اکثر دادهها در پسزمینه را محدود میکند. ایمیل، پیامرسانی و برنامههای دیگری که به همگامسازی وابستهاند، تا زمانیکه آنها را باز نکنید نمیتوانند بهروز شوند.\n\nبهینهسازی باتری بهصورت خودکار در هنگام شارژ شدن دستگاه خاموش میشود."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"مسدود: هرگز این اعلانها نشان داده نشود"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"پایین: بدون صدا در پایین فهرست اعلان نشان داده شود"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"عادی: این اعلانها بدون صدا نشان داده شود"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"بالا: در بالای فهرست اعلانها و به همراه صدا نشان داده شود"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"ضروری: نمای کلی به همراه صدا در صفحه نشان داده شود"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">به مدت %1$d دقیقه (تا <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">به مدت %1$d دقیقه (تا <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 87ad95a..9dad4c0 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Mis à jour par votre administrateur"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Supprimé par votre administrateur"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Pour améliorer l\'autonomie de la pile, la fonction d\'économie d\'énergie réduit les performances de votre appareil et limite la vibration, les services de localisation ainsi que la plupart des données en arrière-plan. Les applications Courriel, Messages et d\'autres qui reposent sur la synchronisation ne peuvent pas se mettre à jour, sauf si vous les ouvrez. \n\n L\'économiseur d\'énergie se désactive automatiquement lorsque votre appareil est en charge."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Bloquée : ne jamais afficher ces notifications"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Faible : afficher en mode silencieux au bas de la liste de notifications"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normale : afficher ces notifications en mode silencieux"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Élevée : afficher en haut de la liste des notifications et émettre un son"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Urgent : afficher sur l\'écran et émettre un son"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">Pendant %1$d minute (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Pendant %1$d minutes (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index b478c85..dc08738 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Mis à jour par votre administrateur"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Supprimé par votre administrateur"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Pour améliorer l\'autonomie de la batterie, l\'économiseur de batterie réduit les performances et désactive le vibreur, les services de localisation et la plupart des données en arrière-plan. Les messageries électroniques ou autres applications utilisant la synchronisation pourraient ne pas se mettre à jour, sauf si vous les ouvrez.\n\nL\'économiseur de batterie s\'éteint automatiquement lorsque l\'appareil est en charge."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Bloquée : ne jamais afficher ces notifications"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Faible : afficher en mode silencieux au bas de la liste de notifications"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normale : afficher ces notifications en mode silencieux"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Élevée : afficher en haut de la liste des notifications et émettre un son"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Urgent : afficher sur l\'écran et émettre un son"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">Pendant %1$d minute (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Pendant %1$d minutes (jusqu\'à <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 1d6cf41c..02ee7b45 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Actualizado polo administrador"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Eliminado polo administrador"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Para axudar a mellorar a duración da batería, a función aforro de batería reduce o rendemento do teu dispositivo e limita a vibración, os servizos de localización e a maioría dos datos en segundo plano. É posible que o correo electrónico, as mensaxes e outras aplicacións que dependen da sincronización non se actualicen a menos que os abras. \n\nA función aforro de batería desactívase automaticamente cando pos a cargar o teu dispositivo."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Bloqueada: non mostrar nunca estas notificacións"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Baixa: mostrar de forma silenciosa na parte inferior da lista de notificacións"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normal: mostrar estas notificacións de forma silenciosa"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Alta: mostrar na parte superior da lista de notificacións e emitir son"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Urxente: mostrar na pantalla e emitir son"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">Durante %1$d minutos (ata as <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">Durante un minuto (ata as <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 679419c..f53625e 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1449,16 +1449,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Ažurira vaš administrator"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Izbrisao administrator"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Da bi se produljilo trajanje baterije, ušteda baterije smanjuje rad uređaja i ograničava vibraciju, usluge lokacije i većinu pozadinskih podataka. Aplikacije za e-poštu, slanje poruka i druge aplikacije koje se oslanjaju na sinkronizaciju možda se neće ažurirati ako ih ne otvorite.\n\nUšteda baterije isključuje se automatski dok se uređaj puni."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Blokirano: nikad ne prikazuj te obavijesti"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Nisko: tiho prikaži na dnu popisa obavijesti"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Uobičajeno: prikazuj te obavijesti tiho"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Visoko: prikaži na vrhu popisa obavijesti i emitiraj zvučni signal"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Hitno: prikaži na zaslonu i emitiraj zvučni signal"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">%1$d minutu (do <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="few">%1$d minute (do <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 8f65256..257d435 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Ադմինիստրատորը թարմացրել է այն"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Ադմինիստրատորը ջնջել է այն"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Մարտկոցի աշխատանքի ժամկետը երկարացնելու նպատակով, մարտկոցի էներգիայի խնայման գործառույթը սահմանափակում է սարքի աշխատանքը, թրթռոցը, տեղադրության ծառայությունները և հետնաշերտում աշխատող շատ գործընթացներ: Էլփոստը, հաղորդագրությունների փոխանակումը և տվյալների համաժամեցումից կախված այլ հավելվածները կարող են չթարմացվել, եթե դուք դրանք չգործարկեք:\n\nԵրբ ձեր սարքը լիցքավորվում է, մարտկոցի էներգիայի խնայման գործառույթն ինքնաշխատորեն անջատվում է:"</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Արգելափակված է. Երբեք չցուցադրել այս ծանուցումները"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Ցածր. Ցուցադրել ծանուցումների ցանկի ներքևում առանց ձայնային ազդանշանի"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Նորմալ. Ցուցադրել այս ծանուցումներն առանց ձայնային ազդանշանի"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Բարձր. Ցուցադրել ծանուցումների ցանկի վերևում և հնչեցնել ձայնային ազդանշան"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Հրատապ. Ցուցադրել էկրանին և հնչեցնել ձայնային ազդանշան"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">%1$d րոպե (մինչև <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">%1$d րոպե (մինչև <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index cee6af2..c2b8a8c 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Uppfært af kerfisstjóranum"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Eytt af kerfisstjóra"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Til að auka endingu rafhlöðunnar mun orkusparnaður draga úr afköstum tækisins og takmarka titring, staðsetningarþjónustu og megnið af bakgrunnsgögnum. Ekki er víst að tölvupóstur, skilaboð og önnur forrit sem reiða sig á samstillingu uppfærist nema þú opnir þau.\n\nSjálfkrafa er slökkt á orkusparnaði þegar tækið er í hleðslu."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Útilokaðar: Aldrei sýna þessar tilkynningar"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Léttvægar: Sýna neðst á tilkynningalistanum án hljóðs"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Venjulegar: Sýna þessar tilkynningar án hljóðs"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Mikilvægar: Sýna efst á tilkynningalistanum og spila hljóð"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Áríðandi: Birta á skjánum og spila hljóð"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">Í %1$d mínútu (til <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Í %1$d mínútur (til <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 3dc2240..6dd4e9a 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"管理者によって更新されています"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"管理者によって削除されました"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使用するその他のアプリは、起動しても更新されないことがあります。\n\nバッテリーセーバーは端末の充電中は自動的にOFFになります。"</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"ブロック: 今後はこの通知を表示しない"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"低: 通知リストの下にマナーモードで表示する"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"標準: この通知をマナーモードで表示する"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"高: 通知リストの上に表示し、音声でも知らせる"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"緊急: 画面にプレビューを表示し、音声でも知らせる"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d分間(<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>まで)</item>
<item quantity="one">1分間(<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>まで)</item>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index e43607c..6709d92 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"განახლებულია თქვენი ადმინისტრატორის მიერ"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"თქვენი ადმინისტრატორის მიერ წაშლილი"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"ელემენტის მოქმედების ვადის გაუმჯობესებისათვის, ელემენტის დამზოგი ამცირებს თქვენი მოწყობილობის შესრულებას და ზღუდავს ვიბრაციას, ადგილმდებარეობის მომსახურებებს და ძირითად ფონურ მონაცემებს. ელ-ფოსტა, შეტყობინებები და სხვა სინქრონიზაციაზე დაყრდნობილი აპლიკაციების განახლება არ მოხდება მათ გახსნეამდე. \n\n ელემენტის დამზოგველი ავტომატურად გამოირთვება, როდესაც თქვენს მოწყობილობას დამტენთან შეაერთებთ."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"დაბლოკილი: ეს შეტყობინებები აღარასოდეს გამოჩნდება"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"დაბალი: ეს შეტყობინებები სიის ბოლოში, უხმოდ გამოჩნდება"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"ნორმალური: ეს შეტყობინებები უხმოდ გამოჩნდება"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"მაღალი: ეს შეტყობინებები სიის თავში, ხმოვან სიგნალთან ერთად გამოჩნდება"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"გადაუდებელი: შეტყობინებები პირდაპირ ეკრანზე, ხმოვან სიგნალთან ერთად გამოჩნდება"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d წუთის განმავლობაში (<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>-მდე)</item>
<item quantity="one">ერთი წუთის განმავლობაში (<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>-მდე)</item>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 9a9b781..85f46d1 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"관리자에 의해 업데이트됨"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"관리자가 삭제함"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"배터리 수명 개선을 위해, 배터리 세이버는 기기의 성능을 줄이고 진동, 위치 서비스 및 대부분의 백그라운드 데이터를 제한합니다. 이메일, 메시지 및 동기화에 의존하는 기타 앱은 앱을 열 때까지 업데이트되지 않을 수 있습니다.\n\n배터리 세이버는 기기를 충전 중일 때는 자동으로 사용 중지됩니다."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"차단: 알림 다시 표시 안함"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"낮음: 알림 목록 하단에 무음으로 표시"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"일반: 무음으로 알림 표시"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"높음: 알림 목록 상단에 표시하고 소리로 알림"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"긴급: 화면에 표시하고 소리로 알림"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d분 동안(<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>까지)</item>
<item quantity="one">1분 동안(<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>까지)</item>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 4b77fbf..bb1cd7f 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -1441,16 +1441,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Администраторуңуз жаңырткан"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Администраторуңуз тарабынан жок кылынган"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Батареянын өмүрүн узартуу үчүн, батареяны үнөмдөгүч түзмөгүңүздүн ишинин майнаптуулугун азайтып, дирилдөө, жайгашкан жерди аныктоо кызматтары жана фондук дайындардын көпчүлүгүн чектеп коёт. Электрондук почта, билдирүү жазышуу жана башка шайкештештирүүгө байланыштуу колдонмолор ачылмайынча жаңыртылбай калышы мүмкүн.\n\nБатарея үнөмдөгүч түзмөгүңүз кубатталып жатканда автоматтык түрдө өчүп калат."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Бөгөттөлгөн: Бул эскертмелер эч качан көрсөтүлбөсүн"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Төмөн: Эскертмелер тизмесинин эң ылдыйында көрсөтүлсүн"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Кадимки: Бул эскертмелер акырын көрсөтүлсүн"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Жогору: Эскертмелер тизмесинин эң жогорусунда үн чыгарып көрсөтүлсүн"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Шашылыш: Үн менен коштолуп, экранга чыгарылсын"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d мүнөткө (<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g> чейин)</item>
<item quantity="one">Бир мүнөткө (<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g> чейин)</item>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index ec7d45a..9cfa6cf 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -1442,16 +1442,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Ажурирано од администраторот"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Избришано од администраторот"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"За да ви помогне да ја подобрите трајноста на батеријата, штедачот на батеријата ја намалува изведбата на уредот и го ограничува вибрирањето, услугите за локација и повеќето податоци од заднина. Е-поштата, испраќањето пораки и другите апликации кои се потпираат на синхронизација можеби нема да се ажурираат доколку не ги отворите.\n\nШтедачот на батеријата автоматски се исклучува кога уредот се полни."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Блокирано: никогаш не покажувај ги овие известувања"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Ниско: покажувај ги тивко на дното на списокот со известувања"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Нормално: покажувај ги овие известувања тивко"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Високо: покажувај ги на врв на списокот со известувања и дај звук"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Итно: нека се појават на екранот и дај им звук"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">За %1$d минута (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">За %1$d минути (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index a29179a..3a3ec41 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -1444,7 +1444,7 @@
<string name="notification_importance_low" msgid="6447640449918427187">"താഴ്ന്നത്: അറിയിപ്പ് ലിസ്റ്റിന്റെ താഴെ ശബ്ദമുണ്ടാക്കാതെ കാണിക്കുക"</string>
<string name="notification_importance_default" msgid="7991157697609575271">"സാധാരണം: ഈ അറിയിപ്പുകൾ നിശബ്ദമായി കാണിക്കുക"</string>
<string name="notification_importance_high" msgid="3152238637737215654">"ഉയർന്നത്: അറിയിപ്പ് ലിസ്റ്റിന്റെ ഏറ്റവും മുകളിൽ കാണിക്കുക, ശബ്ദമുണ്ടാക്കുക"</string>
- <string name="notification_importance_max" msgid="1153693080467904474">"അടിയന്തിരം: സ്ക്രീനിൽ ദൃശ്യമാക്കുക, ശബ്ദമുണ്ടാക്കുക"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"അടിയന്തരം: സ്ക്രീനിൽ ദൃശ്യമാക്കുക, ശബ്ദമുണ്ടാക്കുക"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d മിനിറ്റ് സമയത്തേക്ക് (<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g> വരെ)</item>
<item quantity="one">ഒരു മിനിറ്റ് സമയത്തേക്ക് (<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g> വരെ)</item>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 941729f..ab2285b 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Танай админ шинэчилсэн"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Таны админ устгасан байна"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Батарей хадгалах функц нь таны төхөөрөмжийн цэнэгийг хадгалахын тулд гүйцэтгэлийг багасгаж, чичрэлтийг бууруулж, байршлын үйлчилгээнүүд болон бусад өгөгдлийн хэмжээг багасгадаг юм. И-мэйл, мессеж болон бусад синхрон хийдэг апликейшнүүд дараа дахин нээгдэх хүртлээ автоматаар шинэчлэлт хийхгүй.\n\nМөн батарей хадгалах функц нь таныг төхөөрөмжөө цэнэглэх үед автоматаар унтрах юм."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Блоклосон: Эдгээр мэдэгдлийг хэзээ ч харуулахгүй"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Бага: Мэдэгдлийг жагсаалтын доод хэсэгт дуугүй харуулах"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Ердийн: Эдгээр мэдэгдлийг дуугүй харуулах"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Өндөр: мэдэгдлийг жагсаалтын эхэнд дуутай харуулах"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Яаралтай: Дэлгэцэнд яаралтай, дуутай гаргах"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other"> %1$d минутын турш ( <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g> хүртэл)</item>
<item quantity="one">нэг минутын турш (<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g> хүртэл)</item>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 98c2f6d..ab683fcb 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Dikemas kini oleh pentadbir anda"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Dipadamkan oleh pentadbir anda"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Untuk membantu memperbaik hayat bateri, penjimat bateri mengurangkan prestasi peranti anda dan mengehadkan getaran, perkhidmatan lokasi dan kebanyakan data latar belakang. E-mel, pemesejan dan apl lain yang bergantung kepada penyegerakan mungkin tidak mengemas kini, melainkan anda membuka apl itu.\n\nPenjimat bateri dimatikan secara automatik semasa peranti anda sedang dicas."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Disekat: Jangan sekali-kali tunjukkan pemberitahuan ini"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Rendah: Tunjukkan pada bahagian bawah senarai pemberitahuan secara senyap"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Biasa: Tunjukkan pemberitahuan ini secara senyap"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Tinggi: Tunjukkan pada bahagian atas senarai pemberitahuan dan bunyikan"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Segera: Intai pada skrin dan bunyikan"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">Selama %1$d minit (sehingga <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">Selama satu minit (sehingga <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index c92b310..c966e38 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Oppdatert av administratoren"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Slettet av administratoren"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"For å forlenge batterilevetiden reduserer batterispareren ytelsen til enheten din og begrenser vibrering, posisjonstjenester og mesteparten av bakgrunnsdataene. E-post, sending av meldinger og andre apper som er avhengig av synkronisering, oppdateres kanskje ikke med mindre du åpner dem.\n\nBatterisparing slås av automatisk når enheten lader."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Blokkert: Aldri vis disse varslene"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Lavt: Vis nederst i varsellisten uten lyd"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normalt: Vis disse varslene uten lyd"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Høyt: Vis øverst i varsellisten med lyd"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Haster: Vises fort på skjermen med lyd"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">I %1$d minutter (til <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">I 1 minutt (til <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index fa8ade1..ee4f63a 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Geüpdatet door je beheerder"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Verwijderd door je beheerder"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Accubesparing beperkt de prestaties van je apparaat, de trilstand, locatieservices en de meeste achtergrondgegevens om de gebruiksduur van de accu te verlengen.\n\nAccubesparing wordt automatisch uitgeschakeld terwijl je apparaat wordt opgeladen."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Geblokkeerd: deze meldingen nooit weergeven"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Laag: zonder geluid onder aan de lijst met meldingen weergeven"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normaal: deze meldingen zonder geluid weergeven"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Hoog: boven aan de lijst met meldingen weergeven en geluid laten horen"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Urgent: op het scherm weergeven en geluid laten horen"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d minuten (tot <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">Eén minuut (tot <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 4ccd4f9..7430d54 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1458,16 +1458,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Zaktualizowane przez administratora"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Usunięty przez administratora"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Aby wydłużyć czas pracy baterii, Oszczędzanie baterii ogranicza aktywność urządzenia, w tym wibracje, usługi lokalizacyjne i przetwarzanie większości danych w tle. Poczta, czat i inne synchronizowane aplikacje mogą nie aktualizować swojej zawartości, dopóki ich nie otworzysz.\n\nOszczędzanie baterii wyłącza się automatycznie podczas ładowania urządzenia."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Zablokowana: nigdy nie pokazuj tych powiadomień"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Niska: pokazuj na dole listy powiadomień bez sygnału dźwiękowego"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normalna: pokazuj te powiadomienia bez sygnału dźwiękowego"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Wysoka: pokazuj na początku listy powiadomień i odtwarzaj dźwięk"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Pilna: wyświetlaj na ekranie i odtwarzaj dźwięk"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="few">Przez %1$d minuty (do <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="many">Przez %1$d minut (do <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 471c1ad5..826771e 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Atualizado pelo administrador"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Eliminado pelo administrador"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Para ajudar a melhorar a autonomia da bateria, a poupança de bateria reduz o desempenho do seu dispositivo e limita a vibração, os serviços de localização e a maioria dos dados em segundo plano. O email, as mensagens e outras aplicações que dependem da sincronização não podem ser atualizados exceto se os abrir.\n\nA poupança de bateria desliga-se automaticamente quando o dispositivo está a carregar."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Bloqueado: nunca mostrar estas notificações"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Baixo: mostrar na parte inferior da lista de notificações sem som"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normal: mostrar estas notificações sem som"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Elevado: mostrar na parte superior da lista de notificações com som"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Urgente: mostrar no ecrã com som"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">Durante %1$d minutos (até às <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">Durante um minuto (até às <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 8bb2d3b..f49d6a1 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1449,16 +1449,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Actualizat de un administrator"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Șters de administrator"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Pentru a îmbunătăți autonomia bateriei, funcția de economisire a energiei reduce performanțele dispozitivului și limitează vibrațiile, serviciile de localizare și majoritatea datelor de fundal. Este posibil ca e-mailurile, mesageria și alte aplicații care depind de sincronizare să nu se actualizeze dacă nu le deschideți.\n\nFuncția de economisire a energiei se dezactivează automat când dispozitivul se încarcă."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Blocate: aceste notificări nu se afișează niciodată"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Redusă: se afișează în partea de jos a listei cu notificări fără a se emite un sunet"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normală: aceste notificări se afișează fără a se emite un sunet"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Ridicată: se afișează în partea de sus a listei cu notificări și se emite un sunet"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Urgente: se afișează pentru o scurtă durată pe ecran și se emite un sunet"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="few">Timp de %1$d minute (până la <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Timp de %1$d de minute (până la <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 7d7e6ca..3f6769c 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1458,16 +1458,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Posodobil skrbnik"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Izbrisal skrbnik"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Varčevanje z energijo akumulatorja podaljša čas njegovega delovanja tako, da zmanjša zmogljivost delovanja naprave in omeji vibriranje, lokacijske storitve ter prenos večine podatkov v ozadju. Aplikacije za e-pošto, sporočanje in drugo, ki uporabljajo sinhroniziranje, se morda ne posodabljajo, razen če jih odprete.\n\nVarčevanje z energijo akumulatorja se samodejno izklopi med polnjenjem akumulatorja naprave."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Blokirano: nikoli ne prikaži teh obvestil"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Manj pomembno: prikaži na dnu seznama obvestil brez zvoka"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Običajno: prikaži ta obvestila brez zvoka"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Pomembno: prikaži na vrhu seznama obvestil in predvajaj zvok"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Nujno: za hip pokaži predogled na zaslonu in predvajaj zvok"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">%d minuto (do <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="two">%d minuti (do <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 071d023..2653c71 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Përditësuar nga administratori"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"U fshi nga administratori yt"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Për të përmirësuar jetëgjatësinë e baterisë, opsioni i kursimit të baterisë ul rendimentin e pajisjes tënde si dhe kufizon dridhjet dhe shumicën e të dhënave në sfond. Mail-i, mesazhet dhe aplikacionet e tjera që sinkronizohen automatikisht mund të mos përditësohen pa i hapur.\n\nKursimi i baterisë çaktivizohet automatikisht kur pajisja vihet në ngarkim."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Të bllokuara: Mos i shfaq asnjëherë këto njoftime"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Të ulëta: Shfaqi në heshtje në fund të listës së njoftimeve"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normale: Shfaqi këto njoftime në heshtje"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Të larta: Shfaqi në krye të listës së njoftimeve dhe lësho tingull"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Urgjente: Shfaq një vështrim të shpejtë në ekran dhe lësho tingull"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">Për %1$d minuta (deri në <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">Për një minutë (deri në <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index dd714b7..069d717 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1449,16 +1449,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Ажурирао је администратор"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Избрисао је ваш адмиистратор"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Да би продужила време трајања батерије, уштеда батерије смањује перформансе уређаја и ограничава вибрацију, услуге локације и већину позадинских података. Имејл, размена порука и друге апликације које се ослањају на синхронизацију можда неће да се ажурирају ако их не отворите.\n\nУштеда батерије се аутоматски искључује када се уређај пуни."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Блокирана: Ова обавештења се никада не приказују"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Ниска: Приказују се у дну листе обавештења без звука"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Уобичајена: Ова обавештења се приказују без звука"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Висока: Приказују се у врху листе обавештења и активира се звучни сигнал"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Хитна: Накратко се приказују на екрану и активира се звучни сигнал"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">%1$d минут (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="few">%1$d минута (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 0229153..748257a 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Uppdaterat av administratören"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Paketet har raderats av administratören"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"I batterisparläget reduceras enhetens prestanda så att batteriet ska räcka längre och vibration, platstjänster samt den mesta användningen av bakgrundsdata begränsas. Det kan hända att appar för e-post, sms och annat som kräver synkronisering inte uppdateras förrän du öppnar dem.\n\nBatterisparläget inaktiveras automatiskt när enheten laddas."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Blockerad: Visa aldrig dessa aviseringar"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Låg: Visa längst ned i aviseringslistan – utan ljud"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normal: Visa aviseringarna – utan ljud"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Hög: Visa högst upp i aviseringslistan – med ljud"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Brådskande: Visa på skärmen – med ljud"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">I %1$d minuter (till kl. <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">I en minut (till kl. <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8ea3463..cdb86ae 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1442,7 +1442,7 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Kimesasiswa na msimamizi wako"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Kilifutwa na msimamizi wako"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Kusaidia kuboresha muda wa matumizi ya betri, inayookoa betri hupunguza utendaji wa kifaa chako na kupunguza mtetemo, huduma za utambuzi wa mahali, na data nyingi ya chini chini. Barua pepe, ujumbe na programu nyingine zinazotege,ea usawazishaji huenda zisisasishwe usipozifungua.\n\nInayookoa betri hujizima kiotomatiki kifaa chako kinapokuwa kinachaji."</string>
- <string name="notification_importance_blocked" msgid="7118826900767047125">"Imezuiwa: Usionyeshe kamwe arifa hizi"</string>
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Imezuiwa: Usionyeshe arifa hizi kamwe"</string>
<string name="notification_importance_low" msgid="6447640449918427187">"Chini: Onyesha katika sehemu ya chini ya orodha ya arifa bila kutoa sauti"</string>
<string name="notification_importance_default" msgid="7991157697609575271">"Kawaida: Onyesha arifa hizi bila sauti"</string>
<string name="notification_importance_high" msgid="3152238637737215654">"Juu: Onyesha katika sehemu ya juu ya orodha ya arifa na itoe sauti"</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index a8adc73..245f1ca 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"உங்கள் நிர்வாகி புதுப்பித்துள்ளார்"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"நிர்வாகி நீக்கிவிட்டார்"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"பேட்டரி ஆயுளை மேம்படுத்த, பேட்டரி சேமிப்பான் உங்கள் சாதனத்தின் செயல்திறனைக் குறைத்து, அதிர்வு, இடச் சேவைகள் மற்றும் பெரும்பாலான பின்புலத் தரவு போன்றவற்றைக் கட்டுப்படுத்துகிறது. ஒத்திசைவைச் சார்ந்துள்ள மின்னஞ்சல், செய்தியிடல் மற்றும் பிற பயன்பாடுகள் திறக்கும்வரை, அவை புதுப்பிக்கப்படாமல் இருக்கலாம்.\n\nஉங்கள் ஃபோன் சார்ஜ் செய்யப்படும்போது, பேட்டரி சேமிப்பான் தானாகவே முடங்கும்."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"தடுக்கப்பட்டது: இந்த அறிவிப்புகளை ஒருபோதும் காட்டாது"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"குறைவு: ஒலியின்றி அறிவிப்புப் பட்டியலின் கீழே காட்டும்"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"இயல்பு: ஒலியின்றி இந்த அறிவிப்புகளைக் காட்டும்"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"அதிகம்: அறிவிப்புகள் பட்டியலின் மேல் பகுதியில் ஒலியுடன் காட்டும்"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"அவசரம்: ஒலியுடன் திரையில் தோன்றும்"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d நிமிடங்களுக்கு (<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g> வரை)</item>
<item quantity="one">ஒரு நிமிடத்திற்கு (<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g> வரை)</item>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index c9db65f..57b9598 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"อัปเดตโดยผู้ดูแลระบบ"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"ลบโดยผู้ดูแลระบบของคุณ"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"เพื่อช่วยปรับปรุงอายุการใช้งานแบตเตอรี่ โหมดประหยัดแบตเตอรี่จะลดการทำงานของอุปกรณ์และจำกัดการสั่น บริการตำแหน่ง และข้อมูลแบ็กกราวด์ส่วนใหญ่ สำหรับอีเมล การรับส่งข้อความ และแอปอื่นๆ ที่ใช้การซิงค์จะไม่อัปเดตหากคุณไม่เปิดขึ้นมา\n\nโหมดประหยัดแบตเตอรี่จะปิดโดยอัตโนมัติขณะชาร์จอุปกรณ์"</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"บล็อก: อย่าแสดงการแจ้งเตือนเหล่านี้"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"ต่ำ: แสดงที่ด้านล่างของรายการแจ้งเตือนโดยไม่ส่งเสียง"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"ปกติ: แสดงการแจ้งเตือนเหล่านี้โดยไม่ส่งเสียง"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"สูง: แสดงที่ด้านบนของรายการแจ้งเตือนและส่งเสียง"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"ด่วน: แสดงบนหน้าจอในช่วงเวลาสั้นๆ และส่งเสียง"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">ระยะเวลา %1$d นาที (จนถึงเวลา <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">ระยะเวลา 1 นาที (จนถึงเวลา <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 6ea63b8..8cca676 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Na-update ng iyong administrator"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Na-delete ng iyong administrator"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Upang matulungang pagbutihin ang tagal ng baterya, binabawasan ng pangtipid ng baterya ang pagganap ng iyong device at nililimitahan ang pag-vibrate, mga serbisyo ng lokasyon at karamihan sa data ng background. Maaaring hindi mag-update ang email, pagmemensahe at iba pang mga app na umaasa sa pagsi-sync maliban kung buksan mo ang mga iyon.\n\nAwtomatikong nag-o-off ang pangtipid ng baterya kapag nagcha-charge ang iyong device."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Naka-block: Huwag kailanman ipakita ang mga notification na ito"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Mababa: Tahimik na ipakita sa ibaba ng listahan ng mga notification"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normal: Tahimik na ipakita ang mga notification na ito"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Mataas: Ipakita sa taas ng listahan ng mga notification at mag-play ng tunog"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Agaran: Ipasilip sa screen at mag-play ng tunog"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">Sa loob ng %1$d minuto (hanggang <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="other">Sa loob ng %1$d na minuto (hanggang <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 996e93f..60eee12 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Yöneticiniz tarafından güncellendi"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Yöneticiniz tarafından silindi"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Pil tasarrufu özelliği, pil ömrünü iyileştirmeye yardımcı olmak için cihazın performansını düşürür, titreşimi, konum hizmetlerini ve arka plan verilerinin çoğunu sınırlar. Senkronizasyona dayalı olarak çalışan e-posta, mesajlaşma uygulamaları ve diğer uygulamalar, bunları açmadığınız sürece güncellenmeyebilir.\n\nCihazınız şarj olurken pil tasarrufu otomatik olarak kapatılır."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Engellendi: Bu bildirimleri hiçbir zaman gösterme"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Düşük: Bildirim listesinin altında sessizce göster"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Normal: Bu bildirimleri sessizce göster"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Yüksek: Bildirim listesinin üstünde göster ve ses çıkar"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Acil: Ekrana getir ve ses çıkar"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d dakika için (şu saate kadar: <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">Bir dakika için (şu saate kadar: <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 3780374..adf6234 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1458,16 +1458,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Оновлено адміністратором"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Видалив адміністратор"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Щоб подовжити час роботи акумулятора, функція заощадження заряду акумулятора знижує продуктивність пристрою, а також обмежує вібрацію, функції служб локації та передавання більшості фонових даних. Електронна пошта, чати й інші додатки, які синхронізуються, можуть не оновлюватися, доки ви їх не відкриєте.\n\nФункція заощадження заряду акумулятора автоматично вимикається під час заряджання пристрою."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Заблоковано: не показувати ці сповіщення"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Низький пріоритет: показувати ці сповіщення внизу списку без звукового сигналу"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Стандартний пріоритет: показувати ці сповіщення без звукового сигналу"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Високий пріоритет: показувати ці сповіщення вгорі списку зі звуковим сигналом"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Терміново: показувати ці сповіщення на екрані зі звуковим сигналом"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="one">%1$d хвилину (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="few">%1$d хвилини (до <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index d3e2943..54af31e 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"آپ کے منتظم نے اپ ڈيٹ کر دیا"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"آپ کے منتظم کی جانب سے حذف کر دیا گیا"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"بیٹری کی میعاد بہتر کرنے میں مدد کرنے کیلئے، بیٹری سیور آپ کے آلہ کی کارکردگی کم کر دیتی ہے اور وائبریشن، مقام کی سروسز اور پس منظر کا بیشتر ڈیٹا محدود کر دیتی ہے۔ ای میل، پیغام رسانی اور مطابقت پذیری پر منحصر دیگر ایپس ممکن ہے اس وقت تک اپ ڈیٹ نہ ہوں جب تک آپ انہیں نہ کھولیں۔\n\nآپ کا آلہ چارج ہوتے وقت بیٹری سیور خود بخود آف ہو جاتی ہے۔"</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"مسدود کردہ: یہ اطلاعات کبھی مت دکھائیں"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"پست: اطلاعات کی فہرست کے نیچے خاموشی سے دکھائیں"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"عام: خاموشی سے یہ اطلاعات دکھائیں"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"اعلی: اطلاعات کی فہرست پر سب سے اوپر دکھائیں اور آواز چلائیں"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"ارجنٹ: اسکرین پر دکھائیں اور آواز چلائیں"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d منٹ کیلئے (<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g> تک)</item>
<item quantity="one">ایک منٹ کیلئے (تک <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 39c35b0..c4701b6 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"Administratoringiz tomonidan yangilandi"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"Administratoringiz tomonidan o‘chirilgan"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"Batareya quvvatini uzoqroq vaqtga yetkazish uchun quvvat tejash funksiyasi qurilmangiz unumdorligini kamaytiradi hamda uning tebranishi va orqa fonda internetdan foydalanishini cheklaydi. Sinxronlanishni talab qiladigan e-pochta, xabar almashinuv va boshqa ilovalar esa qachonki ularni ishga tushirganingizda yangilanadi.\n\nQurilma quvvat olayotganda quvvat tejash funksiyasi avtomatik tarzda o‘chadi."</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"Bloklangan: bildirishnomalar hech qachon ko‘rsatilmasin"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"Past: bildirishnomalar ro‘yxatining pastida ovozsiz ko‘rsatilsin"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"Oddiy: bildirishnomalar ovozsiz ko‘rsatilsin"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"Yuqori: bildirishnomalar ro‘yxatining yuqorisida ovoz bilan ko‘rsatilsin"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"Shoshilinch: barcha oynalar ustida signal ovozi bilan ko‘rsatilsin"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d daqiqa (<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g> gacha)</item>
<item quantity="one">Bir daqiqa (<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g> gacha)</item>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 0132667..d54eb1e 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1440,16 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"由您单位的管理员更新"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"已被管理员删除"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"为了延长电池的续航时间,节电助手会降低设备的性能,并限制振动、位置信息服务和大部分后台流量。对于电子邮件、聊天工具等依赖于同步功能的应用,可能要打开这类应用时才能收到新信息。\n\n节电助手会在设备充电时自动关闭。"</string>
- <!-- no translation found for notification_importance_blocked (7118826900767047125) -->
- <skip />
- <!-- no translation found for notification_importance_low (6447640449918427187) -->
- <skip />
- <!-- no translation found for notification_importance_default (7991157697609575271) -->
- <skip />
- <!-- no translation found for notification_importance_high (3152238637737215654) -->
- <skip />
- <!-- no translation found for notification_importance_max (1153693080467904474) -->
- <skip />
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"屏蔽:一律不显示这些通知"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"低:在通知列表底部显示,不发出提示音"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"一般:显示这些通知,但不发出提示音"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"高:在通知列表顶部显示,并发出提示音"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"紧急:在屏幕上持续显示,并发出提示音"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d 分钟(到<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">1 分钟(到<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 8b30283..a583e37 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1440,11 +1440,11 @@
<string name="package_updated_device_owner" msgid="8856631322440187071">"已由您的管理員更新"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"已由管理員刪除"</string>
<string name="battery_saver_description" msgid="1960431123816253034">"節約電池用量模式有助於延長電池壽命,但這會降低裝置效能,並限制震動、定位服務及大部分背景數據傳輸。除非您啟用,否則電郵、短訊及其他需要使用同步功能的應用程式均不會更新。\n\n當裝置充電時,節約電池用量模式會自動關閉。"</string>
- <string name="notification_importance_blocked" msgid="7118826900767047125">"封鎖:一律不顯示這些通知"</string>
- <string name="notification_importance_low" msgid="6447640449918427187">"低:顯示在通知清單底部且不發出任何音效"</string>
- <string name="notification_importance_default" msgid="7991157697609575271">"一般:顯示這些通知且不發出任何音效"</string>
- <string name="notification_importance_high" msgid="3152238637737215654">"高:顯示在通知清單頂端並發出音效"</string>
- <string name="notification_importance_max" msgid="1153693080467904474">"緊急:持續顯示在螢幕上並發出音效"</string>
+ <string name="notification_importance_blocked" msgid="7118826900767047125">"已封鎖:永不顯示這些通知"</string>
+ <string name="notification_importance_low" msgid="6447640449918427187">"低:以靜音方式顯示在通知清單底部"</string>
+ <string name="notification_importance_default" msgid="7991157697609575271">"一般:以靜音方式顯示這些通知"</string>
+ <string name="notification_importance_high" msgid="3152238637737215654">"高:顯示在通知清單頂部並發出音效"</string>
+ <string name="notification_importance_max" msgid="1153693080467904474">"緊急:不時於螢幕出現並發出音效"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">需時 %1$d 分鐘 (完成時間:<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">需時 1 分鐘 (完成時間:<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9bca3d6..786554c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3042,7 +3042,8 @@
<attr name="imeSubtypeLocale" format="string" />
<!-- The mode of the subtype. This string can be a mode (e.g. voice, keyboard...) and this
string will be passed to the IME when the framework calls the IME with the
- subtype. -->
+ subtype. {@link android.view.inputmethod.InputMethodSubtype#getLocale()} returns the
+ value specified in this attribute. -->
<attr name="imeSubtypeMode" format="string" />
<!-- Set true if the subtype is auxiliary. An auxiliary subtype won't be shown in the
input method selection list in the settings app.
@@ -3067,6 +3068,9 @@
this subtype. This is important because many password fields only allow
ASCII-characters. -->
<attr name="isAsciiCapable" format="boolean" />
+ <!-- The BCP-47 Language Tag of the subtype. This replaces
+ {@link android.R.styleable#InputMethod_Subtype_imeSubtypeLocale}. -->
+ <attr name="languageTag" format="string" />
</declare-styleable>
<!-- Use <code>spell-checker</code> as the root tag of the XML resource that
@@ -3090,7 +3094,8 @@
<attr name="label" />
<!-- The locale of the subtype. This string should be a locale (e.g. en_US, fr_FR...)
This is also used by the framework to know the supported locales
- of the spell checker. -->
+ of the spell checker. {@link android.view.textservice.SpellCheckerSubtype#getLocale()}
+ returns the value specified in this attribute. -->
<attr name="subtypeLocale" format="string" />
<!-- The extra value of the subtype. This string can be any string and will be passed to
the SpellChecker. -->
@@ -3102,6 +3107,9 @@
{@code Arrays.hashCode(new Object[] {subtypeLocale, extraValue}) will be used instead.
-->
<attr name="subtypeId" />
+ <!-- The BCP-47 Language Tag of the subtype. This replaces
+ {@link android.R.styleable#SpellChecker_Subtype_subtypeLocale}. -->
+ <attr name="languageTag" />
</declare-styleable>
<!-- Use <code>accessibility-service</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index af8ff2e..7711825 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -130,8 +130,8 @@
<drawable name="notification_template_divider">#29000000</drawable>
<drawable name="notification_template_divider_media">#29ffffff</drawable>
- <color name="notification_icon_default_color">#ff616161</color>
- <color name="notification_action_color_filter">@color/secondary_text_material_light</color>
+ <color name="notification_default_color">#757575</color> <!-- Gray 600 -->
+ <color name="notification_icon_default_color">@color/notification_default_color</color>
<color name="notification_progress_background_color">@color/secondary_text_material_light</color>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b6b2e20..addeb05 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2683,6 +2683,7 @@
<public type="attr" name="encryptionAware" />
<public type="attr" name="preferenceFragmentStyle" />
<public type="attr" name="canControlMagnification" />
+ <public type="attr" name="languageTag" />
<public type="style" name="Theme.Material.DayNight" />
<public type="style" name="Theme.Material.DayNight.DarkActionBar" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index eadcae0..dd81f89 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -16,10 +16,6 @@
*/
-->
<resources>
- <!-- We don't want to publish private symbols in android.R as part of the
- SDK. Instead, put them here. -->
- <private-symbols package="com.android.internal" />
-
<!-- Private symbols that we need to reference from framework code. See
frameworks/base/core/res/MakeJavaSymbols.sed for how to easily generate
this.
@@ -1878,7 +1874,6 @@
<java-symbol type="layout" name="notification_template_material_big_text" />
<java-symbol type="layout" name="notification_template_header" />
<java-symbol type="layout" name="notification_material_media_action" />
- <java-symbol type="color" name="notification_action_color_filter" />
<java-symbol type="color" name="notification_icon_default_color" />
<java-symbol type="color" name="notification_progress_background_color" />
<java-symbol type="id" name="media_actions" />
@@ -1958,9 +1953,12 @@
<java-symbol type="bool" name="config_built_in_sip_phone" />
<java-symbol type="id" name="maximize_window" />
<java-symbol type="id" name="close_window" />
- <java-symbol type="layout" name="decor_caption_light" />
- <java-symbol type="layout" name="decor_caption_dark" />
+ <java-symbol type="layout" name="decor_caption" />
<java-symbol type="drawable" name="decor_caption_title_focused" />
+ <java-symbol type="drawable" name="decor_close_button_dark" />
+ <java-symbol type="drawable" name="decor_close_button_light" />
+ <java-symbol type="drawable" name="decor_maximize_button_dark" />
+ <java-symbol type="drawable" name="decor_maximize_button_light" />
<!-- From TelephonyProvider -->
<java-symbol type="xml" name="apns" />
@@ -2267,6 +2265,7 @@
<!-- Floating toolbar -->
<java-symbol type="id" name="floating_toolbar_menu_item_image_button" />
+ <java-symbol type="id" name="overflow" />
<java-symbol type="layout" name="floating_popup_container" />
<java-symbol type="layout" name="floating_popup_menu_button" />
<java-symbol type="layout" name="floating_popup_open_overflow_button" />
diff --git a/core/tests/coretests/res/layout/activity_text_view.xml b/core/tests/coretests/res/layout/activity_text_view.xml
index 7ab0b13..e795c10 100644
--- a/core/tests/coretests/res/layout/activity_text_view.xml
+++ b/core/tests/coretests/res/layout/activity_text_view.xml
@@ -23,6 +23,6 @@
<EditText
android:id="@+id/textview"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="wrap_content" />
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
index 73fdb10..4a1c414 100644
--- a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
+++ b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
@@ -30,11 +30,16 @@
*/
public class SpellCheckerSubtypeTest extends InstrumentationTestCase {
private static final int SUBTYPE_SUBTYPE_ID_NONE = 0;
+ private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_NONE = "";
+ private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE = "";
+
private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_A = "en_GB";
+ private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_A = "en-GB";
private static final int SUBTYPE_NAME_RES_ID_A = 0x12345;
private static final String SUBTYPE_EXTRA_VALUE_A = "Key1=Value1,Key2=Value2";
private static final int SUBTYPE_SUBTYPE_ID_A = 42;
private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_B = "en_IN";
+ private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_B = "en-IN";
private static final int SUBTYPE_NAME_RES_ID_B = 0x54321;
private static final String SUBTYPE_EXTRA_VALUE_B = "Key3=Value3,Key4=Value4";
private static final int SUBTYPE_SUBTYPE_ID_B = -42;
@@ -60,9 +65,11 @@
@SmallTest
public void testSubtypeWithNoSubtypeId() throws Exception {
final SpellCheckerSubtype subtype = new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A,
- SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE);
+ SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_SUBTYPE_LANGUAGE_TAG_A,
+ SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE);
assertEquals(SUBTYPE_NAME_RES_ID_A, subtype.getNameResId());
assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, subtype.getLocale());
+ assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, subtype.getLanguageTag());
assertEquals("Value1", subtype.getExtraValueOf("Key1"));
assertEquals("Value2", subtype.getExtraValueOf("Key2"));
// Historically we have used SpellCheckerSubtype#hashCode() to track which subtype is
@@ -75,6 +82,7 @@
final SpellCheckerSubtype clonedSubtype = cloneViaParcel(subtype);
assertEquals(SUBTYPE_NAME_RES_ID_A, clonedSubtype.getNameResId());
assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, clonedSubtype.getLocale());
+ assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, clonedSubtype.getLanguageTag());
assertEquals("Value1", clonedSubtype.getExtraValueOf("Key1"));
assertEquals("Value2", clonedSubtype.getExtraValueOf("Key2"));
assertEquals(
@@ -84,10 +92,12 @@
public void testSubtypeWithSubtypeId() throws Exception {
final SpellCheckerSubtype subtype = new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A,
- SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A);
+ SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_SUBTYPE_LANGUAGE_TAG_A,
+ SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A);
assertEquals(SUBTYPE_NAME_RES_ID_A, subtype.getNameResId());
assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, subtype.getLocale());
+ assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, subtype.getLanguageTag());
assertEquals("Value1", subtype.getExtraValueOf("Key1"));
assertEquals("Value2", subtype.getExtraValueOf("Key2"));
// Similar to "SubtypeId" in InputMethodSubtype, "SubtypeId" in SpellCheckerSubtype enables
@@ -97,6 +107,7 @@
final SpellCheckerSubtype clonedSubtype = cloneViaParcel(subtype);
assertEquals(SUBTYPE_NAME_RES_ID_A, clonedSubtype.getNameResId());
assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, clonedSubtype.getLocale());
+ assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, clonedSubtype.getLanguageTag());
assertEquals("Value1", clonedSubtype.getExtraValueOf("Key1"));
assertEquals("Value2", clonedSubtype.getExtraValueOf("Key2"));
assertEquals(SUBTYPE_SUBTYPE_ID_A, clonedSubtype.hashCode());
@@ -104,30 +115,42 @@
@SmallTest
public void testGetLocaleObject() throws Exception {
- assertEquals(new Locale("en"), new SpellCheckerSubtype(
- SUBTYPE_NAME_RES_ID_A, "en", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
- assertEquals(new Locale("en", "US"), new SpellCheckerSubtype(
- SUBTYPE_NAME_RES_ID_A, "en_US", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
- assertEquals(new Locale("en", "US", "POSIX"), new SpellCheckerSubtype(
- SUBTYPE_NAME_RES_ID_A, "en_US_POSIX", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
+ assertEquals(new Locale("en", "GB"),
+ new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "en_GB",
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
+ assertEquals(new Locale("en", "GB"),
+ new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE,
+ "en-GB", SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
- // Special rewrite rule for "tl" for versions of Android earlier than Lollipop that did not
- // support three letter language codes, and used "tl" (Tagalog) as the language string for
- // "fil" (Filipino).
- assertEquals(new Locale("fil"), new SpellCheckerSubtype(
- SUBTYPE_NAME_RES_ID_A, "tl", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
- assertEquals(new Locale("fil", "PH"), new SpellCheckerSubtype(
- SUBTYPE_NAME_RES_ID_A, "tl_PH", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
- assertEquals(new Locale("fil", "PH", "POSIX"), new SpellCheckerSubtype(
- SUBTYPE_NAME_RES_ID_A, "tl_PH_POSIX", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
+ // If neither locale string nor language tag is specified,
+ // {@link SpellCheckerSubtype#getLocaleObject} returns null.
+ assertNull(
+ new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE,
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
- // So far rejecting invalid/unexpected locale strings is out of the scope.
- assertEquals(new Locale("a"), new SpellCheckerSubtype(
- SUBTYPE_NAME_RES_ID_A, "a", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
- assertEquals(new Locale("a b c"), new SpellCheckerSubtype(
- SUBTYPE_NAME_RES_ID_A, "a b c", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
- assertEquals(new Locale("en-US"), new SpellCheckerSubtype(
- SUBTYPE_NAME_RES_ID_A, "en-US", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
+ // If both locale string and language tag are specified,
+ // {@link SpellCheckerSubtype#getLocaleObject} uses language tag.
+ assertEquals(new Locale("en", "GB"),
+ new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "en_US", "en-GB",
+ SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
+
+ // Make sure that "tl_PH" is rewritten to "fil_PH" for spell checkers that need to support
+ // Android KitKat and prior, which do not support 3-letter language codes.
+ assertEquals(new Locale("fil", "PH"),
+ new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "tl_PH",
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
+
+ // "languageTag" attribute is available in Android N and later, where 3-letter country codes
+ // are guaranteed to be available. It's developers' responsibility for specifying a valid
+ // country subtags here and we do not rewrite "tl" to "fil" for simplicity.
+ assertEquals("tl",
+ new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE,
+ "tl-PH", SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE)
+ .getLocaleObject().getLanguage());
}
@SmallTest
@@ -156,50 +179,83 @@
// If subtype ID is 0 (== SUBTYPE_SUBTYPE_ID_NONE), we keep the same behavior.
assertEquals(
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE),
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE));
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE));
assertNotEqual(
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE),
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_B, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE));
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE));
assertNotEqual(
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE),
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE));
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE));
assertNotEqual(
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE),
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_B, SUBTYPE_SUBTYPE_ID_NONE));
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_B, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE));
+ assertNotEqual(
+ new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_NONE),
+ new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_B,
+ SUBTYPE_SUBTYPE_ID_NONE));
// If subtype ID is not 0, we test the equality based only on the subtype ID.
assertEquals(
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_A),
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A));
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_A));
assertEquals(
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_A),
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_B, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A));
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_A));
assertEquals(
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_A),
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A));
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_A));
assertEquals(
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_A),
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_B, SUBTYPE_SUBTYPE_ID_A));
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_B, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_A));
+ assertEquals(
+ new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_A),
+ new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_B,
+ SUBTYPE_SUBTYPE_ID_A));
assertNotEqual(
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_A),
new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
- SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_B));
+ SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+ SUBTYPE_SUBTYPE_ID_B));
}
+
}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index ddbdc87..83a9e01 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -16,15 +16,23 @@
package android.widget;
+import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
+import static android.widget.espresso.DragHandleUtils.onHandleView;
+import static android.widget.espresso.TextViewActions.mouseClickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.mouseDoubleClickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.mouseLongClickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.mouseDoubleClickAndDragOnText;
import static android.widget.espresso.TextViewActions.mouseDragOnText;
import static android.widget.espresso.TextViewActions.mouseLongClickAndDragOnText;
+import static android.widget.espresso.TextViewActions.mouseTripleClickAndDragOnText;
+import static android.widget.espresso.TextViewActions.mouseTripleClickOnTextAtIndex;
import static android.widget.espresso.TextViewAssertions.hasSelection;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.replaceText;
import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import com.android.frameworks.coretests.R;
@@ -48,10 +56,23 @@
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+
+ assertNoSelectionHandles();
+
onView(withId(R.id.textview)).perform(
mouseDragOnText(helloWorld.indexOf("llo"), helloWorld.indexOf("ld!")));
onView(withId(R.id.textview)).check(hasSelection("llo wor"));
+
+ onHandleView(com.android.internal.R.id.selection_start_handle)
+ .check(matches(isDisplayed()));
+ onHandleView(com.android.internal.R.id.selection_end_handle)
+ .check(matches(isDisplayed()));
+
+ onView(withId(R.id.textview)).perform(mouseClickOnTextAtIndex(helloWorld.indexOf("w")));
+ onView(withId(R.id.textview)).check(hasSelection(""));
+
+ assertNoSelectionHandles();
}
@SmallTest
@@ -89,6 +110,9 @@
onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex(
helloWorld.indexOf("rld")));
onView(withId(R.id.textview)).check(hasSelection("world"));
+
+ onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex(helloWorld.length()));
+ onView(withId(R.id.textview)).check(hasSelection("!"));
}
@SmallTest
@@ -113,6 +137,9 @@
onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex(
helloWorld.indexOf("rld")));
onView(withId(R.id.textview)).check(hasSelection("world"));
+
+ onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex(helloWorld.length()));
+ onView(withId(R.id.textview)).check(hasSelection("!"));
}
@SmallTest
@@ -166,4 +193,102 @@
mouseLongClickAndDragOnText(text.indexOf("j"), text.indexOf("f")));
onView(withId(R.id.textview)).check(hasSelection("efg hijk"));
}
+
+ @SmallTest
+ public void testSelectTextByTripleClick() throws Exception {
+ getActivity();
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append("First paragraph.\n");
+ builder.append("Second paragraph.");
+ for (int i = 0; i < 10; i++) {
+ builder.append(" This paragraph is very long.");
+ }
+ builder.append('\n');
+ builder.append("Third paragraph.");
+ final String text = builder.toString();
+
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ onView(withId(R.id.textview)).perform(
+ mouseTripleClickOnTextAtIndex(text.indexOf("rst")));
+ onView(withId(R.id.textview)).check(hasSelection("First paragraph.\n"));
+
+ onView(withId(R.id.textview)).perform(
+ mouseTripleClickOnTextAtIndex(text.indexOf("cond")));
+ onView(withId(R.id.textview)).check(hasSelection(
+ text.substring(text.indexOf("Second"), text.indexOf("Third"))));
+
+ onView(withId(R.id.textview)).perform(
+ mouseTripleClickOnTextAtIndex(text.indexOf("ird")));
+ onView(withId(R.id.textview)).check(hasSelection("Third paragraph."));
+
+ onView(withId(R.id.textview)).perform(
+ mouseTripleClickOnTextAtIndex(text.indexOf("very long")));
+ onView(withId(R.id.textview)).check(hasSelection(
+ text.substring(text.indexOf("Second"), text.indexOf("Third"))));
+ }
+
+ @SmallTest
+ public void testSelectTextByTripleClickAndDrag() throws Exception {
+ getActivity();
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append("First paragraph.\n");
+ builder.append("Second paragraph.");
+ for (int i = 0; i < 10; i++) {
+ builder.append(" This paragraph is very long.");
+ }
+ builder.append('\n');
+ builder.append("Third paragraph.");
+ final String text = builder.toString();
+
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ onView(withId(R.id.textview)).perform(
+ mouseTripleClickAndDragOnText(text.indexOf("irst"), text.indexOf("st")));
+ onView(withId(R.id.textview)).check(hasSelection("First paragraph.\n"));
+
+ onView(withId(R.id.textview)).perform(
+ mouseTripleClickAndDragOnText(text.indexOf("cond"), text.indexOf("Third") - 2));
+ onView(withId(R.id.textview)).check(hasSelection(
+ text.substring(text.indexOf("Second"), text.indexOf("Third"))));
+
+ onView(withId(R.id.textview)).perform(
+ mouseTripleClickAndDragOnText(text.indexOf("First"), text.indexOf("ird")));
+ onView(withId(R.id.textview)).check(hasSelection(text));
+ }
+
+ @SmallTest
+ public void testSelectTextByTripleClickAndDrag_reverse() throws Exception {
+ getActivity();
+
+ final StringBuilder builder = new StringBuilder();
+ builder.append("First paragraph.\n");
+ builder.append("Second paragraph.");
+ for (int i = 0; i < 10; i++) {
+ builder.append(" This paragraph is very long.");
+ }
+ builder.append('\n');
+ builder.append("Third paragraph.");
+ final String text = builder.toString();
+
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ onView(withId(R.id.textview)).perform(
+ mouseTripleClickAndDragOnText(text.indexOf("st"), text.indexOf("irst")));
+ onView(withId(R.id.textview)).check(hasSelection("First paragraph.\n"));
+
+ onView(withId(R.id.textview)).perform(
+ mouseTripleClickAndDragOnText(text.indexOf("Third") - 2, text.indexOf("cond")));
+ onView(withId(R.id.textview)).check(hasSelection(
+ text.substring(text.indexOf("Second"), text.indexOf("Third"))));
+
+ onView(withId(R.id.textview)).perform(
+ mouseTripleClickAndDragOnText(text.indexOf("ird"), text.indexOf("First")));
+ onView(withId(R.id.textview)).check(hasSelection(text));
+ }
}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 4614505..e54723e 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
+import static android.widget.espresso.DragHandleUtils.onHandleView;
import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
@@ -26,24 +28,22 @@
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
import static android.widget.espresso.TextViewAssertions.hasSelection;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsNotDisplayed;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.pressKey;
+import static android.support.test.espresso.action.ViewActions.replaceText;
import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
-import static org.hamcrest.Matchers.allOf;
import com.android.frameworks.coretests.R;
-import android.support.test.espresso.NoMatchingRootException;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.ViewInteraction;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.KeyEvent;
@@ -153,19 +153,115 @@
@SmallTest
public void testToolbarAppearsAfterSelection() throws Exception {
- // It'll be nice to check that the toolbar is not visible (or does not exist) here
- // I can't currently find a way to do this. I'll get to it later.
-
final String text = "Toolbar appears after selection.";
onView(withId(R.id.textview)).perform(click());
+ assertFloatingToolbarIsNotDisplayed();
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
onView(withId(R.id.textview)).perform(
longPressOnTextAtIndex(text.indexOf("appears")));
- // It takes the toolbar less than 100ms to start to animate into screen.
- // Ideally, we'll wait using the UiController, but I guess this works for now.
- Thread.sleep(100);
- assertFloatingToolbarIsDisplayed(getActivity());
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+
+ final String text2 = "Toolbar disappears after typing text.";
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text2));
+ assertFloatingToolbarIsNotDisplayed();
+ }
+
+ @SmallTest
+ public void testToolbarAndInsertionHandle() throws Exception {
+ final String text = "text";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
+ assertFloatingToolbarIsNotDisplayed();
+
+ onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+
+ assertFloatingToolbarContainsItem(
+ getActivity().getString(com.android.internal.R.string.selectAll));
+ assertFloatingToolbarDoesNotContainItem(
+ getActivity().getString(com.android.internal.R.string.copy));
+ assertFloatingToolbarDoesNotContainItem(
+ getActivity().getString(com.android.internal.R.string.cut));
+ }
+
+ @SmallTest
+ public void testToolbarAndSelectionHandle() throws Exception {
+ final String text = "abcd efg hijk";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+
+ onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f")));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+
+ assertFloatingToolbarContainsItem(
+ getActivity().getString(com.android.internal.R.string.selectAll));
+ assertFloatingToolbarContainsItem(
+ getActivity().getString(com.android.internal.R.string.copy));
+ assertFloatingToolbarContainsItem(
+ getActivity().getString(com.android.internal.R.string.cut));
+
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+ onHandleView(com.android.internal.R.id.selection_start_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+
+ onHandleView(com.android.internal.R.id.selection_end_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_END, text.length()));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+
+ assertFloatingToolbarDoesNotContainItem(
+ getActivity().getString(com.android.internal.R.string.selectAll));
+ assertFloatingToolbarContainsItem(
+ getActivity().getString(com.android.internal.R.string.copy));
+ assertFloatingToolbarContainsItem(
+ getActivity().getString(com.android.internal.R.string.cut));
+ }
+
+ @SmallTest
+ public void testInsertionHandle() throws Exception {
+ final String text = "abcd efg hijk ";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
+
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+
+ onHandleView(com.android.internal.R.id.insertion_handle)
+ .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a')));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a")));
+
+ onHandleView(com.android.internal.R.id.insertion_handle)
+ .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f')));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
+ }
+
+ @SmallTest
+ public void testInsertionHandle_multiLine() throws Exception {
+ final String text = "abcd\n" + "efg\n" + "hijk\n";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
+
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+
+ onHandleView(com.android.internal.R.id.insertion_handle)
+ .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a')));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a")));
+
+ onHandleView(com.android.internal.R.id.insertion_handle)
+ .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f')));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
}
@SmallTest
@@ -183,7 +279,7 @@
onHandleView(com.android.internal.R.id.selection_end_handle)
.check(matches(isDisplayed()));
- final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
onHandleView(com.android.internal.R.id.selection_start_handle)
.perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
onView(withId(R.id.textview)).check(hasSelection("abcd efg"));
@@ -200,7 +296,7 @@
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
- final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
onHandleView(com.android.internal.R.id.selection_start_handle)
.perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('e')));
onView(withId(R.id.textview)).check(hasSelection("efg\nhijk"));
@@ -219,13 +315,46 @@
}
@SmallTest
+ public void testSelectionHandles_multiLine_rtl() throws Exception {
+ // Arabic text.
+ final String text = "\u062A\u062B\u062C\n" + "\u062D\u062E\u062F\n"
+ + "\u0630\u0631\u0632\n" + "\u0633\u0634\u0635\n" + "\u0636\u0637\u0638\n"
+ + "\u0639\u063A\u063B";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
+ onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('\u0634')));
+
+ final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+ onHandleView(com.android.internal.R.id.selection_start_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u062E')));
+ onView(withId(R.id.textview)).check(hasSelection(
+ text.substring(text.indexOf('\u062D'), text.indexOf('\u0635') + 1)));
+
+ onHandleView(com.android.internal.R.id.selection_start_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u062A')));
+ onView(withId(R.id.textview)).check(hasSelection(
+ text.substring(text.indexOf('\u062A'), text.indexOf('\u0635') + 1)));
+
+ onHandleView(com.android.internal.R.id.selection_end_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('\u0638')));
+ onView(withId(R.id.textview)).check(hasSelection(
+ text.substring(text.indexOf('\u062A'), text.indexOf('\u0638') + 1)));
+
+ onHandleView(com.android.internal.R.id.selection_end_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('\u063B')));
+ onView(withId(R.id.textview)).check(hasSelection(text));
+ }
+
+
+ @SmallTest
public void testSelectionHandles_doesNotPassAnotherHandle() throws Exception {
final String text = "abcd efg hijk lmn";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('f')));
- final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
onHandleView(com.android.internal.R.id.selection_start_handle)
.perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('l')));
onView(withId(R.id.textview)).check(hasSelection("g"));
@@ -243,7 +372,7 @@
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
- final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
onHandleView(com.android.internal.R.id.selection_start_handle)
.perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('r') + 1));
onView(withId(R.id.textview)).check(hasSelection("k"));
@@ -261,7 +390,7 @@
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
- final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
onHandleView(com.android.internal.R.id.selection_start_handle)
.perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('f')));
@@ -314,7 +443,7 @@
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('m')));
- final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
onHandleView(com.android.internal.R.id.selection_start_handle)
.perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('c')));
@@ -342,25 +471,4 @@
.perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('i')));
onView(withId(R.id.textview)).check(hasSelection("hijk"));
}
-
- private static void assertNoSelectionHandles() {
- try {
- onHandleView(com.android.internal.R.id.selection_start_handle)
- .check(matches(isDisplayed()));
- } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
- try {
- onHandleView(com.android.internal.R.id.selection_end_handle)
- .check(matches(isDisplayed()));
- } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e1) {
- return;
- }
- }
- throw new AssertionError("Selection handle found");
- }
-
- private static ViewInteraction onHandleView(int id)
- throws NoMatchingRootException, NoMatchingViewException, AssertionError {
- return onView(allOf(withId(id), isAssignableFrom(Editor.HandleView.class)))
- .inRoot(withDecorView(hasDescendant(withId(id))));
- }
}
diff --git a/core/tests/coretests/src/android/widget/espresso/DragAction.java b/core/tests/coretests/src/android/widget/espresso/DragAction.java
index ce97568..b2c8e38 100644
--- a/core/tests/coretests/src/android/widget/espresso/DragAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/DragAction.java
@@ -157,6 +157,59 @@
},
/**
+ * Starts a drag with a mouse triple click.
+ */
+ MOUSE_TRIPLE_CLICK {
+ private DownMotionPerformer downMotion = new DownMotionPerformer() {
+ @Override
+ @Nullable
+ public MotionEvent perform(
+ UiController uiController, float[] coordinates, float[] precision) {
+ MotionEvent downEvent = MotionEvents.sendDown(
+ uiController, coordinates, precision)
+ .down;
+ for (int i = 0; i < 2; ++i) {
+ try {
+ if (!MotionEvents.sendUp(uiController, downEvent)) {
+ String logMessage = "Injection of up event as part of the triple "
+ + "click failed. Sending cancel event.";
+ Log.d(TAG, logMessage);
+ MotionEvents.sendCancel(uiController, downEvent);
+ return null;
+ }
+
+ long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+ uiController.loopMainThreadForAtLeast(doubleTapMinimumTimeout);
+ } finally {
+ downEvent.recycle();
+ }
+ downEvent = MotionEvents.sendDown(
+ uiController, coordinates, precision).down;
+ }
+ return downEvent;
+ }
+ };
+
+ @Override
+ public Status sendSwipe(
+ UiController uiController,
+ float[] startCoordinates, float[] endCoordinates, float[] precision) {
+ return sendLinearDrag(
+ uiController, downMotion, startCoordinates, endCoordinates, precision);
+ }
+
+ @Override
+ public String toString() {
+ return "mouse triple click and drag to select";
+ }
+
+ @Override
+ public UiController wrapUiController(UiController uiController) {
+ return new MouseUiController(uiController);
+ }
+ },
+
+ /**
* Starts a drag with a tap.
*/
TAP {
diff --git a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
new file mode 100644
index 0000000..f744cae
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
@@ -0,0 +1,58 @@
+/*
+ * 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.espresso;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.Matchers.allOf;
+
+import android.support.test.espresso.NoMatchingRootException;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewInteraction;
+import android.widget.Editor;
+
+public class DragHandleUtils {
+ private DragHandleUtils() {
+
+ }
+
+ public static void assertNoSelectionHandles() {
+ try {
+ onHandleView(com.android.internal.R.id.selection_start_handle)
+ .check(matches(isDisplayed()));
+ } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
+ try {
+ onHandleView(com.android.internal.R.id.selection_end_handle)
+ .check(matches(isDisplayed()));
+ } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e1) {
+ return;
+ }
+ }
+ throw new AssertionError("Selection handle found");
+ }
+
+ public static ViewInteraction onHandleView(int id)
+ throws NoMatchingRootException, NoMatchingViewException, AssertionError {
+ return onView(allOf(withId(id), isAssignableFrom(Editor.HandleView.class)))
+ .inRoot(withDecorView(hasDescendant(withId(id))));
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index fc01d84..f02fe00 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -19,31 +19,133 @@
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
import static android.support.test.espresso.matcher.ViewMatchers.withTagValue;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import android.app.Activity;
+import org.hamcrest.Matcher;
+
+import android.support.test.espresso.NoMatchingRootException;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.ViewInteraction;
+import android.support.test.espresso.action.ViewActions;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.view.View;
+
import com.android.internal.widget.FloatingToolbar;
/**
* Espresso utility methods for the floating toolbar.
*/
public class FloatingToolbarEspressoUtils {
-
+ private final static Object TAG = FloatingToolbar.FLOATING_TOOLBAR_TAG;
private FloatingToolbarEspressoUtils() {}
+ private static ViewInteraction onFloatingToolBar() {
+ return onView(withTagValue(is(TAG)))
+ .inRoot(withDecorView(hasDescendant(withTagValue(is(TAG)))));
+ }
+
/**
* Asserts that the floating toolbar is displayed on screen.
*
* @throws AssertionError if the assertion fails
*/
- public static void assertFloatingToolbarIsDisplayed(Activity activity) {
- onView(withTagValue(is((Object) FloatingToolbar.FLOATING_TOOLBAR_TAG)))
- .inRoot(withDecorView(not(is(activity.getWindow().getDecorView()))))
- .check(matches(isDisplayed()));
+ public static void assertFloatingToolbarIsDisplayed() {
+ onFloatingToolBar().check(matches(isDisplayed()));
}
+ /**
+ * Asserts that the floating toolbar is not displayed on screen.
+ *
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertFloatingToolbarIsNotDisplayed() {
+ try {
+ onFloatingToolBar().check(matches(isDisplayed()));
+ } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
+ return;
+ }
+ throw new AssertionError("Floating toolbar is displayed");
+ }
+
+ private static void toggleOverflow() {
+ final int id = com.android.internal.R.id.overflow;
+ onView(allOf(withId(id), isDisplayed()))
+ .inRoot(withDecorView(hasDescendant(withId(id))))
+ .perform(ViewActions.click());
+ onView(isRoot()).perform(SLEEP);
+ }
+
+ public static void sleepForFloatingToolbarPopup() {
+ onView(isRoot()).perform(SLEEP);
+ }
+
+ /**
+ * Asserts that the floating toolbar contains the specified item.
+ *
+ * @param itemLabel label of the item.
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertFloatingToolbarContainsItem(String itemLabel) {
+ try{
+ onFloatingToolBar().check(matches(hasDescendant(ViewMatchers.withText(itemLabel))));
+ } catch (AssertionError e) {
+ try{
+ toggleOverflow();
+ } catch (NoMatchingViewException | NoMatchingRootException e2) {
+ // No overflow items.
+ throw e;
+ }
+ try{
+ onFloatingToolBar().check(matches(hasDescendant(ViewMatchers.withText(itemLabel))));
+ } finally {
+ toggleOverflow();
+ }
+ }
+ }
+
+ /**
+ * Asserts that the floating toolbar doesn't contain the specified item.
+ *
+ * @param itemLabel label of the item.
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
+ try{
+ assertFloatingToolbarContainsItem(itemLabel);
+ } catch (AssertionError e) {
+ return;
+ }
+ throw new AssertionError("Floating toolbar contains " + itemLabel);
+ }
+
+ /**
+ * ViewAction to sleep to wait floating toolbar's animation.
+ */
+ private static final ViewAction SLEEP = new ViewAction() {
+ private static final long SLEEP_DURATION = 400;
+
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayed();
+ }
+
+ @Override
+ public String getDescription() {
+ return "Sleep " + SLEEP_DURATION + " ms.";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadForAtLeast(SLEEP_DURATION);
+ }
+ };
}
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
index de640ca..e51f2785 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
@@ -22,9 +22,13 @@
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.action.CoordinatesProvider;
import android.support.test.espresso.action.GeneralClickAction;
+import android.support.test.espresso.action.MotionEvents;
+import android.support.test.espresso.action.MotionEvents.DownResultHolder;
import android.support.test.espresso.action.PrecisionDescriber;
+import android.support.test.espresso.action.Press;
import android.support.test.espresso.action.Tapper;
import android.view.View;
+import android.view.ViewConfiguration;
/**
* ViewAction for performing an click on View by a mouse.
@@ -32,10 +36,58 @@
public final class MouseClickAction implements ViewAction {
private final GeneralClickAction mGeneralClickAction;
- public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider,
- PrecisionDescriber precisionDescriber) {
+ public enum CLICK implements Tapper {
+ TRIPLE {
+ @Override
+ public Tapper.Status sendTap(UiController uiController, float[] coordinates,
+ float[] precision) {
+ Tapper.Status stat = sendSingleTap(uiController, coordinates, precision);
+ boolean warning = false;
+ if (stat == Tapper.Status.FAILURE) {
+ return Tapper.Status.FAILURE;
+ } else if (stat == Tapper.Status.WARNING) {
+ warning = true;
+ }
+
+ long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+ for (int i = 0; i < 2; i++) {
+ if (0 < doubleTapMinimumTimeout) {
+ uiController.loopMainThreadForAtLeast(doubleTapMinimumTimeout);
+ }
+ stat = sendSingleTap(uiController, coordinates, precision);
+ if (stat == Tapper.Status.FAILURE) {
+ return Tapper.Status.FAILURE;
+ } else if (stat == Tapper.Status.WARNING) {
+ warning = true;
+ }
+ }
+
+ if (warning) {
+ return Tapper.Status.WARNING;
+ } else {
+ return Tapper.Status.SUCCESS;
+ }
+ }
+ };
+
+ private static Tapper.Status sendSingleTap(UiController uiController,
+ float[] coordinates, float[] precision) {
+ DownResultHolder res = MotionEvents.sendDown(uiController, coordinates, precision);
+ try {
+ if (!MotionEvents.sendUp(uiController, res.down)) {
+ MotionEvents.sendCancel(uiController, res.down);
+ return Tapper.Status.FAILURE;
+ }
+ } finally {
+ res.down.recycle();
+ }
+ return res.longPress ? Tapper.Status.WARNING : Tapper.Status.SUCCESS;
+ }
+ };
+
+ public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider) {
mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider,
- precisionDescriber);
+ Press.PINPOINT);
}
@Override
@@ -51,5 +103,13 @@
@Override
public void perform(UiController uiController, View view) {
mGeneralClickAction.perform(new MouseUiController(uiController), view);
+ long doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
+ if (0 < doubleTapTimeout) {
+ // Wait to avoid false gesture detection. Without this wait, consecutive clicks can be
+ // detected as a triple click. e.g. 2 double clicks are detected as a triple click and
+ // a single click because espresso isn't aware of triple click detection logic, which
+ // is TextView specific gesture.
+ uiController.loopMainThreadForAtLeast(doubleTapTimeout);
+ }
}
}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index 32cc6d6..54d5823 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -64,7 +64,7 @@
*/
public static ViewAction mouseClickOnTextAtIndex(int index) {
return actionWithAssertions(
- new MouseClickAction(Tap.SINGLE, new TextCoordinates(index), Press.PINPOINT));
+ new MouseClickAction(Tap.SINGLE, new TextCoordinates(index)));
}
/**
@@ -94,7 +94,7 @@
*/
public static ViewAction mouseDoubleClickOnTextAtIndex(int index) {
return actionWithAssertions(
- new MouseClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.PINPOINT));
+ new MouseClickAction(Tap.DOUBLE, new TextCoordinates(index)));
}
/**
@@ -124,7 +124,22 @@
*/
public static ViewAction mouseLongClickOnTextAtIndex(int index) {
return actionWithAssertions(
- new MouseClickAction(Tap.LONG, new TextCoordinates(index), Press.PINPOINT));
+ new MouseClickAction(Tap.LONG, new TextCoordinates(index)));
+ }
+
+ /**
+ * Returns an action that triple-clicks by mouse on text at an index on the TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param index The index of the TextView's text to triple-click on.
+ */
+ public static ViewAction mouseTripleClickOnTextAtIndex(int index) {
+ return actionWithAssertions(
+ new MouseClickAction(MouseClickAction.CLICK.TRIPLE, new TextCoordinates(index)));
}
/**
@@ -237,6 +252,28 @@
TextView.class));
}
+ /**
+ * Returns an action that triple click then drags by mouse on text from startIndex to endIndex
+ * on the TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param startIndex The index of the TextView's text to start a drag from
+ * @param endIndex The index of the TextView's text to end the drag at
+ */
+ public static ViewAction mouseTripleClickAndDragOnText(int startIndex, int endIndex) {
+ return actionWithAssertions(
+ new DragAction(
+ DragAction.Drag.MOUSE_TRIPLE_CLICK,
+ new TextCoordinates(startIndex),
+ new TextCoordinates(endIndex),
+ Press.PINPOINT,
+ TextView.class));
+ }
+
public enum Handle {
SELECTION_START,
SELECTION_END,
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index b8b7e76..6309ed3 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -58,12 +58,12 @@
}
}
- public boolean addFont(String path) {
- return nAddFont(mNativePtr, path);
+ public boolean addFont(String path, int ttcIndex) {
+ return nAddFont(mNativePtr, path, ttcIndex);
}
- public boolean addFontWeightStyle(String path, int weight, boolean style) {
- return nAddFontWeightStyle(mNativePtr, path, weight, style);
+ public boolean addFontWeightStyle(String path, int ttcIndex, int weight, boolean style) {
+ return nAddFontWeightStyle(mNativePtr, path, ttcIndex, weight, style);
}
public boolean addFontFromAsset(AssetManager mgr, String path) {
@@ -72,9 +72,9 @@
private static native long nCreateFamily(String lang, int variant);
private static native void nUnrefFamily(long nativePtr);
- private static native boolean nAddFont(long nativeFamily, String path);
+ private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex);
private static native boolean nAddFontWeightStyle(long nativeFamily, String path,
- int weight, boolean isItalic);
+ int ttcIndex, int weight, boolean isItalic);
private static native boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr,
String path);
}
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 97081f9..2596f53 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -43,12 +43,14 @@
}
public static class Font {
- Font(String fontName, int weight, boolean isItalic) {
+ Font(String fontName, int ttcIndex, int weight, boolean isItalic) {
this.fontName = fontName;
+ this.ttcIndex = ttcIndex;
this.weight = weight;
this.isItalic = isItalic;
}
public String fontName;
+ public int ttcIndex;
public int weight;
public boolean isItalic;
}
@@ -112,12 +114,14 @@
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (tag.equals("font")) {
+ String ttcIndexStr = parser.getAttributeValue(null, "ttcIndex");
+ int ttcIndex = ttcIndexStr == null ? 0 : Integer.parseInt(ttcIndexStr);
String weightStr = parser.getAttributeValue(null, "weight");
int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
String filename = parser.nextText();
String fullFilename = "/system/fonts/" + filename;
- fonts.add(new Font(fullFilename, weight, isItalic));
+ fonts.add(new Font(fullFilename, ttcIndex, weight, isItalic));
} else {
skip(parser);
}
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 7aa0aef..a226e85 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -361,6 +361,17 @@
public static final int RAW_SENSOR = 0x20;
/**
+ * <p>Private raw camera sensor image format, a single channel image with
+ * implementation depedent pixel layout.</p>
+ *
+ * <p>RAW_PRIVATE is a format for unprocessed raw image buffers coming from an
+ * image sensor. The actual structure of buffers of this format is
+ * implementation-dependent.</p>
+ *
+ */
+ public static final int RAW_PRIVATE = 0x24;
+
+ /**
* <p>
* Android 10-bit raw format
* </p>
@@ -748,6 +759,7 @@
case FLEX_RGB_888:
case FLEX_RGBA_8888:
case RAW_SENSOR:
+ case RAW_PRIVATE:
case RAW10:
case RAW12:
case DEPTH16:
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 7eb5584..1294323 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -209,7 +209,7 @@
public static Typeface createFromFile(String path) {
if (sFallbackFonts != null) {
FontFamily fontFamily = new FontFamily();
- if (fontFamily.addFont(path)) {
+ if (fontFamily.addFont(path, 0 /* ttcIndex */)) {
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families);
}
@@ -262,7 +262,7 @@
private static FontFamily makeFamilyFromParsed(FontListParser.Family family) {
FontFamily fontFamily = new FontFamily(family.lang, family.variant);
for (FontListParser.Font font : family.fonts) {
- fontFamily.addFontWeightStyle(font.fontName, font.weight, font.isItalic);
+ fontFamily.addFontWeightStyle(font.fontName, font.ttcIndex, font.weight, font.isItalic);
}
return fontFamily;
}
diff --git a/include/android_runtime/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
index ed83314..b1e552a 100644
--- a/include/android_runtime/android_view_Surface.h
+++ b/include/android_runtime/android_view_Surface.h
@@ -45,7 +45,9 @@
RAW_SENSOR = 0x20,
PRIVATE = 0x22,
YUV_420_888 = 0x23,
+ RAW_PRIVATE = 0x24,
RAW10 = 0x25,
+ RAW12 = 0x26,
JPEG = 0x100,
DEPTH_POINT_CLOUD = 0x101,
YV12 = 0x32315659,
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index fde12dd..d4dbb00 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -20,6 +20,8 @@
#include "Caches.h"
#include "Glop.h"
#include "GlopBuilder.h"
+#include "Patch.h"
+#include "PathTessellator.h"
#include "renderstate/OffscreenBufferPool.h"
#include "renderstate/RenderState.h"
#include "utils/GLUtils.h"
@@ -27,6 +29,7 @@
#include <algorithm>
#include <math.h>
+#include <SkPaintDefaults.h>
namespace android {
namespace uirenderer {
@@ -54,8 +57,6 @@
if (entry) {
entry->uvMapper.map(texCoords);
}
- // init to non-empty, so we can safely expandtoCoverRect
- Rect totalBounds = firstState.computedState.clippedBounds;
for (size_t i = 0; i < opList.count; i++) {
const BakedOpState& state = *(opList.states[i]);
TextureVertex* rectVerts = &vertices[i * 4];
@@ -66,8 +67,6 @@
}
storeTexturedRect(rectVerts, opBounds, texCoords);
renderer.dirtyRenderTarget(opBounds);
-
- totalBounds.expandToCover(opBounds);
}
const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
@@ -78,7 +77,111 @@
.setMeshTexturedIndexedQuads(vertices, opList.count * 6)
.setFillTexturePaint(*texture, textureFillFlags, firstState.op->paint, firstState.alpha)
.setTransform(Matrix4::identity(), TransformFlags::None)
- .setModelViewOffsetRect(0, 0, totalBounds) // don't snap here, we snap per-quad above
+ .setModelViewIdentityEmptyBounds()
+ .build();
+ renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
+}
+
+void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
+ const MergedBakedOpList& opList) {
+ const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op));
+ const BakedOpState& firstState = *(opList.states[0]);
+ AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(
+ firstOp.bitmap->pixelRef());
+
+ // Batches will usually contain a small number of items so it's
+ // worth performing a first iteration to count the exact number
+ // of vertices we need in the new mesh
+ uint32_t totalVertices = 0;
+
+ for (size_t i = 0; i < opList.count; i++) {
+ const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
+
+ // TODO: cache mesh lookups
+ const Patch* opMesh = renderer.caches().patchCache.get(
+ entry, op.bitmap->width(), op.bitmap->height(),
+ op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
+ totalVertices += opMesh->verticesCount;
+ }
+
+ const bool dirtyRenderTarget = renderer.offscreenRenderTarget();
+
+ uint32_t indexCount = 0;
+
+ TextureVertex vertices[totalVertices];
+ TextureVertex* vertex = &vertices[0];
+ // Create a mesh that contains the transformed vertices for all the
+ // 9-patch objects that are part of the batch. Note that onDefer()
+ // enforces ops drawn by this function to have a pure translate or
+ // identity matrix
+ for (size_t i = 0; i < opList.count; i++) {
+ const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
+ const BakedOpState& state = *opList.states[i];
+
+ // TODO: cache mesh lookups
+ const Patch* opMesh = renderer.caches().patchCache.get(
+ entry, op.bitmap->width(), op.bitmap->height(),
+ op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
+
+
+ uint32_t vertexCount = opMesh->verticesCount;
+ if (vertexCount == 0) continue;
+
+ // We use the bounds to know where to translate our vertices
+ // Using patchOp->state.mBounds wouldn't work because these
+ // bounds are clipped
+ const float tx = floorf(state.computedState.transform.getTranslateX()
+ + op.unmappedBounds.left + 0.5f);
+ const float ty = floorf(state.computedState.transform.getTranslateY()
+ + op.unmappedBounds.top + 0.5f);
+
+ // Copy & transform all the vertices for the current operation
+ TextureVertex* opVertices = opMesh->vertices.get();
+ for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
+ TextureVertex::set(vertex++,
+ opVertices->x + tx, opVertices->y + ty,
+ opVertices->u, opVertices->v);
+ }
+
+ // Dirty the current layer if possible. When the 9-patch does not
+ // contain empty quads we can take a shortcut and simply set the
+ // dirty rect to the object's bounds.
+ if (dirtyRenderTarget) {
+ if (!opMesh->hasEmptyQuads) {
+ renderer.dirtyRenderTarget(Rect(tx, ty,
+ tx + op.unmappedBounds.getWidth(), ty + op.unmappedBounds.getHeight()));
+ } else {
+ const size_t count = opMesh->quads.size();
+ for (size_t i = 0; i < count; i++) {
+ const Rect& quadBounds = opMesh->quads[i];
+ const float x = tx + quadBounds.left;
+ const float y = ty + quadBounds.top;
+ renderer.dirtyRenderTarget(Rect(x, y,
+ x + quadBounds.getWidth(), y + quadBounds.getHeight()));
+ }
+ }
+ }
+
+ indexCount += opMesh->indexCount;
+ }
+
+
+ Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap);
+ if (!texture) return;
+ const AutoTexture autoCleanup(texture);
+
+ // 9 patches are built for stretching - always filter
+ int textureFillFlags = TextureFillFlags::ForceFilter;
+ if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) {
+ textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
+ }
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(firstState.roundRectClipState)
+ .setMeshTexturedIndexedQuads(vertices, indexCount)
+ .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha)
+ .setTransform(Matrix4::identity(), TransformFlags::None)
+ .setModelViewIdentityEmptyBounds()
.build();
renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
}
@@ -206,6 +309,98 @@
LOG_ALWAYS_FATAL("unsupported operation");
}
+void BakedOpDispatcher::onCirclePropsOp(BakedOpRenderer&, const CirclePropsOp&, const BakedOpState&) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpDispatcher::onRoundRectPropsOp(BakedOpRenderer&, const RoundRectPropsOp&, const BakedOpState&) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+namespace VertexBufferRenderFlags {
+ enum {
+ Offset = 0x1,
+ ShadowInterp = 0x2,
+ };
+}
+
+static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
+ const VertexBuffer& vertexBuffer, float translateX, float translateY,
+ const SkPaint& paint, int vertexBufferRenderFlags) {
+ if (CC_LIKELY(vertexBuffer.getVertexCount())) {
+ bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
+ const int transformFlags = TransformFlags::OffsetByFudgeFactor;
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshVertexBuffer(vertexBuffer, shadowInterp)
+ .setFillPaint(paint, state.alpha)
+ .setTransform(state.computedState.transform, transformFlags)
+ .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
+ .build();
+ renderer.renderGlop(state, glop);
+ }
+}
+
+static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
+ const SkPath& path, const SkPaint& paint) {
+ VertexBuffer vertexBuffer;
+ // TODO: try clipping large paths to viewport
+ PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
+ renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
+}
+
+static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
+ PathTexture& texture, const RecordedOp& op) {
+ Rect dest(texture.width, texture.height);
+ dest.translate(texture.left + op.unmappedBounds.left - texture.offset,
+ texture.top + op.unmappedBounds.top - texture.offset);
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshTexturedUnitQuad(nullptr)
+ .setFillPathTexturePaint(texture, *(op.paint), state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewMapUnitToRect(dest)
+ .build();
+ renderer.renderGlop(state, glop);
+}
+
+SkRect getBoundsOfFill(const RecordedOp& op) {
+ SkRect bounds = op.unmappedBounds.toSkRect();
+ if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
+ float outsetDistance = op.paint->getStrokeWidth() / 2;
+ bounds.outset(outsetDistance, outsetDistance);
+ }
+ return bounds;
+}
+
+void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) {
+ // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
+ if (op.paint->getStyle() != SkPaint::kStroke_Style
+ || op.paint->getPathEffect() != nullptr
+ || op.useCenter) {
+ PathTexture* texture = renderer.caches().pathCache.getArc(
+ op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
+ op.startAngle, op.sweepAngle, op.useCenter, op.paint);
+ const AutoTexture holder(texture);
+ if (CC_LIKELY(holder.texture)) {
+ renderPathTexture(renderer, state, *texture, op);
+ }
+ } else {
+ SkRect rect = getBoundsOfFill(op);
+ SkPath path;
+ if (op.useCenter) {
+ path.moveTo(rect.centerX(), rect.centerY());
+ }
+ path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
+ if (op.useCenter) {
+ path.close();
+ }
+ renderConvexPath(renderer, state, path, *(op.paint));
+ }
+}
+
void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
Texture* texture = renderer.getTexture(op.bitmap);
if (!texture) return;
@@ -224,44 +419,230 @@
renderer.renderGlop(state, glop);
}
-void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
- LOG_ALWAYS_FATAL("todo");
-}
+void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) {
+ const static UvMapper defaultUvMapper;
+ const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;
-void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
+ std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
+ ColorTextureVertex* vertex = &mesh[0];
+
+ const int* colors = op.colors;
+ std::unique_ptr<int[]> tempColors;
+ if (!colors) {
+ uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1);
+ tempColors.reset(new int[colorsCount]);
+ memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
+ colors = tempColors.get();
+ }
+
+ Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef());
+ const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper);
+
+ for (int32_t y = 0; y < op.meshHeight; y++) {
+ for (int32_t x = 0; x < op.meshWidth; x++) {
+ uint32_t i = (y * (op.meshWidth + 1) + x) * 2;
+
+ float u1 = float(x) / op.meshWidth;
+ float u2 = float(x + 1) / op.meshWidth;
+ float v1 = float(y) / op.meshHeight;
+ float v2 = float(y + 1) / op.meshHeight;
+
+ mapper.map(u1, v1, u2, v2);
+
+ int ax = i + (op.meshWidth + 1) * 2;
+ int ay = ax + 1;
+ int bx = i;
+ int by = bx + 1;
+ int cx = i + 2;
+ int cy = cx + 1;
+ int dx = i + (op.meshWidth + 1) * 2 + 2;
+ int dy = dx + 1;
+
+ const float* vertices = op.vertices;
+ ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
+ ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
+ ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
+
+ ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
+ ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
+ ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
+ }
+ }
+
+ if (!texture) {
+ texture = renderer.caches().textureCache.get(op.bitmap);
+ if (!texture) {
+ return;
+ }
+ }
+ const AutoTexture autoCleanup(texture);
+
+ /*
+ * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
+ * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
+ */
+ const int textureFillFlags = TextureFillFlags::None;
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
.setRoundRectClipState(state.roundRectClipState)
- .setMeshUnitQuad()
- .setFillPaint(*op.paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRect(op.unmappedBounds)
+ .setMeshColoredTexturedMesh(mesh.get(), elementCount)
+ .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewOffsetRect(0, 0, op.unmappedBounds)
.build();
renderer.renderGlop(state, glop);
}
-namespace VertexBufferRenderFlags {
- enum {
- Offset = 0x1,
- ShadowInterp = 0x2,
- };
+void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) {
+ Texture* texture = renderer.getTexture(op.bitmap);
+ if (!texture) return;
+ const AutoTexture autoCleanup(texture);
+
+ Rect uv(std::max(0.0f, op.src.left / texture->width),
+ std::max(0.0f, op.src.top / texture->height),
+ std::min(1.0f, op.src.right / texture->width),
+ std::min(1.0f, op.src.bottom / texture->height));
+
+ const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
+ ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
+ const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth())
+ && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight());
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshTexturedUvQuad(texture->uvMapper, uv)
+ .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
+ .build();
+ renderer.renderGlop(state, glop);
}
-static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
- const VertexBuffer& vertexBuffer, float translateX, float translateY,
- SkPaint& paint, int vertexBufferRenderFlags) {
- if (CC_LIKELY(vertexBuffer.getVertexCount())) {
- bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
- const int transformFlags = TransformFlags::OffsetByFudgeFactor;
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshVertexBuffer(vertexBuffer, shadowInterp)
- .setFillPaint(paint, state.alpha)
- .setTransform(state.computedState.transform, transformFlags)
- .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
- .build();
- renderer.renderGlop(state, glop);
+void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
+ VertexBuffer buffer;
+ PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
+ state.computedState.transform, buffer);
+ int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
+ renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
+}
+
+void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) {
+ if (op.paint->getPathEffect() != nullptr) {
+ PathTexture* texture = renderer.caches().pathCache.getOval(
+ op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
+ const AutoTexture holder(texture);
+ if (CC_LIKELY(holder.texture)) {
+ renderPathTexture(renderer, state, *texture, op);
+ }
+ } else {
+ SkPath path;
+ SkRect rect = getBoundsOfFill(op);
+ path.addOval(rect);
+ renderConvexPath(renderer, state, path, *(op.paint));
+ }
+}
+
+void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) {
+ // 9 patches are built for stretching - always filter
+ int textureFillFlags = TextureFillFlags::ForceFilter;
+ if (op.bitmap->colorType() == kAlpha_8_SkColorType) {
+ textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
+ }
+
+ // TODO: avoid redoing the below work each frame:
+ AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef());
+ const Patch* mesh = renderer.caches().patchCache.get(
+ entry, op.bitmap->width(), op.bitmap->height(),
+ op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
+
+ Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
+ if (!texture) return;
+ const AutoTexture autoCleanup(texture);
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshPatchQuads(*mesh)
+ .setMeshTexturedUnitQuad(texture->uvMapper)
+ .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
+ Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
+ .build();
+ renderer.renderGlop(state, glop);
+}
+
+void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
+ PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
+ const AutoTexture holder(texture);
+ if (CC_LIKELY(holder.texture)) {
+ renderPathTexture(renderer, state, *texture, op);
+ }
+}
+
+void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
+ VertexBuffer buffer;
+ PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
+ state.computedState.transform, buffer);
+ int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
+ renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
+}
+
+// See SkPaintDefaults.h
+#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
+
+void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
+ if (op.paint->getStyle() != SkPaint::kFill_Style) {
+ // only fill + default miter is supported by drawConvexPath, since others must handle joins
+ static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
+ if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
+ || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
+ || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
+ PathTexture* texture = renderer.caches().pathCache.getRect(
+ op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
+ const AutoTexture holder(texture);
+ if (CC_LIKELY(holder.texture)) {
+ renderPathTexture(renderer, state, *texture, op);
+ }
+ } else {
+ SkPath path;
+ path.addRect(getBoundsOfFill(op));
+ renderConvexPath(renderer, state, path, *(op.paint));
+ }
+ } else {
+ if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
+ SkPath path;
+ path.addRect(op.unmappedBounds.toSkRect());
+ renderConvexPath(renderer, state, path, *(op.paint));
+ } else {
+ // render simple unit quad, no tessellation required
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshUnitQuad()
+ .setFillPaint(*op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewMapUnitToRect(op.unmappedBounds)
+ .build();
+ renderer.renderGlop(state, glop);
+ }
+ }
+}
+
+void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) {
+ if (op.paint->getPathEffect() != nullptr) {
+ PathTexture* texture = renderer.caches().pathCache.getRoundRect(
+ op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
+ op.rx, op.ry, op.paint);
+ const AutoTexture holder(texture);
+ if (CC_LIKELY(holder.texture)) {
+ renderPathTexture(renderer, state, *texture, op);
+ }
+ } else {
+ const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
+ state.computedState.transform, *(op.paint),
+ op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
+ renderVertexBuffer(renderer, state, *buffer,
+ op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
}
}
@@ -323,8 +704,6 @@
void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
OffscreenBuffer* buffer = *op.layerHandle;
- // TODO: extend this to handle HW layers & paint properties which
- // reside in node.properties().layerProperties()
float layerAlpha = op.alpha * state.alpha;
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
diff --git a/libs/hwui/BakedOpDispatcher.h b/libs/hwui/BakedOpDispatcher.h
index 0e763d9..ed34ada 100644
--- a/libs/hwui/BakedOpDispatcher.h
+++ b/libs/hwui/BakedOpDispatcher.h
@@ -26,6 +26,10 @@
/**
* Provides all "onBitmapOp(...)" style static methods for every op type, which convert the
* RecordedOps and their state to Glops, and renders them with the provided BakedOpRenderer.
+ *
+ * onXXXOp methods must either render directly with the renderer, or call a static renderYYY
+ * method to render content. There should never be draw content rejection in BakedOpDispatcher -
+ * it must happen at a higher level (except in error-ish cases, like texture-too-big).
*/
class BakedOpDispatcher {
public:
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index 983c27b..9c836a0 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -53,7 +53,7 @@
class ResolvedRenderState {
public:
// TODO: remove the mapRects/matrix multiply when snapshot & recorded transforms are translates
- ResolvedRenderState(const Snapshot& snapshot, const RecordedOp& recordedOp) {
+ ResolvedRenderState(const Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke) {
/* TODO: benchmark a fast path for translate-only matrices, such as:
if (CC_LIKELY(snapshot.transform->getType() == Matrix4::kTypeTranslate
&& recordedOp.localMatrix.getType() == Matrix4::kTypeTranslate)) {
@@ -83,7 +83,17 @@
// resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
clippedBounds = recordedOp.unmappedBounds;
+ if (CC_UNLIKELY(expandForStroke)) {
+ // account for non-hairline stroke
+ clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f);
+ }
transform.mapRect(clippedBounds);
+ if (CC_UNLIKELY(expandForStroke
+ && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) {
+ // account for hairline stroke when stroke may be < 1 scaled pixel
+ // Non translate || strokeWidth < 1 is conservative, but will cover all cases
+ clippedBounds.outset(0.5f);
+ }
if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left;
if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top;
@@ -129,13 +139,36 @@
public:
static BakedOpState* tryConstruct(LinearAllocator& allocator,
const Snapshot& snapshot, const RecordedOp& recordedOp) {
- BakedOpState* bakedOp = new (allocator) BakedOpState(snapshot, recordedOp);
- if (bakedOp->computedState.clippedBounds.isEmpty()) {
+ BakedOpState* bakedState = new (allocator) BakedOpState(snapshot, recordedOp, false);
+ if (bakedState->computedState.clippedBounds.isEmpty()) {
// bounds are empty, so op is rejected
- allocator.rewindIfLastAlloc(bakedOp);
+ allocator.rewindIfLastAlloc(bakedState);
return nullptr;
}
- return bakedOp;
+ return bakedState;
+ }
+
+ enum class StrokeBehavior {
+ // stroking is forced, regardless of style on paint
+ Forced,
+ // stroking is defined by style on paint
+ StyleDefined,
+ };
+
+ static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator,
+ const Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
+ bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
+ ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
+ : true;
+
+ BakedOpState* bakedState = new (allocator) BakedOpState(
+ snapshot, recordedOp, expandForStroke);
+ if (bakedState->computedState.clippedBounds.isEmpty()) {
+ // bounds are empty, so op is rejected
+ allocator.rewindIfLastAlloc(bakedState);
+ return nullptr;
+ }
+ return bakedState;
}
static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
@@ -160,8 +193,8 @@
const RecordedOp* op;
private:
- BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp)
- : computedState(snapshot, recordedOp)
+ BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke)
+ : computedState(snapshot, recordedOp, expandForStroke)
, alpha(snapshot.alpha)
, roundRectClipState(snapshot.roundRectClipState)
, projectionPathMask(snapshot.projectionPathMask)
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index b585a27..0643a54 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -113,10 +113,10 @@
// Geometry
virtual void drawPoint(float x, float y, const SkPaint& paint) = 0;
- virtual void drawPoints(const float* points, int count, const SkPaint& paint) = 0;
+ virtual void drawPoints(const float* points, int floatCount, const SkPaint& paint) = 0;
virtual void drawLine(float startX, float startY, float stopX, float stopY,
const SkPaint& paint) = 0;
- virtual void drawLines(const float* points, int count, const SkPaint& paint) = 0;
+ virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) = 0;
virtual void drawRect(float left, float top, float right, float bottom,
const SkPaint& paint) = 0;
virtual void drawRegion(const SkRegion& region, const SkPaint& paint) = 0;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 9c8649f..8acdb62 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -73,7 +73,7 @@
.setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
.setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha)
.setTransform(bakedState->computedState.transform, transformFlags)
- .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
+ .setModelViewIdentityEmptyBounds()
.build();
// Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer
renderer->renderGlop(nullptr, clip, glop);
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index 4785ea4..bcf819e 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -64,7 +64,7 @@
// Canvas transform isn't applied to the mesh at draw time,
//since it's already built in.
- MeshIgnoresCanvasTransform = 1 << 1,
+ MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove
};
};
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index b647b90..6e5797d 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -95,6 +95,10 @@
return setModelViewOffsetRect(offsetX, offsetY, source);
}
}
+ GlopBuilder& setModelViewIdentityEmptyBounds() {
+ // pass empty rect since not needed for damage / snap
+ return setModelViewOffsetRect(0, 0, Rect());
+ }
GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState);
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index 9460361..ec03e83 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -467,13 +467,13 @@
// (temp layers are clipped to viewport, since they don't persist offscreen content)
SkPaint saveLayerPaint;
saveLayerPaint.setAlpha(properties.getAlpha());
- onBeginLayerOp(*new (mAllocator) BeginLayerOp(
+ deferBeginLayerOp(*new (mAllocator) BeginLayerOp(
saveLayerBounds,
Matrix4::identity(),
saveLayerBounds,
&saveLayerPaint));
deferNodeOps(node);
- onEndLayerOp(*new (mAllocator) EndLayerOp());
+ deferEndLayerOp(*new (mAllocator) EndLayerOp());
} else {
deferNodeOps(node);
}
@@ -559,7 +559,7 @@
}
const RenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
- deferRenderNodeOp(*childOp);
+ deferRenderNodeOpImpl(*childOp);
drawIndex++;
}
}
@@ -645,7 +645,7 @@
int restoreTo = mCanvasState.save(SkCanvas::kMatrix_SaveFlag);
mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor);
- deferRenderNodeOp(*childOp);
+ deferRenderNodeOpImpl(*childOp);
mCanvasState.restoreToCount(restoreTo);
}
@@ -653,13 +653,13 @@
}
/**
- * Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
+ * Used to define a list of lambdas referencing private OpReorderer::onXX::defer() methods.
*
* This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
* E.g. a BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
*/
#define OP_RECEIVER(Type) \
- [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast<const Type&>(op)); },
+ [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); },
void OpReorderer::deferNodeOps(const RenderNode& renderNode) {
typedef void (*OpDispatcher) (OpReorderer& reorderer, const RecordedOp& op);
static OpDispatcher receivers[] = {
@@ -687,7 +687,7 @@
}
}
-void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+void OpReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
if (op.renderNode->nothingToDraw()) return;
int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
@@ -702,19 +702,43 @@
mCanvasState.restoreToCount(count);
}
-void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
+void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
if (!op.skipInOrderDraw) {
- deferRenderNodeOp(op);
+ deferRenderNodeOpImpl(op);
}
}
-static batchid_t tessellatedBatchId(const SkPaint& paint) {
+/**
+ * Defers an unmergeable, strokeable op, accounting correctly
+ * for paint's style on the bounds being computed.
+ */
+void OpReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+ BakedOpState::StrokeBehavior strokeBehavior) {
+ // Note: here we account for stroke when baking the op
+ BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
+ mAllocator, *mCanvasState.currentSnapshot(), op, strokeBehavior);
+ if (!bakedState) return; // quick rejected
+ currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId);
+}
+
+/**
+ * Returns batch id for tessellatable shapes, based on paint. Checks to see if path effect/AA will
+ * be used, since they trigger significantly different rendering paths.
+ *
+ * Note: not used for lines/points, since they don't currently support path effects.
+ */
+static batchid_t tessBatchId(const RecordedOp& op) {
+ const SkPaint& paint = *(op.paint);
return paint.getPathEffect()
? OpBatchType::AlphaMaskTexture
: (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
}
-void OpReorderer::onBitmapOp(const BitmapOp& op) {
+void OpReorderer::deferArcOp(const ArcOp& op) {
+ deferStrokeableOp(op, tessBatchId(op));
+}
+
+void OpReorderer::deferBitmapOp(const BitmapOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
@@ -733,25 +757,94 @@
}
}
-void OpReorderer::onLinesOp(const LinesOp& op) {
+void OpReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, tessellatedBatchId(*op.paint));
+ currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
-void OpReorderer::onRectOp(const RectOp& op) {
+void OpReorderer::deferBitmapRectOp(const BitmapRectOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, tessellatedBatchId(*op.paint));
+ currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
-void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) {
+void OpReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
+ // allocate a temporary oval op (with mAllocator, so it persists until render), so the
+ // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
+ float x = *(op.x);
+ float y = *(op.y);
+ float radius = *(op.radius);
+ Rect unmappedBounds(x - radius, y - radius, x + radius, y + radius);
+ const OvalOp* resolvedOp = new (mAllocator) OvalOp(
+ unmappedBounds,
+ op.localMatrix,
+ op.localClipRect,
+ op.paint);
+ deferOvalOp(*resolvedOp);
+}
+
+void OpReorderer::deferLinesOp(const LinesOp& op) {
+ batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
+ deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
+}
+
+void OpReorderer::deferOvalOp(const OvalOp& op) {
+ deferStrokeableOp(op, tessBatchId(op));
+}
+
+void OpReorderer::deferPatchOp(const PatchOp& op) {
+ BakedOpState* bakedState = tryBakeOpState(op);
+ if (!bakedState) return; // quick rejected
+
+ if (bakedState->computedState.transform.isPureTranslate()
+ && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode) {
+ mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+ // TODO: AssetAtlas in mergeId
+
+ // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together
+ currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId);
+ } else {
+ // Use Bitmap batchId since Bitmap+Patch use same shader
+ currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
+ }
+}
+
+void OpReorderer::deferPathOp(const PathOp& op) {
+ deferStrokeableOp(op, OpBatchType::Bitmap);
+}
+
+void OpReorderer::deferPointsOp(const PointsOp& op) {
+ batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
+ deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
+}
+
+void OpReorderer::deferRectOp(const RectOp& op) {
+ deferStrokeableOp(op, tessBatchId(op));
+}
+
+void OpReorderer::deferRoundRectOp(const RoundRectOp& op) {
+ deferStrokeableOp(op, tessBatchId(op));
+}
+
+void OpReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
+ // allocate a temporary round rect op (with mAllocator, so it persists until render), so the
+ // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
+ const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
+ Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)),
+ op.localMatrix,
+ op.localClipRect,
+ op.paint, *op.rx, *op.ry);
+ deferRoundRectOp(*resolvedOp);
+}
+
+void OpReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
}
-void OpReorderer::onTextOp(const TextOp& op) {
+void OpReorderer::deferTextOp(const TextOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
@@ -794,7 +887,7 @@
}
// TODO: test rejection at defer time, where the bounds become empty
-void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
+void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
@@ -839,7 +932,7 @@
&op, nullptr);
}
-void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
+void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
int finishedLayerIndex = mLayerStack.back();
@@ -865,11 +958,11 @@
}
}
-void OpReorderer::onLayerOp(const LayerOp& op) {
+void OpReorderer::deferLayerOp(const LayerOp& op) {
LOG_ALWAYS_FATAL("unsupported");
}
-void OpReorderer::onShadowOp(const ShadowOp& op) {
+void OpReorderer::deferShadowOp(const ShadowOp& op) {
LOG_ALWAYS_FATAL("unsupported");
}
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index fc77c61..35343c8b 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -45,7 +45,7 @@
enum {
None = 0, // Don't batch
Bitmap,
- Patch,
+ MergedPatch,
AlphaVertices,
Vertices,
AlphaMaskTexture,
@@ -237,7 +237,7 @@
void deferNodeOps(const RenderNode& renderNode);
- void deferRenderNodeOp(const RenderNodeOp& op);
+ void deferRenderNodeOpImpl(const RenderNodeOp& op);
void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers);
@@ -245,14 +245,18 @@
mFrameAllocatedPaths.emplace_back(new SkPath);
return mFrameAllocatedPaths.back().get();
}
+
+ void deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+ BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
+
/**
- * Declares all OpReorderer::onXXXXOp() methods for every RecordedOp type.
+ * Declares all OpReorderer::deferXXXXOp() methods for every RecordedOp type.
*
* These private methods are called from within deferImpl to defer each individual op
* type differently.
*/
#define INTERNAL_OP_HANDLER(Type) \
- void on##Type(const Type& op);
+ void defer##Type(const Type& op);
MAP_OPS(INTERNAL_OP_HANDLER)
std::vector<std::unique_ptr<SkPath> > mFrameAllocatedPaths;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2cb32c4..f49237c 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -40,6 +40,7 @@
#include <SkCanvas.h>
#include <SkColor.h>
+#include <SkPaintDefaults.h>
#include <SkPathOps.h>
#include <SkShader.h>
#include <SkTypeface.h>
@@ -1908,9 +1909,6 @@
drawConvexPath(path, p);
}
-// See SkPaintDefaults.h
-#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
-
void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
const SkPaint* p) {
if (mState.currentlyIgnored()
@@ -1921,6 +1919,7 @@
if (p->getStyle() != SkPaint::kFill_Style) {
// only fill style is supported by drawConvexPath, since others have to handle joins
+ static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
if (p->getPathEffect() != nullptr || p->getStrokeJoin() != SkPaint::kMiter_Join ||
p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
mCaches.textureState().activateTexture(0);
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index b966401..d1a4866 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -24,7 +24,8 @@
#include "utils/LinearAllocator.h"
#include "Vector.h"
-#include "SkXfermode.h"
+#include <androidfw/ResourceTypes.h>
+#include <SkXfermode.h>
class SkBitmap;
class SkPaint;
@@ -43,10 +44,20 @@
* This serves as the authoritative list of ops, used for generating ID enum, and ID based LUTs.
*/
#define MAP_OPS_BASED_ON_MERGEABILITY(U_OP_FN, M_OP_FN) \
+ U_OP_FN(ArcOp) \
M_OP_FN(BitmapOp) \
+ U_OP_FN(BitmapMeshOp) \
+ U_OP_FN(BitmapRectOp) \
+ U_OP_FN(CirclePropsOp) \
U_OP_FN(LinesOp) \
+ U_OP_FN(OvalOp) \
+ M_OP_FN(PatchOp) \
+ U_OP_FN(PathOp) \
+ U_OP_FN(PointsOp) \
U_OP_FN(RectOp) \
U_OP_FN(RenderNodeOp) \
+ U_OP_FN(RoundRectOp) \
+ U_OP_FN(RoundRectPropsOp) \
U_OP_FN(ShadowOp) \
U_OP_FN(SimpleRectsOp) \
M_OP_FN(TextOp) \
@@ -74,7 +85,7 @@
Count,
};
}
-static_assert(RecordedOpId::BitmapOp == 0,
+static_assert(RecordedOpId::ArcOp == 0,
"First index must be zero for LUTs to work");
#define BASE_PARAMS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint
@@ -86,7 +97,7 @@
/* ID from RecordedOpId - generally used for jumping into function tables */
const int opId;
- /* bounds in *local* space, without accounting for DisplayList transformation */
+ /* bounds in *local* space, without accounting for DisplayList transformation, or stroke */
const Rect unmappedBounds;
/* transform in recording space (vs DisplayList origin) */
@@ -128,6 +139,17 @@
// Standard Ops
////////////////////////////////////////////////////////////////////////////////////////////////////
+struct ArcOp : RecordedOp {
+ ArcOp(BASE_PARAMS, float startAngle, float sweepAngle, bool useCenter)
+ : SUPER(ArcOp)
+ , startAngle(startAngle)
+ , sweepAngle(sweepAngle)
+ , useCenter(useCenter) {}
+ const float startAngle;
+ const float sweepAngle;
+ const bool useCenter;
+};
+
struct BitmapOp : RecordedOp {
BitmapOp(BASE_PARAMS, const SkBitmap* bitmap)
: SUPER(BitmapOp)
@@ -136,6 +158,43 @@
// TODO: asset atlas/texture id lookup?
};
+struct BitmapMeshOp : RecordedOp {
+ BitmapMeshOp(BASE_PARAMS, const SkBitmap* bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors)
+ : SUPER(BitmapMeshOp)
+ , bitmap(bitmap)
+ , meshWidth(meshWidth)
+ , meshHeight(meshHeight)
+ , vertices(vertices)
+ , colors(colors) {}
+ const SkBitmap* bitmap;
+ const int meshWidth;
+ const int meshHeight;
+ const float* vertices;
+ const int* colors;
+};
+
+struct BitmapRectOp : RecordedOp {
+ BitmapRectOp(BASE_PARAMS, const SkBitmap* bitmap, const Rect& src)
+ : SUPER(BitmapRectOp)
+ , bitmap(bitmap)
+ , src(src) {}
+ const SkBitmap* bitmap;
+ const Rect src;
+};
+
+struct CirclePropsOp : RecordedOp {
+ CirclePropsOp(const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint,
+ float* x, float* y, float* radius)
+ : RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClipRect, paint)
+ , x(x)
+ , y(y)
+ , radius(radius) {}
+ const float* x;
+ const float* y;
+ const float* radius;
+};
+
struct LinesOp : RecordedOp {
LinesOp(BASE_PARAMS, const float* points, const int floatCount)
: SUPER(LinesOp)
@@ -145,11 +204,68 @@
const int floatCount;
};
+struct OvalOp : RecordedOp {
+ OvalOp(BASE_PARAMS)
+ : SUPER(OvalOp) {}
+};
+
+struct PatchOp : RecordedOp {
+ PatchOp(BASE_PARAMS, const SkBitmap* bitmap, const Res_png_9patch* patch)
+ : SUPER(PatchOp)
+ , bitmap(bitmap)
+ , patch(patch) {}
+ const SkBitmap* bitmap;
+ const Res_png_9patch* patch;
+};
+
+struct PathOp : RecordedOp {
+ PathOp(BASE_PARAMS, const SkPath* path)
+ : SUPER(PathOp)
+ , path(path) {}
+ const SkPath* path;
+};
+
+struct PointsOp : RecordedOp {
+ PointsOp(BASE_PARAMS, const float* points, const int floatCount)
+ : SUPER(PointsOp)
+ , points(points)
+ , floatCount(floatCount) {}
+ const float* points;
+ const int floatCount;
+};
+
struct RectOp : RecordedOp {
RectOp(BASE_PARAMS)
: SUPER(RectOp) {}
};
+struct RoundRectOp : RecordedOp {
+ RoundRectOp(BASE_PARAMS, float rx, float ry)
+ : SUPER(RoundRectOp)
+ , rx(rx)
+ , ry(ry) {}
+ const float rx;
+ const float ry;
+};
+
+struct RoundRectPropsOp : RecordedOp {
+ RoundRectPropsOp(const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint,
+ float* left, float* top, float* right, float* bottom, float *rx, float *ry)
+ : RecordedOp(RecordedOpId::RoundRectPropsOp, Rect(), localMatrix, localClipRect, paint)
+ , left(left)
+ , top(top)
+ , right(right)
+ , bottom(bottom)
+ , rx(rx)
+ , ry(ry) {}
+ const float* left;
+ const float* top;
+ const float* right;
+ const float* bottom;
+ const float* rx;
+ const float* ry;
+};
+
/**
* Real-time, dynamic-lit shadow.
*
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index e6020cd..1bf92be 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -237,26 +237,32 @@
refPaint(&paint)));
}
+static Rect calcBoundsOfPoints(const float* points, int floatCount) {
+ Rect unmappedBounds(points[0], points[1], points[0], points[1]);
+ for (int i = 2; i < floatCount; i += 2) {
+ unmappedBounds.expandToCover(points[i], points[i + 1]);
+ }
+ return unmappedBounds;
+}
+
// Geometry
-void RecordingCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
- LOG_ALWAYS_FATAL("TODO!");
+void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPaint& paint) {
+ if (floatCount < 2) return;
+ floatCount &= ~0x1; // round down to nearest two
+
+ addOp(new (alloc()) PointsOp(
+ calcBoundsOfPoints(points, floatCount),
+ *mState.currentSnapshot()->transform,
+ mState.getRenderTargetClipBounds(),
+ refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
}
void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) {
if (floatCount < 4) return;
floatCount &= ~0x3; // round down to nearest four
- Rect unmappedBounds(points[0], points[1], points[0], points[1]);
- for (int i = 2; i < floatCount; i += 2) {
- unmappedBounds.expandToCover(points[i], points[i + 1]);
- }
-
- // since anything AA stroke with less than 1.0 pixel width is drawn with an alpha-reduced
- // 1.0 stroke, treat 1.0 as minimum.
- unmappedBounds.outset(std::max(paint.getStrokeWidth(), 1.0f) * 0.5f);
-
addOp(new (alloc()) LinesOp(
- unmappedBounds,
+ calcBoundsOfPoints(points, floatCount),
*mState.currentSnapshot()->transform,
mState.getRenderTargetClipBounds(),
refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
@@ -330,20 +336,80 @@
}
void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint& paint) {
- LOG_ALWAYS_FATAL("TODO!");
+ addOp(new (alloc()) RoundRectOp(
+ Rect(left, top, right, bottom),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(&paint), rx, ry));
}
+
+void RecordingCanvas::drawRoundRect(
+ CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
+ CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
+ CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
+ CanvasPropertyPaint* paint) {
+ mDisplayList->ref(left);
+ mDisplayList->ref(top);
+ mDisplayList->ref(right);
+ mDisplayList->ref(bottom);
+ mDisplayList->ref(rx);
+ mDisplayList->ref(ry);
+ mDisplayList->ref(paint);
+ refBitmapsInShader(paint->value.getShader());
+ addOp(new (alloc()) RoundRectPropsOp(
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ &paint->value,
+ &left->value, &top->value, &right->value, &bottom->value,
+ &rx->value, &ry->value));
+}
+
void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
- LOG_ALWAYS_FATAL("TODO!");
+ // TODO: move to Canvas.h
+ if (radius <= 0) return;
+ drawOval(x - radius, y - radius, x + radius, y + radius, paint);
}
+
+void RecordingCanvas::drawCircle(
+ CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+ CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
+ mDisplayList->ref(x);
+ mDisplayList->ref(y);
+ mDisplayList->ref(radius);
+ mDisplayList->ref(paint);
+ refBitmapsInShader(paint->value.getShader());
+ addOp(new (alloc()) CirclePropsOp(
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ &paint->value,
+ &x->value, &y->value, &radius->value));
+}
+
+
void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
- LOG_ALWAYS_FATAL("TODO!");
+ addOp(new (alloc()) OvalOp(
+ Rect(left, top, right, bottom),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(&paint)));
}
+
void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
- LOG_ALWAYS_FATAL("TODO!");
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+ addOp(new (alloc()) ArcOp(
+ Rect(left, top, right, bottom),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(&paint),
+ startAngle, sweepAngle, useCenter));
}
+
void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
- LOG_ALWAYS_FATAL("TODO!");
+ addOp(new (alloc()) PathOp(
+ Rect(path.getBounds()),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(&paint), refPath(&path)));
}
// Bitmap-based
@@ -375,6 +441,7 @@
restore();
}
}
+
void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint) {
@@ -389,17 +456,35 @@
drawBitmap(&bitmap, paint);
restore();
} else {
- LOG_ALWAYS_FATAL("TODO!");
+ addOp(new (alloc()) BitmapRectOp(
+ Rect(dstLeft, dstTop, dstRight, dstBottom),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(paint), refBitmap(bitmap),
+ Rect(srcLeft, srcTop, srcRight, srcBottom)));
}
}
+
void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) {
- LOG_ALWAYS_FATAL("TODO!");
+ int vertexCount = (meshWidth + 1) * (meshHeight + 1);
+ addOp(new (alloc()) BitmapMeshOp(
+ calcBoundsOfPoints(vertices, vertexCount * 2),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight,
+ refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex
+ refBuffer<int>(colors, vertexCount))); // 1 color per vertex
}
-void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+
+void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& patch,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) {
- LOG_ALWAYS_FATAL("TODO!");
+ addOp(new (alloc()) PatchOp(
+ Rect(dstLeft, dstTop, dstRight, dstBottom),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(paint), refBitmap(bitmap), refPatch(&patch)));
}
// Text
@@ -421,7 +506,6 @@
void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
float hOffset, float vOffset, const SkPaint& paint) {
- // NOTE: can't use refPaint() directly, since it forces left alignment
LOG_ALWAYS_FATAL("TODO!");
}
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 6d0e9e0..6fbaa8a 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -69,6 +69,17 @@
virtual GLuint getTargetFbo() const override { return -1; }
// ----------------------------------------------------------------------------
+// HWUI Canvas draw operations
+// ----------------------------------------------------------------------------
+
+ void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
+ CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
+ CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
+ CanvasPropertyPaint* paint);
+ void drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+ CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint);
+
+// ----------------------------------------------------------------------------
// android/graphics/Canvas interface
// ----------------------------------------------------------------------------
virtual SkCanvas* asSkCanvas() override;
@@ -140,13 +151,13 @@
float points[2] = { x, y };
drawPoints(points, 2, paint);
}
- virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawPoints(const float* points, int floatCount, const SkPaint& paint) override;
virtual void drawLine(float startX, float startY, float stopX, float stopY,
const SkPaint& paint) override {
float points[4] = { startX, startY, stopX, stopY };
drawLines(points, 4, paint);
}
- virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) override;
virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
virtual void drawRoundRect(float left, float top, float right, float bottom,
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index 4bcd96d..1c544b9 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -120,16 +120,16 @@
class AutoTexture {
public:
- AutoTexture(const Texture* texture): mTexture(texture) { }
+ AutoTexture(const Texture* texture)
+ : texture(texture) {}
~AutoTexture() {
- if (mTexture && mTexture->cleanup) {
- mTexture->deleteTexture();
- delete mTexture;
+ if (texture && texture->cleanup) {
+ texture->deleteTexture();
+ delete texture;
}
}
-private:
- const Texture* mTexture;
+ const Texture *const texture;
}; // class AutoTexture
}; // namespace uirenderer
diff --git a/libs/hwui/tests/common/scenes/OpPropAnimation.cpp b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp
new file mode 100644
index 0000000..5dfb2b4
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "TestSceneBase.h"
+#include "utils/Color.h"
+
+class OpPropAnimation;
+
+static TestScene::Registrar _Shapes(TestScene::Info{
+ "opprops",
+ "A minimal demonstration of CanvasProperty drawing operations.",
+ TestScene::simpleCreateScene<OpPropAnimation>
+});
+
+class OpPropAnimation : public TestScene {
+public:
+ sp<CanvasPropertyPaint> mPaint = new CanvasPropertyPaint(SkPaint());
+
+ sp<CanvasPropertyPrimitive> mRoundRectLeft = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mRoundRectTop = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mRoundRectRight = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mRoundRectBottom = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mRoundRectRx = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mRoundRectRy = new CanvasPropertyPrimitive(0);
+
+ sp<CanvasPropertyPrimitive> mCircleX = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mCircleY = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mCircleRadius = new CanvasPropertyPrimitive(0);
+
+ sp<RenderNode> content;
+ void createContent(int width, int height, TestCanvas& canvas) override {
+ content = TestUtils::createNode(0, 0, width, height,
+ [this, width, height](RenderProperties& props, TestCanvas& canvas) {
+ mPaint->value.setAntiAlias(true);
+ mPaint->value.setColor(Color::Blue_500);
+
+ mRoundRectRight->value = width / 2;
+ mRoundRectBottom->value = height / 2;
+
+ mCircleX->value = width * 0.75;
+ mCircleY->value = height * 0.75;
+
+ canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawRoundRect(mRoundRectLeft.get(), mRoundRectTop.get(),
+ mRoundRectRight.get(), mRoundRectBottom.get(),
+ mRoundRectRx.get(), mRoundRectRy.get(), mPaint.get());
+ canvas.drawCircle(mCircleX.get(), mCircleY.get(), mCircleRadius.get(), mPaint.get());
+ });
+ canvas.drawRenderNode(content.get());
+ }
+
+ void doFrame(int frameNr) override {
+ float value = (abs((frameNr % 200) - 100)) / 100.0f;
+ mRoundRectRx->value = dp(10) + value * dp(40);
+ mRoundRectRy->value = dp(10) + value * dp(80);
+ mCircleRadius->value = value * dp(200);
+ content->setPropertyFieldsDirty(RenderNode::GENERIC);
+ }
+};
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
new file mode 100644
index 0000000..0cba344
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#include "TestSceneBase.h"
+#include "utils/Color.h"
+
+#include <cstdio>
+
+class ShapeAnimation;
+
+static TestScene::Registrar _Shapes(TestScene::Info{
+ "shapes",
+ "A grid of shape drawing test cases.",
+ TestScene::simpleCreateScene<ShapeAnimation>
+});
+
+class ShapeAnimation : public TestScene {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, TestCanvas& canvas) override {
+ card = TestUtils::createNode(0, 0, width, height,
+ [width](RenderProperties& props, TestCanvas& canvas) {
+ std::function<void(TestCanvas&, float, const SkPaint&)> ops[] = {
+ [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ canvas.drawArc(0, 0, size, size, 50, 189, true, paint);
+ },
+ [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ canvas.drawOval(0, 0, size, size, paint);
+ },
+ [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ SkPath diamondPath;
+ diamondPath.moveTo(size / 2, 0);
+ diamondPath.lineTo(size, size / 2);
+ diamondPath.lineTo(size / 2, size);
+ diamondPath.lineTo(0, size / 2);
+ diamondPath.close();
+ canvas.drawPath(diamondPath, paint);
+ },
+ [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ float data[] = {0, 0, size, size, 0, size, size, 0 };
+ canvas.drawLines(data, sizeof(data) / sizeof(float), paint);
+ },
+ [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ float data[] = {0, 0, size, size, 0, size, size, 0 };
+ canvas.drawPoints(data, sizeof(data) / sizeof(float), paint);
+ },
+ [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ canvas.drawRect(0, 0, size, size, paint);
+ },
+ [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ float rad = size / 4;
+ canvas.drawRoundRect(0, 0, size, size, rad, rad, paint);
+ }
+ };
+ float cellSpace = dp(4);
+ float cellSize = floorf(width / 7 - cellSpace);
+
+ // each combination of strokeWidth + style gets a column
+ int outerCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ SkPaint::Style styles[] = {
+ SkPaint::kStroke_Style, SkPaint::kFill_Style, SkPaint::kStrokeAndFill_Style };
+ for (auto style : styles) {
+ paint.setStyle(style);
+ for (auto strokeWidth : { 0.0f, 0.5f, 8.0f }) {
+ paint.setStrokeWidth(strokeWidth);
+ // fill column with each op
+ int middleCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ for (auto op : ops) {
+ int innerCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ canvas.clipRect(0, 0, cellSize, cellSize, SkRegion::kIntersect_Op);
+ canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode);
+ op(canvas, cellSize, paint);
+ canvas.restoreToCount(innerCount);
+ canvas.translate(cellSize + cellSpace, 0);
+ }
+ canvas.restoreToCount(middleCount);
+ canvas.translate(0, cellSize + cellSpace);
+ }
+ }
+ canvas.restoreToCount(outerCount);
+ });
+ canvas.drawColor(Color::Grey_500, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawRenderNode(card.get());
+ }
+
+ void doFrame(int frameNr) override {
+ card->mutateStagingProperties().setTranslationY(frameNr % 150);
+ card->setPropertyFieldsDirty(RenderNode::Y);
+ }
+};
diff --git a/libs/hwui/tests/unit/BakedOpStateTests.cpp b/libs/hwui/tests/unit/BakedOpStateTests.cpp
index de14abf..8321ff9 100644
--- a/libs/hwui/tests/unit/BakedOpStateTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpStateTests.cpp
@@ -23,41 +23,130 @@
namespace android {
namespace uirenderer {
-TEST(ResolvedRenderState, resolution) {
- Matrix4 identity;
- identity.loadIdentity();
-
+TEST(ResolvedRenderState, construct) {
Matrix4 translate10x20;
translate10x20.loadTranslate(10, 20, 0);
SkPaint paint;
- RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, Rect(0, 0, 100, 200), &paint);
+ RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, Rect(100, 200), &paint);
{
// recorded with transform, no parent transform
- auto parentSnapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
- ResolvedRenderState state(*parentSnapshot, recordedOp);
+ auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+ ResolvedRenderState state(*parentSnapshot, recordedOp, false);
EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20);
- EXPECT_EQ(state.clipRect, Rect(0, 0, 100, 200));
- EXPECT_EQ(state.clippedBounds, Rect(40, 60, 100, 200)); // translated and also clipped
+ EXPECT_EQ(Rect(0, 0, 100, 200), state.clipRect);
+ EXPECT_EQ(Rect(40, 60, 100, 200), state.clippedBounds); // translated and also clipped
+ EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
}
{
// recorded with transform and parent transform
- auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(0, 0, 100, 200));
- ResolvedRenderState state(*parentSnapshot, recordedOp);
+ auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
+ ResolvedRenderState state(*parentSnapshot, recordedOp, false);
Matrix4 expectedTranslate;
expectedTranslate.loadTranslate(20, 40, 0);
- EXPECT_MATRIX_APPROX_EQ(state.transform, expectedTranslate);
+ EXPECT_MATRIX_APPROX_EQ(expectedTranslate, state.transform);
// intersection of parent & transformed child clip
- EXPECT_EQ(state.clipRect, Rect(10, 20, 100, 200));
+ EXPECT_EQ(Rect(10, 20, 100, 200), state.clipRect);
// translated and also clipped
- EXPECT_EQ(state.clippedBounds, Rect(50, 80, 100, 200));
+ EXPECT_EQ(Rect(50, 80, 100, 200), state.clippedBounds);
+ EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
}
}
-TEST(BakedOpState, constructAndReject) {
+const float HAIRLINE = 0.0f;
+
+// Note: bounds will be conservative, but not precise for non-hairline
+// - use approx bounds checks for these
+const float SEMI_HAIRLINE = 0.3f;
+
+struct StrokeTestCase {
+ float scale;
+ float strokeWidth;
+ const std::function<void(const ResolvedRenderState&)> validator;
+};
+
+const static StrokeTestCase sStrokeTestCases[] = {
+ {
+ 1, HAIRLINE, [](const ResolvedRenderState& state) {
+ EXPECT_EQ(Rect(49.5f, 49.5f, 150.5f, 150.5f), state.clippedBounds);
+ }
+ },
+ {
+ 1, SEMI_HAIRLINE, [](const ResolvedRenderState& state) {
+ EXPECT_TRUE(state.clippedBounds.contains(49.5f, 49.5f, 150.5f, 150.5f));
+ EXPECT_TRUE(Rect(49, 49, 151, 151).contains(state.clippedBounds));
+ }
+ },
+ {
+ 1, 20, [](const ResolvedRenderState& state) {
+ EXPECT_EQ(Rect(40, 40, 160, 160), state.clippedBounds);
+ }
+ },
+
+ // 3x3 scale:
+ {
+ 3, HAIRLINE, [](const ResolvedRenderState& state) {
+ EXPECT_EQ(Rect(149.5f, 149.5f, 200, 200), state.clippedBounds);
+ EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
+ }
+ },
+ {
+ 3, SEMI_HAIRLINE, [](const ResolvedRenderState& state) {
+ EXPECT_TRUE(state.clippedBounds.contains(149.5f, 149.5f, 200, 200));
+ EXPECT_TRUE(Rect(149, 149, 200, 200).contains(state.clippedBounds));
+ }
+ },
+ {
+ 3, 20, [](const ResolvedRenderState& state) {
+ EXPECT_TRUE(state.clippedBounds.contains(120, 120, 200, 200));
+ EXPECT_TRUE(Rect(119, 119, 200, 200).contains(state.clippedBounds));
+ }
+ },
+
+ // 0.5f x 0.5f scale
+ {
+ 0.5f, HAIRLINE, [](const ResolvedRenderState& state) {
+ EXPECT_EQ(Rect(24.5f, 24.5f, 75.5f, 75.5f), state.clippedBounds);
+ }
+ },
+ {
+ 0.5f, SEMI_HAIRLINE, [](const ResolvedRenderState& state) {
+ EXPECT_TRUE(state.clippedBounds.contains(24.5f, 24.5f, 75.5f, 75.5f));
+ EXPECT_TRUE(Rect(24, 24, 76, 76).contains(state.clippedBounds));
+ }
+ },
+ {
+ 0.5f, 20, [](const ResolvedRenderState& state) {
+ EXPECT_TRUE(state.clippedBounds.contains(19.5f, 19.5f, 80.5f, 80.5f));
+ EXPECT_TRUE(Rect(19, 19, 81, 81).contains(state.clippedBounds));
+ }
+ }
+};
+
+TEST(ResolvedRenderState, construct_expandForStroke) {
+ // Loop over table of test cases and verify different combinations of stroke width and transform
+ for (auto&& testCase : sStrokeTestCases) {
+ SkPaint strokedPaint;
+ strokedPaint.setAntiAlias(true);
+ strokedPaint.setStyle(SkPaint::kStroke_Style);
+ strokedPaint.setStrokeWidth(testCase.strokeWidth);
+
+ RectOp recordedOp(Rect(50, 50, 150, 150),
+ Matrix4::identity(), Rect(200, 200), &strokedPaint);
+
+ Matrix4 snapshotMatrix;
+ snapshotMatrix.loadScale(testCase.scale, testCase.scale, 1);
+ auto parentSnapshot = TestUtils::makeSnapshot(snapshotMatrix, Rect(200, 200));
+
+ ResolvedRenderState state(*parentSnapshot, recordedOp, true);
+ testCase.validator(state);
+ }
+}
+
+TEST(BakedOpState, tryConstruct) {
LinearAllocator allocator;
Matrix4 translate100x0;
@@ -65,41 +154,85 @@
SkPaint paint;
{
- RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, Rect(0, 0, 100, 200), &paint);
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
- BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp);
+ RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, Rect(100, 200), &paint);
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+ BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp);
- EXPECT_EQ(bakedOp, nullptr); // rejected by clip, so not constructed
- EXPECT_LE(allocator.usedSize(), 8u); // no significant allocation space used for rejected op
+ EXPECT_EQ(nullptr, bakedState); // rejected by clip, so not constructed
+ EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
}
{
- RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), Rect(0, 0, 100, 200), &paint);
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
- BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, successOp);
+ RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), Rect(100, 200), &paint);
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+ BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, successOp);
- EXPECT_NE(bakedOp, nullptr); // NOT rejected by clip, so will be constructed
- EXPECT_GT(allocator.usedSize(), 64u); // relatively large alloc for non-rejected op
+ EXPECT_NE(nullptr, bakedState); // NOT rejected by clip, so will be constructed
+ EXPECT_LE(64u, allocator.usedSize()); // relatively large alloc for non-rejected op
}
}
-TEST(BakedOpState, oplessConstructAndReject) {
+TEST(BakedOpState, tryShadowOpConstruct) {
LinearAllocator allocator;
{
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 0, 0)); // empty
- BakedOpState* bakedOp = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
+ BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
- EXPECT_EQ(bakedOp, nullptr); // rejected by clip, so not constructed
- EXPECT_LE(allocator.usedSize(), 8u); // no significant allocation space used for rejected op
+ EXPECT_EQ(nullptr, bakedState); // rejected by clip, so not constructed
+ EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
}
{
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
- BakedOpState* bakedOp = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+ BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
- EXPECT_NE(bakedOp, nullptr); // NOT rejected by clip, so will be constructed
- EXPECT_GT(allocator.usedSize(), 64u); // relatively large alloc for non-rejected op
- EXPECT_EQ((ShadowOp*)0x1234, bakedOp->op);
+ ASSERT_NE(nullptr, bakedState); // NOT rejected by clip, so will be constructed
+ EXPECT_LE(64u, allocator.usedSize()); // relatively large alloc for non-rejected op
}
}
+TEST(BakedOpState, tryStrokeableOpConstruct) {
+ LinearAllocator allocator;
+ {
+ // check regular rejection
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStrokeAndFill_Style);
+ paint.setStrokeWidth(0.0f);
+ RectOp rejectOp(Rect(0, 0, 100, 200), Matrix4::identity(), Rect(100, 200), &paint);
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
+ auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
+ BakedOpState::StrokeBehavior::StyleDefined);
+
+ EXPECT_EQ(nullptr, bakedState);
+ EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
+ }
+ {
+ // check simple unscaled expansion
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStrokeAndFill_Style);
+ paint.setStrokeWidth(10.0f);
+ RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), Rect(200, 200), &paint);
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
+ auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
+ BakedOpState::StrokeBehavior::StyleDefined);
+
+ ASSERT_NE(nullptr, bakedState);
+ EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
+ EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
+ }
+ {
+ // check simple unscaled expansion, and fill style with stroke forced
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setStrokeWidth(10.0f);
+ RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), Rect(200, 200), &paint);
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
+ auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
+ BakedOpState::StrokeBehavior::Forced);
+
+ ASSERT_NE(nullptr, bakedState);
+ EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
+ EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
+ }
}
-}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp
index d6192df..c4d305e 100644
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ b/libs/hwui/tests/unit/ClipAreaTests.cpp
@@ -92,12 +92,10 @@
TEST(ClipArea, paths) {
ClipArea area(createClipArea());
- Matrix4 transform;
- transform.loadIdentity();
SkPath path;
SkScalar r = 100;
path.addCircle(r, r, r);
- area.clipPathWithTransform(path, &transform, SkRegion::kIntersect_Op);
+ area.clipPathWithTransform(path, &Matrix4::identity(), SkRegion::kIntersect_Op);
EXPECT_FALSE(area.isEmpty());
EXPECT_FALSE(area.isSimple());
EXPECT_FALSE(area.isRectangleList());
@@ -116,11 +114,10 @@
ClipArea area(createClipArea());
area.setClip(0, 0, 100, 100);
- Matrix4 transform;
- transform.loadIdentity();
Rect expected(-50, -50, 50, 50);
- area.clipRectWithTransform(expected, &transform, SkRegion::kReplace_Op);
+ area.clipRectWithTransform(expected, &Matrix4::identity(), SkRegion::kReplace_Op);
EXPECT_EQ(expected, area.getClipRect());
}
+
}
}
diff --git a/libs/hwui/tests/unit/DamageAccumulatorTests.cpp b/libs/hwui/tests/unit/DamageAccumulatorTests.cpp
index 29354a7..7700138 100644
--- a/libs/hwui/tests/unit/DamageAccumulatorTests.cpp
+++ b/libs/hwui/tests/unit/DamageAccumulatorTests.cpp
@@ -31,13 +31,11 @@
// as the output.
TEST(DamageAccumulator, identity) {
DamageAccumulator da;
- Matrix4 identity;
SkRect curDirty;
- identity.loadIdentity();
- da.pushTransform(&identity);
+ da.pushTransform(&Matrix4::identity());
da.dirty(50, 50, 100, 100);
{
- da.pushTransform(&identity);
+ da.pushTransform(&Matrix4::identity());
da.peekAtDirty(&curDirty);
ASSERT_EQ(SkRect(), curDirty);
da.popTransform();
@@ -68,15 +66,13 @@
// Test that dirty rectangles are being unioned across "siblings
TEST(DamageAccumulator, union) {
DamageAccumulator da;
- Matrix4 identity;
SkRect curDirty;
- identity.loadIdentity();
- da.pushTransform(&identity);
+ da.pushTransform(&Matrix4::identity());
{
- da.pushTransform(&identity);
+ da.pushTransform(&Matrix4::identity());
da.dirty(50, 50, 100, 100);
da.popTransform();
- da.pushTransform(&identity);
+ da.pushTransform(&Matrix4::identity());
da.dirty(150, 50, 200, 125);
da.popTransform();
}
diff --git a/libs/hwui/tests/unit/OpReordererTests.cpp b/libs/hwui/tests/unit/OpReordererTests.cpp
index 068e832..5eac498 100644
--- a/libs/hwui/tests/unit/OpReordererTests.cpp
+++ b/libs/hwui/tests/unit/OpReordererTests.cpp
@@ -144,6 +144,32 @@
EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
}
+TEST(OpReorderer, simpleStroke) {
+ class SimpleStrokeTestRenderer : public TestRendererBase {
+ public:
+ void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ // even though initial bounds are empty...
+ EXPECT_TRUE(op.unmappedBounds.isEmpty())
+ << "initial bounds should be empty, since they're unstroked";
+ EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
+ << "final bounds should account for stroke";
+ }
+ };
+
+ auto node = TestUtils::createNode(0, 0, 100, 200,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ SkPaint strokedPaint;
+ strokedPaint.setStrokeWidth(10);
+ canvas.drawPoint(50, 50, strokedPaint);
+ });
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ createSyncedNodeList(node), sLightCenter);
+ SimpleStrokeTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex());
+}
+
TEST(OpReorderer, simpleRejection) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 2449ce8..ba9d185 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -44,7 +44,7 @@
TEST(RecordingCanvas, drawLines) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
SkPaint paint;
- paint.setStrokeWidth(20);
+ paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time
float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line
canvas.drawLines(&points[0], 7, paint);
});
@@ -54,8 +54,8 @@
ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
<< "float count must be rounded down to closest multiple of 4";
- EXPECT_EQ(Rect(-10, -10, 30, 20), op->unmappedBounds)
- << "unmapped bounds must be size of line, outset by 1/2 stroke width";
+ EXPECT_EQ(Rect(0, 0, 20, 10), op->unmappedBounds)
+ << "unmapped bounds must be size of line, and not outset for stroke width";
}
TEST(RecordingCanvas, drawRect) {
diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h
index 6895f07..06bcdcd 100644
--- a/libs/hwui/utils/RingBuffer.h
+++ b/libs/hwui/utils/RingBuffer.h
@@ -32,7 +32,7 @@
~RingBuffer() {}
constexpr size_t capacity() const { return SIZE; }
- size_t size() { return mCount; }
+ size_t size() const { return mCount; }
T& next() {
mHead = (mHead + 1) % SIZE;
@@ -54,6 +54,10 @@
return mBuffer[(mHead + index + 1) % mCount];
}
+ const T& operator[](size_t index) const {
+ return mBuffer[(mHead + index + 1) % mCount];
+ }
+
void clear() {
mCount = 0;
mHead = -1;
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index 84aae75..05b4a72 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -180,6 +180,10 @@
TestWindowContext::TestWindowContext() :
mData (nullptr) { }
+TestWindowContext::~TestWindowContext() {
+ delete mData;
+}
+
void TestWindowContext::initialize(int width, int height) {
mData = new TestWindowData(SkISize::Make(width, height));
}
diff --git a/libs/hwui/utils/TestWindowContext.h b/libs/hwui/utils/TestWindowContext.h
index 445a11b..48ec952 100644
--- a/libs/hwui/utils/TestWindowContext.h
+++ b/libs/hwui/utils/TestWindowContext.h
@@ -35,6 +35,7 @@
public:
TestWindowContext();
+ ~TestWindowContext();
/// We need to know the size of the window.
void initialize(int width, int height);
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 045216b..3e75759 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -141,6 +141,16 @@
* {@link android.hardware.camera2.CameraDevice CameraDevice}.
* </td>
* </tr>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#RAW_PRIVATE RAW_PRIVATE}</td>
+ * <td>1</td>
+ * <td>A single plane of raw sensor image data of private layout.
+ * The details of the layout is implementation specific. Row stride and
+ * pixel stride are undefined for this format. Calling {@link Plane#getRowStride()}
+ * or {@link Plane#getPixelStride()} on RAW_PRIVATE image will cause
+ * UnSupportedOperationException being thrown.
+ * </td>
+ * </tr>
* </table>
*
* @see android.graphics.ImageFormat
@@ -341,7 +351,13 @@
* <p>The row stride for this color plane, in bytes.</p>
*
* <p>This is the distance between the start of two consecutive rows of
- * pixels in the image. The row stride is always greater than 0.</p>
+ * pixels in the image. Note that row stried is undefined for some formats
+ * such as
+ * {@link android.graphics.ImageFormat#RAW_PRIVATE RAW_PRIVATE},
+ * and calling getRowStride on images of these formats will
+ * cause an UnsupportedOperationException being thrown.
+ * For formats where row stride is well defined, the row stride
+ * is always greater than 0.</p>
*/
public abstract int getRowStride();
/**
@@ -350,7 +366,12 @@
* <p>This is the distance between two consecutive pixel values in a row
* of pixels. It may be larger than the size of a single pixel to
* account for interleaved image data or padded formats.
- * The pixel stride is always greater than 0.</p>
+ * Note that pixel stride is undefined for some formats such as
+ * {@link android.graphics.ImageFormat#RAW_PRIVATE RAW_PRIVATE},
+ * and calling getPixelStride on images of these formats will
+ * cause an UnsupportedOperationException being thrown.
+ * For formats where pixel stride is well defined, the pixel stride
+ * is always greater than 0.</p>
*/
public abstract int getPixelStride();
/**
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 66a174c..397ab15 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -570,7 +570,7 @@
nativeDetachImage(image);
si.setDetached(true);
- }
+ }
private boolean isImageOwnedbyMe(Image image) {
if (!(image instanceof SurfaceImage)) {
@@ -675,6 +675,7 @@
switch(getFormat()) {
case ImageFormat.JPEG:
case ImageFormat.DEPTH_POINT_CLOUD:
+ case ImageFormat.RAW_PRIVATE:
width = ImageReader.this.getWidth();
break;
default:
@@ -690,6 +691,7 @@
switch(getFormat()) {
case ImageFormat.JPEG:
case ImageFormat.DEPTH_POINT_CLOUD:
+ case ImageFormat.RAW_PRIVATE:
height = ImageReader.this.getHeight();
break;
default:
@@ -791,12 +793,20 @@
@Override
public int getPixelStride() {
SurfaceImage.this.throwISEIfImageIsInvalid();
+ if (ImageReader.this.mFormat == ImageFormat.RAW_PRIVATE) {
+ throw new UnsupportedOperationException(
+ "getPixelStride is not supported for RAW_PRIVATE plane");
+ }
return mPixelStride;
}
@Override
public int getRowStride() {
SurfaceImage.this.throwISEIfImageIsInvalid();
+ if (ImageReader.this.mFormat == ImageFormat.RAW_PRIVATE) {
+ throw new UnsupportedOperationException(
+ "getRowStride is not supported for RAW_PRIVATE plane");
+ }
return mRowStride;
}
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index eefd69c..abf6b20 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -57,6 +57,7 @@
case ImageFormat.Y8:
case ImageFormat.Y16:
case ImageFormat.RAW_SENSOR:
+ case ImageFormat.RAW_PRIVATE:
case ImageFormat.RAW10:
case ImageFormat.RAW12:
case ImageFormat.DEPTH16:
@@ -98,6 +99,10 @@
dst.getFormat() == ImageFormat.PRIVATE) {
throw new IllegalArgumentException("PRIVATE format images are not copyable");
}
+ if (src.getFormat() == ImageFormat.RAW_PRIVATE) {
+ throw new IllegalArgumentException(
+ "Copy of RAW_OPAQUE format has not been implemented");
+ }
if (!(dst.getOwner() instanceof ImageWriter)) {
throw new IllegalArgumentException("Destination image is not from ImageWriter. Only"
+ " the images from ImageWriter are writable");
@@ -193,7 +198,8 @@
case ImageFormat.YV12:
case ImageFormat.YUV_420_888:
case ImageFormat.NV21:
- case ImageFormat.PRIVATE: // A really rough estimate because the real size is unknown.
+ case ImageFormat.RAW12:
+ case ImageFormat.PRIVATE: // A rough estimate because the real size is unknown.
estimatedBytePerPixel = 1.5;
break;
case ImageFormat.NV16:
@@ -201,6 +207,7 @@
case ImageFormat.YUY2:
case ImageFormat.Y16:
case ImageFormat.RAW_SENSOR:
+ case ImageFormat.RAW_PRIVATE: // round estimate, real size is unknown
case ImageFormat.DEPTH16:
estimatedBytePerPixel = 2.0;
break;
@@ -245,6 +252,7 @@
case ImageFormat.Y16:
case ImageFormat.RAW_SENSOR:
case ImageFormat.RAW10:
+ case ImageFormat.RAW12:
return new Size(image.getWidth(), image.getHeight());
case ImageFormat.PRIVATE:
return new Size(0, 0);
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index ab61e2b..db0c5bb 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -906,18 +906,6 @@
throws DeniedByServerException;
/**
- * Remove provisioning from a device. Only system apps may unprovision a
- * device. Note that removing provisioning will invalidate any keys saved
- * for offline use (KEY_TYPE_OFFLINE), which may render downloaded content
- * unplayable until new licenses are acquired. Since provisioning is global
- * to the device, license invalidation will apply to all content downloaded
- * by any app, so appropriate warnings should be given to the user.
- * @hide
- */
- @SystemApi
- public native void unprovisionDevice();
-
- /**
* A means of enforcing limits on the number of concurrent streams per subscriber
* across devices is provided via SecureStop. This is achieved by securely
* monitoring the lifetime of sessions.
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index a046512..bcc2b406 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.DrawableRes;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.ActivityThread;
import android.content.BroadcastReceiver;
@@ -41,6 +42,8 @@
import android.util.Log;
import android.view.Display;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -201,6 +204,7 @@
info.mDescription = sStatic.mResources.getText(
com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
+ info.mDeviceType = RouteInfo.DEVICE_TYPE_BLUETOOTH;
sStatic.mBluetoothA2dpRoute = info;
addRouteStatic(sStatic.mBluetoothA2dpRoute);
} else {
@@ -480,6 +484,7 @@
route.mName = globalRoute.name;
route.mDescription = globalRoute.description;
route.mSupportedTypes = globalRoute.supportedTypes;
+ route.mDeviceType = globalRoute.deviceType;
route.mEnabled = globalRoute.enabled;
route.setRealStatusCode(globalRoute.statusCode);
route.mPlaybackType = globalRoute.playbackType;
@@ -1411,6 +1416,7 @@
newRoute.mDescription = sStatic.mResources.getText(
com.android.internal.R.string.wireless_display_route_description);
newRoute.updatePresentationDisplay();
+ newRoute.mDeviceType = RouteInfo.DEVICE_TYPE_TV;
return newRoute;
}
@@ -1470,6 +1476,7 @@
CharSequence mDescription;
private CharSequence mStatus;
int mSupportedTypes;
+ int mDeviceType;
RouteGroup mGroup;
final RouteCategory mCategory;
Drawable mIcon;
@@ -1502,6 +1509,42 @@
/** @hide */ public static final int STATUS_IN_USE = 5;
/** @hide */ public static final int STATUS_CONNECTED = 6;
+ /** @hide */
+ @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeviceType {}
+
+ /**
+ * The default receiver device type of the route indicating the type is unknown.
+ *
+ * @see #getDeviceType
+ */
+ public static final int DEVICE_TYPE_UNKNOWN = 0;
+
+ /**
+ * A receiver device type of the route indicating the presentation of the media is happening
+ * on a TV.
+ *
+ * @see #getDeviceType
+ */
+ public static final int DEVICE_TYPE_TV = 1;
+
+ /**
+ * A receiver device type of the route indicating the presentation of the media is happening
+ * on a speaker.
+ *
+ * @see #getDeviceType
+ */
+ public static final int DEVICE_TYPE_SPEAKER = 2;
+
+ /**
+ * A receiver device type of the route indicating the presentation of the media is happening
+ * on a bluetooth device such as a bluetooth speaker.
+ *
+ * @see #getDeviceType
+ */
+ public static final int DEVICE_TYPE_BLUETOOTH = 3;
+
private Object mTag;
/**
@@ -1533,6 +1576,7 @@
RouteInfo(RouteCategory category) {
mCategory = category;
+ mDeviceType = DEVICE_TYPE_UNKNOWN;
}
/**
@@ -1670,6 +1714,18 @@
return mSupportedTypes;
}
+ /**
+ * Gets the type of the receiver device associated with this route.
+ *
+ * @return The type of the receiver device associated with this route:
+ * {@link #DEVICE_TYPE_BLUETOOTH}, {@link #DEVICE_TYPE_TV}, {@link #DEVICE_TYPE_SPEAKER},
+ * or {@link #DEVICE_TYPE_UNKNOWN}.
+ */
+ @DeviceType
+ public int getDeviceType() {
+ return mDeviceType;
+ }
+
/** @hide */
public boolean matchesTypes(int types) {
return (mSupportedTypes & types) != 0;
diff --git a/media/java/android/media/MediaRouterClientState.java b/media/java/android/media/MediaRouterClientState.java
index 54b8276..34e18f6 100644
--- a/media/java/android/media/MediaRouterClientState.java
+++ b/media/java/android/media/MediaRouterClientState.java
@@ -104,6 +104,7 @@
public int volumeMax;
public int volumeHandling;
public int presentationDisplayId;
+ public @MediaRouter.RouteInfo.DeviceType int deviceType;
public RouteInfo(String id) {
this.id = id;
@@ -113,6 +114,7 @@
playbackStream = -1;
volumeHandling = MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
presentationDisplayId = -1;
+ deviceType = MediaRouter.RouteInfo.DEVICE_TYPE_UNKNOWN;
}
public RouteInfo(RouteInfo other) {
@@ -128,6 +130,7 @@
volumeMax = other.volumeMax;
volumeHandling = other.volumeHandling;
presentationDisplayId = other.presentationDisplayId;
+ deviceType = other.deviceType;
}
RouteInfo(Parcel in) {
@@ -143,6 +146,7 @@
volumeMax = in.readInt();
volumeHandling = in.readInt();
presentationDisplayId = in.readInt();
+ deviceType = in.readInt();
}
@Override
@@ -164,6 +168,7 @@
dest.writeInt(volumeMax);
dest.writeInt(volumeHandling);
dest.writeInt(presentationDisplayId);
+ dest.writeInt(deviceType);
}
@Override
@@ -180,6 +185,7 @@
+ ", volumeMax=" + volumeMax
+ ", volumeHandling=" + volumeHandling
+ ", presentationDisplayId=" + presentationDisplayId
+ + ", deviceType=" + deviceType
+ " }";
}
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 9a53186..1c043e0 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -101,6 +101,8 @@
void setOpaqueConsumer(const sp<BufferItemConsumer>& consumer) { mOpaqueConsumer = consumer; }
BufferItemConsumer* getOpaqueConsumer() { return mOpaqueConsumer.get(); }
// This is the only opaque format exposed in the ImageFormat public API.
+ // Note that we do support CPU access for HAL_PIXEL_FORMAT_RAW_OPAQUE
+ // (ImageFormat#RAW_PRIVATE) so it doesn't count as opaque here.
bool isOpaque() { return mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; }
void setProducer(const sp<IGraphicBufferProducer>& producer) { mProducer = producer; }
@@ -470,7 +472,8 @@
case HAL_PIXEL_FORMAT_BLOB:
// Used for JPEG data, height must be 1, width == size, single plane.
ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- ALOG_ASSERT(buffer->height == 1, "JPEG should has height value %d", buffer->height);
+ ALOG_ASSERT(buffer->height == 1,
+ "JPEG should has height value one but got %d", buffer->height);
pData = buffer->data;
dataSize = Image_getJpegSize(buffer, usingRGBAOverride);
@@ -482,6 +485,14 @@
pData = buffer->data;
dataSize = buffer->stride * buffer->height * bytesPerPixel;
break;
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ // Used for RAW_OPAQUE data, height must be 1, width == size, single plane.
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ ALOG_ASSERT(buffer->height == 1,
+ "RAW_PRIVATE should has height value one but got %d", buffer->height);
+ pData = buffer->data;
+ dataSize = buffer->width;
+ break;
case HAL_PIXEL_FORMAT_RAW10:
// Single plane 10bpp bayer data.
ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
@@ -593,6 +604,10 @@
ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
pixelStride = 3;
break;
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ pixelStride = 0; // RAW OPAQUE doesn't have pixel stride
+ break;
default:
jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
"Pixel format: 0x%x is unsupported", fmt);
@@ -669,6 +684,10 @@
ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
rowStride = buffer->stride * 3;
break;
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ rowStride = 0; // RAW OPAQUE doesn't have row stride
+ break;
default:
ALOGE("%s Pixel format: 0x%x is unsupported", __FUNCTION__, fmt);
jniThrowException(env, "java/lang/UnsupportedOperationException",
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 275de1ad..b8849c6 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -1048,22 +1048,6 @@
return certificateObj;
}
-static void android_media_MediaDrm_unprovisionDeviceNative(
- JNIEnv *env, jobject thiz) {
- sp<IDrm> drm = GetDrm(env, thiz);
-
- if (drm == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "MediaDrm obj is null");
- return;
- }
-
- status_t err = drm->unprovisionDevice();
-
- throwExceptionAsNecessary(env, err, "Failed to handle provision response");
- return;
-}
-
static jobject android_media_MediaDrm_getSecureStops(
JNIEnv *env, jobject thiz) {
sp<IDrm> drm = GetDrm(env, thiz);
@@ -1496,9 +1480,6 @@
{ "provideProvisionResponseNative", "([B)Landroid/media/MediaDrm$Certificate;",
(void *)android_media_MediaDrm_provideProvisionResponseNative },
- { "unprovisionDevice", "()V",
- (void *)android_media_MediaDrm_unprovisionDeviceNative },
-
{ "getSecureStops", "()Ljava/util/List;",
(void *)android_media_MediaDrm_getSecureStops },
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 57969ba..6f74203 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -129,7 +129,7 @@
int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_2);
- if (res != CameraBinderTestUtils.NO_ERROR && res != CameraBinderTestUtils.EOPNOTSUPP) {
+ if (res != CameraBinderTestUtils.NO_ERROR && res != -android.system.OsConstants.EOPNOTSUPP) {
fail("Camera service returned bad value when queried if it supports camera2 api: "
+ res + " for camera ID " + cameraId);
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
index 6be538a..5c4b23b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
@@ -2,6 +2,7 @@
package com.android.mediaframeworktest.integration;
import static org.junit.Assert.assertNotNull;
+import static android.system.OsConstants.*;
import android.content.Context;
import android.content.pm.FeatureInfo;
@@ -18,11 +19,10 @@
static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
protected static final int USE_CALLING_UID = -1;
- protected static final int BAD_VALUE = -22;
- protected static final int INVALID_OPERATION = -38;
- protected static final int ALREADY_EXISTS = -17;
+ protected static final int BAD_VALUE = -EINVAL;
+ protected static final int INVALID_OPERATION = -ENOSYS;
+ protected static final int ALREADY_EXISTS = -EEXIST;
public static final int NO_ERROR = 0;
- public static final int EOPNOTSUPP = -95;
private final Context mContext;
public CameraBinderTestUtils(Context context) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsBinderDecoratorTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsBinderDecoratorTest.java
index 727af78..33c6388 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsBinderDecoratorTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsBinderDecoratorTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.*;
import static android.hardware.camera2.utils.CameraBinderDecorator.*;
import static android.hardware.camera2.CameraAccessException.*;
+import static android.system.OsConstants.*;
import junit.framework.Assert;
@@ -78,9 +79,9 @@
when(mock.doSomethingAlreadyExists()).thenReturn(ALREADY_EXISTS);
when(mock.doSomethingBadValue()).thenReturn(BAD_VALUE);
when(mock.doSomethingDeadObject()).thenReturn(DEAD_OBJECT);
- when(mock.doSomethingBadPolicy()).thenReturn(EACCES);
- when(mock.doSomethingDeviceBusy()).thenReturn(EBUSY);
- when(mock.doSomethingNoSuchDevice()).thenReturn(ENODEV);
+ when(mock.doSomethingBadPolicy()).thenReturn(-EACCES);
+ when(mock.doSomethingDeviceBusy()).thenReturn(-EBUSY);
+ when(mock.doSomethingNoSuchDevice()).thenReturn(-ENODEV);
when(mock.doSomethingUnknownErrorCode()).thenReturn(SOME_ARBITRARY_NEGATIVE_INT);
when(mock.doSomethingThrowDeadObjectException()).thenThrow(new DeadObjectException());
when(mock.doSomethingThrowTransactionTooLargeException()).thenThrow(
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 2d77c6a..770d011 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -316,12 +316,8 @@
mAdapter = new DocumentsAdapter(context);
mRecView.setAdapter(mAdapter);
- mDefaultItemColor = context.getResources().getColor(android.R.color.transparent);
- // Get the accent color.
- TypedValue selColor = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.colorAccent, selColor, true);
- // Set the opacity to 10%.
- mSelectedItemColor = (selColor.data & 0x00ffffff) | 0x16000000;
+ mDefaultItemColor = context.getResources().getColor(R.color.item_doc_background);
+ mSelectedItemColor = context.getResources().getColor(R.color.item_doc_background_selected);
GestureDetector.SimpleOnGestureListener listener =
new GestureDetector.SimpleOnGestureListener() {
@@ -943,6 +939,7 @@
public void setSelected(boolean selected) {
itemView.setActivated(selected);
+ itemView.setBackgroundColor(selected ? mSelectedItemColor : mDefaultItemColor);
}
@Override
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/NetworkPolicySerializer.java b/packages/SettingsProvider/src/com/android/providers/settings/NetworkPolicySerializer.java
new file mode 100644
index 0000000..4b87da4
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/NetworkPolicySerializer.java
@@ -0,0 +1,186 @@
+package com.android.providers.settings;
+
+import android.net.NetworkPolicy;
+import android.net.NetworkTemplate;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * Backup/Restore Serializer Class for android.net.NetworkPolicy
+ */
+public class NetworkPolicySerializer {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "NetworkPolicySerializer";
+
+ private static final int NULL = 0;
+ private static final int NOT_NULL = 1;
+ /**
+ * Current Version of the Serializer.
+ */
+ private static int STATE_VERSION = 1;
+
+ /**
+ * Marshals an array of NetworkPolicy objects into a byte-array.
+ *
+ * @param policies - NetworkPolicies to be Marshaled
+ * @return byte array
+ */
+
+ public static byte[] marshalNetworkPolicies(NetworkPolicy policies[]) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ if (policies != null && policies.length != 0) {
+ DataOutputStream out = new DataOutputStream(baos);
+ try {
+ out.writeInt(STATE_VERSION);
+ out.writeInt(policies.length);
+ for (NetworkPolicy policy : policies) {
+ byte[] marshaledPolicy = marshalNetworkPolicy(policy);
+ if (marshaledPolicy != null) {
+ out.writeByte(NOT_NULL);
+ out.writeInt(marshaledPolicy.length);
+ out.write(marshaledPolicy);
+ } else {
+ out.writeByte(NULL);
+ }
+ }
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to Convert NetworkPolicies to byte array", ioe);
+ baos.reset();
+ }
+ }
+ return baos.toByteArray();
+ }
+
+ /**
+ * Unmarshals a byte array into an array of NetworkPolicy Objects
+ *
+ * @param data - marshaled NetworkPolicies Array
+ * @return NetworkPolicy[] array
+ */
+ public static NetworkPolicy[] unmarshalNetworkPolicies(byte[] data) {
+ if (data == null || data.length == 0) {
+ return new NetworkPolicy[0];
+ }
+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
+ try {
+ int version = in.readInt();
+ int length = in.readInt();
+ NetworkPolicy[] policies = new NetworkPolicy[length];
+ for (int i = 0; i < length; i++) {
+ byte isNull = in.readByte();
+ if (isNull == NULL) continue;
+ int byteLength = in.readInt();
+ byte[] policyData = new byte[byteLength];
+ in.read(policyData, 0, byteLength);
+ policies[i] = unmarshalNetworkPolicy(policyData);
+ }
+ return policies;
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to Convert byte array to NetworkPolicies", ioe);
+ return new NetworkPolicy[0];
+ }
+ }
+
+ /**
+ * Marshals a NetworkPolicy object into a byte-array.
+ *
+ * @param networkPolicy - NetworkPolicy to be Marshaled
+ * @return byte array
+ */
+ public static byte[] marshalNetworkPolicy(NetworkPolicy networkPolicy) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ if (networkPolicy != null) {
+ DataOutputStream out = new DataOutputStream(baos);
+ try {
+ out.writeInt(STATE_VERSION);
+ writeNetworkTemplate(out, networkPolicy.template);
+ out.writeInt(networkPolicy.cycleDay);
+ writeString(out, networkPolicy.cycleTimezone);
+ out.writeLong(networkPolicy.warningBytes);
+ out.writeLong(networkPolicy.limitBytes);
+ out.writeLong(networkPolicy.lastWarningSnooze);
+ out.writeLong(networkPolicy.lastLimitSnooze);
+ out.writeInt(networkPolicy.metered ? 1 : 0);
+ out.writeInt(networkPolicy.inferred ? 1 : 0);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to Convert NetworkPolicy to byte array", ioe);
+ baos.reset();
+ }
+ }
+ return baos.toByteArray();
+ }
+
+ /**
+ * Unmarshals a byte array into a NetworkPolicy Object
+ *
+ * @param data - marshaled NetworkPolicy Object
+ * @return NetworkPolicy Object
+ */
+ public static NetworkPolicy unmarshalNetworkPolicy(byte[] data) {
+ if (data == null || data.length == 0) {
+ return null;
+ }
+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
+ try {
+ int version = in.readInt();
+ NetworkTemplate template = readNetworkTemplate(in, version);
+ int cycleDay = in.readInt();
+ String cycleTimeZone = readString(in, version);
+ long warningBytes = in.readLong();
+ long limitBytes = in.readLong();
+ long lastWarningSnooze = in.readLong();
+ long lastLimitSnooze = in.readLong();
+ boolean metered = in.readInt() == 1;
+ boolean inferred = in.readInt() == 1;
+ return new NetworkPolicy(template, cycleDay, cycleTimeZone, warningBytes, limitBytes,
+ lastWarningSnooze, lastLimitSnooze, metered, inferred);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to Convert byte array to NetworkPolicy", ioe);
+ return null;
+ }
+ }
+
+ private static NetworkTemplate readNetworkTemplate(DataInputStream in, int version)
+ throws IOException {
+ byte isNull = in.readByte();
+ if (isNull == NULL) return null;
+ int matchRule = in.readInt();
+ String subscriberId = readString(in, version);
+ String networkId = readString(in, version);
+ return new NetworkTemplate(matchRule, subscriberId, networkId);
+ }
+
+ private static void writeNetworkTemplate(DataOutputStream out, NetworkTemplate template)
+ throws IOException {
+ if (template != null) {
+ out.writeByte(NOT_NULL);
+ out.writeInt(template.getMatchRule());
+ writeString(out, template.getSubscriberId());
+ writeString(out, template.getNetworkId());
+ } else {
+ out.writeByte(NULL);
+ }
+ }
+
+ private static String readString(DataInputStream in, int version) throws IOException {
+ byte isNull = in.readByte();
+ if (isNull == NOT_NULL) {
+ return in.readUTF();
+ }
+ return null;
+ }
+
+ private static void writeString(DataOutputStream out, String val) throws IOException {
+ if (val != null) {
+ out.writeByte(NOT_NULL);
+ out.writeUTF(val);
+ } else {
+ out.writeByte(NULL);
+ }
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 2e96f18..185a03f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -24,6 +24,7 @@
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.net.NetworkPolicyManager;
import android.net.Uri;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
@@ -81,10 +82,13 @@
private static final String KEY_GLOBAL = "global";
private static final String KEY_LOCALE = "locale";
private static final String KEY_LOCK_SETTINGS = "lock_settings";
+ private static final String KEY_SOFTAP_CONFIG = "softap_config";
+ private static final String KEY_NET_POLICIES = "network_policies";
+
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
- private static final int STATE_VERSION = 4;
+ private static final int STATE_VERSION = 6;
// Slots in the checksum array. Never insert new items in the middle
// of this array; new slots must be appended.
@@ -95,22 +99,28 @@
private static final int STATE_WIFI_CONFIG = 4;
private static final int STATE_GLOBAL = 5;
private static final int STATE_LOCK_SETTINGS = 6;
+ private static final int STATE_SOFTAP_CONFIG = 7;
+ private static final int STATE_NET_POLICIES = 8;
- private static final int STATE_SIZE = 7; // The current number of state items
+ private static final int STATE_SIZE = 9; // The current number of state items
// Number of entries in the checksum array at various version numbers
private static final int STATE_SIZES[] = {
- 0,
- 4, // version 1
- 5, // version 2 added STATE_WIFI_CONFIG
- 6, // version 3 added STATE_GLOBAL
- STATE_SIZE // version 4 added STATE_LOCK_SETTINGS
+ 0,
+ 4, // version 1
+ 5, // version 2 added STATE_WIFI_CONFIG
+ 6, // version 3 added STATE_GLOBAL
+ 7, // version 4 added STATE_LOCK_SETTINGS
+ 8, // version 5 added STATE_SOFTAP_CONFIG
+ STATE_SIZE // version 6 added STATE_NET_POLICIES
};
// Versioning of the 'full backup' format
private static final int FULL_BACKUP_VERSION = 3;
private static final int FULL_BACKUP_ADDED_GLOBAL = 2; // added the "global" entry
private static final int FULL_BACKUP_ADDED_LOCK_SETTINGS = 3; // added the "lock_settings" entry
+ private static final int FULL_BACKUP_ADDED_SOFTAP_CONF = 4; //added the "softap_config" entry
+ private static final int FULL_BACKUP_ADDED_NET_POLICIES = 5; //added the "network_policies" entry
private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
@@ -119,8 +129,8 @@
private static final String TAG = "SettingsBackupAgent";
private static final String[] PROJECTION = {
- Settings.NameValueTable.NAME,
- Settings.NameValueTable.VALUE
+ Settings.NameValueTable.NAME,
+ Settings.NameValueTable.VALUE
};
private static final String FILE_WIFI_SUPPLICANT = "/data/misc/wifi/wpa_supplicant.conf";
@@ -396,7 +406,7 @@
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState) throws IOException {
+ ParcelFileDescriptor newState) throws IOException {
byte[] systemSettingsData = getSystemSettings();
byte[] secureSettingsData = getSecureSettings();
@@ -405,26 +415,34 @@
byte[] locale = mSettingsHelper.getLocaleData();
byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
byte[] wifiConfigData = getFileData(mWifiConfigFile);
+ byte[] softApConfigData = getSoftAPConfiguration();
+ byte[] netPoliciesData = getNetworkPolicies();
long[] stateChecksums = readOldChecksums(oldState);
stateChecksums[STATE_SYSTEM] =
- writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
+ writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
stateChecksums[STATE_SECURE] =
- writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
+ writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
stateChecksums[STATE_GLOBAL] =
- writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
+ writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
stateChecksums[STATE_LOCALE] =
- writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
+ writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
stateChecksums[STATE_WIFI_SUPPLICANT] =
- writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
- wifiSupplicantData, data);
+ writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
+ wifiSupplicantData, data);
stateChecksums[STATE_WIFI_CONFIG] =
- writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
- data);
+ writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
+ data);
stateChecksums[STATE_LOCK_SETTINGS] =
- writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
- lockSettingsData, data);
+ writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
+ lockSettingsData, data);
+ stateChecksums[STATE_SOFTAP_CONFIG] =
+ writeIfChanged(stateChecksums[STATE_SOFTAP_CONFIG], KEY_SOFTAP_CONFIG,
+ softApConfigData, data);
+ stateChecksums[STATE_NET_POLICIES] =
+ writeIfChanged(stateChecksums[STATE_NET_POLICIES], KEY_NET_POLICIES,
+ netPoliciesData, data);
writeNewChecksums(stateChecksums, newState);
}
@@ -504,7 +522,7 @@
restoredSupplicantData, restoredSupplicantData.length);
FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
FileUtils.S_IRUSR | FileUtils.S_IWUSR |
- FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+ FileUtils.S_IRGRP | FileUtils.S_IWGRP,
Process.myUid(), Process.WIFI_UID);
}
if (restoredWifiConfigFile != null) {
@@ -533,7 +551,7 @@
@Override
public void onRestore(BackupDataInput data, int appVersionCode,
- ParcelFileDescriptor newState) throws IOException {
+ ParcelFileDescriptor newState) throws IOException {
HashSet<String> movedToGlobal = new HashSet<String>();
Settings.System.getMovedToGlobalSettings(movedToGlobal);
@@ -561,7 +579,15 @@
mWifiRestore.incorporateWifiConfigFile(data);
} else if (KEY_LOCK_SETTINGS.equals(key)) {
restoreLockSettings(data);
- } else {
+ } else if (KEY_SOFTAP_CONFIG.equals(key)){
+ byte[] softapData = new byte[size];
+ data.readEntityData(softapData, 0, size);
+ restoreSoftApConfiguration(softapData);
+ } else if (KEY_NET_POLICIES.equals(key)) {
+ byte[] netPoliciesData = new byte[size];
+ data.readEntityData(netPoliciesData, 0, size);
+ restoreNetworkPolicies(netPoliciesData);
+ } else {
data.skipEntityData();
}
}
@@ -589,6 +615,8 @@
byte[] locale = mSettingsHelper.getLocaleData();
byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
byte[] wifiConfigData = getFileData(mWifiConfigFile);
+ byte[] softApConfigData = getSoftAPConfiguration();
+ byte[] netPoliciesData = getNetworkPolicies();
// Write the data to the staging file, then emit that as our tarfile
// representation of the backed-up settings.
@@ -623,6 +651,12 @@
if (DEBUG_BACKUP) Log.d(TAG, lockSettingsData.length + " bytes of lock settings data");
out.writeInt(lockSettingsData.length);
out.write(lockSettingsData);
+ if (DEBUG_BACKUP) Log.d(TAG, softApConfigData.length + " bytes of softap config data");
+ out.writeInt(softApConfigData.length);
+ out.write(softApConfigData);
+ if (DEBUG_BACKUP) Log.d(TAG, netPoliciesData.length + " bytes of network policies data");
+ out.writeInt(netPoliciesData.length);
+ out.write(netPoliciesData);
out.flush(); // also flushes downstream
@@ -635,7 +669,7 @@
@Override
public void onRestoreFile(ParcelFileDescriptor data, long size,
- int type, String domain, String relpath, long mode, long mtime)
+ int type, String domain, String relpath, long mode, long mtime)
throws IOException {
if (DEBUG_BACKUP) Log.d(TAG, "onRestoreFile() invoked");
// Our data is actually a blob of flattened settings data identical to that
@@ -692,7 +726,7 @@
restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, buffer, nBytes);
FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
FileUtils.S_IRUSR | FileUtils.S_IWUSR |
- FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+ FileUtils.S_IRGRP | FileUtils.S_IWGRP,
Process.myUid(), Process.WIFI_UID);
// retain the previous WIFI state.
enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
@@ -715,6 +749,26 @@
}
}
+ if (version >= FULL_BACKUP_ADDED_SOFTAP_CONF){
+ nBytes = in.readInt();
+ if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of softap config data");
+ if (nBytes > buffer.length) buffer = new byte[nBytes];
+ if (nBytes > 0) {
+ in.readFully(buffer, 0, nBytes);
+ restoreSoftApConfiguration(buffer);
+ }
+ }
+
+ if (version >= FULL_BACKUP_ADDED_NET_POLICIES){
+ nBytes = in.readInt();
+ if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of network policies data");
+ if (nBytes > buffer.length) buffer = new byte[nBytes];
+ if (nBytes > 0) {
+ in.readFully(buffer, 0, nBytes);
+ restoreNetworkPolicies(buffer);
+ }
+ }
+
if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
} else {
data.close();
@@ -754,7 +808,7 @@
}
private long writeIfChanged(long oldChecksum, String key, byte[] data,
- BackupDataOutput output) {
+ BackupDataOutput output) {
CRC32 checkSummer = new CRC32();
checkSummer.update(data);
long newChecksum = checkSummer.getValue();
@@ -829,7 +883,7 @@
}
private void restoreSettings(BackupDataInput data, Uri contentUri,
- HashSet<String> movedToGlobal) {
+ HashSet<String> movedToGlobal) {
byte[] settings = new byte[data.getDataSize()];
try {
data.readEntityData(settings, 0, settings.length);
@@ -841,7 +895,7 @@
}
private void restoreSettings(byte[] settings, int bytes, Uri contentUri,
- HashSet<String> movedToGlobal) {
+ HashSet<String> movedToGlobal) {
if (DEBUG) {
Log.i(TAG, "restoreSettings: " + contentUri);
}
@@ -1160,6 +1214,30 @@
}
}
+ private byte[] getSoftAPConfiguration(){
+ WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
+ return WiFiConfigurationSerializer.marshalWifiConfig(wifiManager.getWifiApConfiguration());
+ }
+
+ private void restoreSoftApConfiguration(byte[] data){
+ WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
+ wifiManager.setWifiApConfiguration(WiFiConfigurationSerializer.unmarshalWifiConfig(data));
+ }
+
+ private byte[] getNetworkPolicies(){
+ NetworkPolicyManager networkPolicyManager =
+ (NetworkPolicyManager)getSystemService(NETWORK_POLICY_SERVICE);
+ return NetworkPolicySerializer
+ .marshalNetworkPolicies(networkPolicyManager.getNetworkPolicies());
+ }
+
+ private void restoreNetworkPolicies(byte[] data){
+ NetworkPolicyManager networkPolicyManager =
+ (NetworkPolicyManager)getSystemService(NETWORK_POLICY_SERVICE);
+ networkPolicyManager
+ .setNetworkPolicies(NetworkPolicySerializer.unmarshalNetworkPolicies(data));
+ }
+
/**
* Write an int in BigEndian into the byte array.
* @param out byte array
@@ -1181,8 +1259,7 @@
}
private int readInt(byte[] in, int pos) {
- int result =
- ((in[pos ] & 0xFF) << 24) |
+ int result = ((in[pos ] & 0xFF) << 24) |
((in[pos + 1] & 0xFF) << 16) |
((in[pos + 2] & 0xFF) << 8) |
((in[pos + 3] & 0xFF) << 0);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java b/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java
new file mode 100644
index 0000000..f9f1d3f
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java
@@ -0,0 +1,400 @@
+/*
+ * 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.providers.settings;
+
+import android.net.IpConfiguration;
+import android.net.LinkAddress;
+import android.net.ProxyInfo;
+import android.net.StaticIpConfiguration;
+import android.net.wifi.WifiConfiguration;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.BitSet;
+
+
+/**
+ * Backup/Restore Serializer Class for com.android.net.wifi.WifiConfiguration
+ */
+public class WiFiConfigurationSerializer {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "WiFiConfigSerializer";
+
+ private static final int NULL = 0;
+ private static final int NOT_NULL = 1;
+ /**
+ * Current Version of the Serializer.
+ */
+ private static int STATE_VERSION = 1;
+
+
+ /**
+ * Marshals a WifiConfig object into a byte-array.
+ *
+ * @param wifiConfig - WifiConfiguration to be Marshalled
+ * @return byte array
+ */
+
+ public static byte[] marshalWifiConfig(WifiConfiguration wifiConfig) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ if(wifiConfig != null) {
+ DataOutputStream out = new DataOutputStream(baos);
+ try {
+ out.writeInt(STATE_VERSION);
+ out.writeInt(wifiConfig.networkId);
+ out.writeInt(wifiConfig.status);
+ out.writeInt(wifiConfig.disableReason);
+ writeString(out, wifiConfig.SSID);
+ writeString(out, wifiConfig.BSSID);
+ out.writeInt(wifiConfig.apBand);
+ out.writeInt(wifiConfig.apChannel);
+ writeString(out, wifiConfig.autoJoinBSSID);
+ writeString(out, wifiConfig.FQDN);
+ writeString(out, wifiConfig.providerFriendlyName);
+ out.writeInt(wifiConfig.roamingConsortiumIds.length);
+ for (long id : wifiConfig.roamingConsortiumIds) {
+ out.writeLong(id);
+ }
+ writeString(out, wifiConfig.preSharedKey);
+ for (String wepKey : wifiConfig.wepKeys) {
+ writeString(out, wepKey);
+ }
+ out.writeInt(wifiConfig.wepTxKeyIndex);
+ out.writeInt(wifiConfig.priority);
+ out.writeInt(wifiConfig.hiddenSSID ? 1 : 0);
+ out.writeInt(wifiConfig.requirePMF ? 1 : 0);
+ writeString(out, wifiConfig.updateIdentifier);
+
+ writeBitSet(out, wifiConfig.allowedKeyManagement);
+ writeBitSet(out, wifiConfig.allowedProtocols);
+ writeBitSet(out, wifiConfig.allowedAuthAlgorithms);
+ writeBitSet(out, wifiConfig.allowedPairwiseCiphers);
+ writeBitSet(out, wifiConfig.allowedGroupCiphers);
+
+
+ //IpConfiguration
+ writeIpConfiguration(out, wifiConfig.getIpConfiguration());
+
+ writeString(out, wifiConfig.dhcpServer);
+ writeString(out, wifiConfig.defaultGwMacAddress);
+ out.writeInt(wifiConfig.autoJoinStatus);
+ out.writeInt(wifiConfig.selfAdded ? 1 : 0);
+ out.writeInt(wifiConfig.didSelfAdd ? 1 : 0);
+ out.writeInt(wifiConfig.validatedInternetAccess ? 1 : 0);
+ out.writeInt(wifiConfig.ephemeral ? 1 : 0);
+ out.writeInt(wifiConfig.creatorUid);
+ out.writeInt(wifiConfig.lastConnectUid);
+ out.writeInt(wifiConfig.lastUpdateUid);
+ writeString(out, wifiConfig.creatorName);
+ writeString(out, wifiConfig.lastUpdateName);
+ out.writeLong(wifiConfig.blackListTimestamp);
+ out.writeLong(wifiConfig.lastConnectionFailure);
+ out.writeLong(wifiConfig.lastRoamingFailure);
+ out.writeInt(wifiConfig.lastRoamingFailureReason);
+ out.writeLong(wifiConfig.roamingFailureBlackListTimeMilli);
+ out.writeLong(wifiConfig.numConnectionFailures);
+ out.writeLong(wifiConfig.numIpConfigFailures);
+ out.writeInt(wifiConfig.numAuthFailures);
+ out.writeInt(wifiConfig.numScorerOverride);
+ out.writeInt(wifiConfig.numScorerOverrideAndSwitchedNetwork);
+ out.writeInt(wifiConfig.numAssociation);
+ out.writeInt(wifiConfig.numUserTriggeredWifiDisableLowRSSI);
+ out.writeInt(wifiConfig.numUserTriggeredWifiDisableBadRSSI);
+ out.writeInt(wifiConfig.numUserTriggeredWifiDisableNotHighRSSI);
+ out.writeInt(wifiConfig.numTicksAtLowRSSI);
+ out.writeInt(wifiConfig.numTicksAtBadRSSI);
+ out.writeInt(wifiConfig.numTicksAtNotHighRSSI);
+ out.writeInt(wifiConfig.numUserTriggeredJoinAttempts);
+ out.writeInt(wifiConfig.autoJoinUseAggressiveJoinAttemptThreshold);
+ out.writeInt(wifiConfig.autoJoinBailedDueToLowRssi ? 1 : 0);
+ out.writeInt(wifiConfig.userApproved);
+ out.writeInt(wifiConfig.numNoInternetAccessReports);
+ out.writeInt(wifiConfig.noInternetAccessExpected ? 1 : 0);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to Convert WifiConfiguration to byte array", ioe);
+ baos.reset();
+ }
+ }
+ return baos.toByteArray();
+ }
+
+ /**
+ * Unmarshals a byte array into a WifiConfig Object
+ *
+ * @param data - marshalled WifiConfig Object
+ * @return WifiConfiguration Object
+ */
+
+ public static WifiConfiguration unmarshalWifiConfig(byte[] data) {
+ if (data == null || data.length == 0) {
+ return null;
+ }
+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
+ WifiConfiguration config = new WifiConfiguration();
+ try {
+ int version = in.readInt();
+
+ config.networkId = in.readInt();
+ config.status = in.readInt();
+ config.disableReason = in.readInt();
+ config.SSID = readString(in, version);
+ config.BSSID = readString(in, version);
+ config.apBand = in.readInt();
+ config.apChannel = in.readInt();
+ config.autoJoinBSSID = readString(in, version);
+ config.FQDN = readString(in, version);
+ config.providerFriendlyName = readString(in, version);
+ int numRoamingConsortiumIds = in.readInt();
+ config.roamingConsortiumIds = new long[numRoamingConsortiumIds];
+ for (int i = 0; i < numRoamingConsortiumIds; i++) {
+ config.roamingConsortiumIds[i] = in.readLong();
+ }
+ config.preSharedKey = readString(in, version);
+ for (int i = 0; i < config.wepKeys.length; i++) {
+ config.wepKeys[i] = readString(in, version);
+ }
+ config.wepTxKeyIndex = in.readInt();
+ config.priority = in.readInt();
+ config.hiddenSSID = in.readInt() != 0;
+ config.requirePMF = in.readInt() != 0;
+ config.updateIdentifier = readString(in, version);
+
+ config.allowedKeyManagement = readBitSet(in, version);
+ config.allowedProtocols = readBitSet(in, version);
+ config.allowedAuthAlgorithms = readBitSet(in, version);
+ config.allowedPairwiseCiphers = readBitSet(in, version);
+ config.allowedGroupCiphers = readBitSet(in, version);
+
+ //Not backed-up because EnterpriseConfig involves
+ //Certificates which are device specific.
+ //config.enterpriseConfig = new WifiEnterpriseConfig();
+
+ config.setIpConfiguration(readIpConfiguration(in, version));
+
+
+ config.dhcpServer = readString(in, version);
+ config.defaultGwMacAddress = readString(in, version);
+ config.autoJoinStatus = in.readInt();
+ config.selfAdded = in.readInt() != 0;
+ config.didSelfAdd = in.readInt() != 0;
+ config.validatedInternetAccess = in.readInt() != 0;
+ config.ephemeral = in.readInt() != 0;
+ config.creatorUid = in.readInt();
+ config.lastConnectUid = in.readInt();
+ config.lastUpdateUid = in.readInt();
+ config.creatorName = readString(in, version);
+ config.lastUpdateName = readString(in, version);
+ config.blackListTimestamp = in.readLong();
+ config.lastConnectionFailure = in.readLong();
+ config.lastRoamingFailure = in.readLong();
+ config.lastRoamingFailureReason = in.readInt();
+ config.roamingFailureBlackListTimeMilli = in.readLong();
+ config.numConnectionFailures = in.readInt();
+ config.numIpConfigFailures = in.readInt();
+ config.numAuthFailures = in.readInt();
+ config.numScorerOverride = in.readInt();
+ config.numScorerOverrideAndSwitchedNetwork = in.readInt();
+ config.numAssociation = in.readInt();
+ config.numUserTriggeredWifiDisableLowRSSI = in.readInt();
+ config.numUserTriggeredWifiDisableBadRSSI = in.readInt();
+ config.numUserTriggeredWifiDisableNotHighRSSI = in.readInt();
+ config.numTicksAtLowRSSI = in.readInt();
+ config.numTicksAtBadRSSI = in.readInt();
+ config.numTicksAtNotHighRSSI = in.readInt();
+ config.numUserTriggeredJoinAttempts = in.readInt();
+ config.autoJoinUseAggressiveJoinAttemptThreshold = in.readInt();
+ config.autoJoinBailedDueToLowRssi = in.readInt() != 0;
+ config.userApproved = in.readInt();
+ config.numNoInternetAccessReports = in.readInt();
+ config.noInternetAccessExpected = in.readInt() != 0;
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to convert byte array to WifiConfiguration object", ioe);
+ return null;
+ }
+ return config;
+ }
+
+ private static ProxyInfo readProxyInfo(DataInputStream in, int version) throws IOException {
+ int isNull = in.readByte();
+ if (isNull == NULL) return null;
+ String host = readString(in, version);
+ int port = in.readInt();
+ String exclusionList = readString(in, version);
+ return new ProxyInfo(host, port, exclusionList);
+ }
+
+ private static void writeProxyInfo(DataOutputStream out, ProxyInfo proxyInfo) throws IOException {
+ if (proxyInfo != null) {
+ out.writeByte(NOT_NULL);
+ writeString(out, proxyInfo.getHost());
+ out.writeInt(proxyInfo.getPort());
+ writeString(out, proxyInfo.getExclusionListAsString());
+ } else {
+ out.writeByte(NULL);
+ }
+ }
+
+ private static InetAddress readInetAddress(DataInputStream in, int version) throws IOException {
+ int isNull = in.readByte();
+ if (isNull == NULL) return null;
+ InetAddress address = null;
+ int addressLength = in.readInt();
+ if (addressLength < 1) return address;
+ byte[] addressBytes = new byte[addressLength];
+ in.read(addressBytes, 0, addressLength);
+ try {
+ address = InetAddress.getByAddress(addressBytes);
+ } catch (UnknownHostException unknownHostException) {
+ return null;
+ }
+ return address;
+ }
+
+ private static void writeInetAddress(DataOutputStream out, InetAddress address) throws IOException {
+ if (address.getAddress() != null) {
+ out.writeByte(NOT_NULL);
+ out.writeInt(address.getAddress().length);
+ out.write(address.getAddress(), 0, address.getAddress().length);
+ } else {
+ out.writeByte(NULL);
+ }
+ }
+
+ private static LinkAddress readLinkAddress(DataInputStream in, int version) throws IOException {
+ int isNull = in.readByte();
+ if (isNull == NULL) return null;
+ InetAddress address = readInetAddress(in, version);
+ int prefixLength = in.readInt();
+ int flags = in.readInt();
+ int scope = in.readInt();
+ return new LinkAddress(address, prefixLength, flags, scope);
+ }
+
+ private static void writeLinkAddress(DataOutputStream out, LinkAddress address) throws IOException {
+ if (address != null) {
+ out.writeByte(NOT_NULL);
+ writeInetAddress(out, address.getAddress());
+ out.writeInt(address.getPrefixLength());
+ out.writeInt(address.getFlags());
+ out.writeInt(address.getScope());
+ } else {
+ out.writeByte(NULL);
+ }
+ }
+
+ private static StaticIpConfiguration readStaticIpConfiguration(DataInputStream in, int version) throws IOException {
+ int isNull = in.readByte();
+ if (isNull == NULL) return null;
+ StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
+ staticIpConfiguration.ipAddress = readLinkAddress(in, version);
+ staticIpConfiguration.gateway = readInetAddress(in, version);
+ int dnsServersLength = in.readInt();
+ for (int i = 0; i < dnsServersLength; i++) {
+ staticIpConfiguration.dnsServers.add(readInetAddress(in, version));
+ }
+ staticIpConfiguration.domains = readString(in, version);
+ return staticIpConfiguration;
+ }
+
+ private static void writeStaticIpConfiguration(DataOutputStream out, StaticIpConfiguration staticIpConfiguration) throws IOException {
+ if (staticIpConfiguration != null) {
+ out.writeByte(NOT_NULL);
+ writeLinkAddress(out, staticIpConfiguration.ipAddress);
+ writeInetAddress(out, staticIpConfiguration.gateway);
+ out.writeInt(staticIpConfiguration.dnsServers.size());
+ for (InetAddress inetAddress : staticIpConfiguration.dnsServers) {
+ writeInetAddress(out, inetAddress);
+ }
+ writeString(out, staticIpConfiguration.domains);
+ } else {
+ out.writeByte(NULL);
+ }
+ }
+
+ private static IpConfiguration readIpConfiguration(DataInputStream in, int version) throws IOException {
+ int isNull = in.readByte();
+ if (isNull == NULL) return null;
+ IpConfiguration ipConfiguration = new IpConfiguration();
+ String tmp = readString(in, version);
+ ipConfiguration.ipAssignment = tmp == null ? null : IpConfiguration.IpAssignment.valueOf(tmp);
+ tmp = readString(in, version);
+ ipConfiguration.proxySettings = tmp == null ? null : IpConfiguration.ProxySettings.valueOf(tmp);
+ ipConfiguration.staticIpConfiguration = readStaticIpConfiguration(in, version);
+ ipConfiguration.httpProxy = readProxyInfo(in, version);
+ return ipConfiguration;
+ }
+
+
+ private static void writeIpConfiguration(DataOutputStream out, IpConfiguration ipConfiguration) throws IOException {
+ if (ipConfiguration != null) {
+ out.writeByte(NOT_NULL);
+ writeString(out, ipConfiguration.ipAssignment != null ? ipConfiguration.ipAssignment.name() : null);
+ writeString(out, ipConfiguration.proxySettings != null ? ipConfiguration.proxySettings.name() : null);
+ writeStaticIpConfiguration(out, ipConfiguration.staticIpConfiguration);
+ writeProxyInfo(out, ipConfiguration.httpProxy);
+ } else {
+ out.writeByte(NULL);
+ }
+
+ }
+
+ private static String readString(DataInputStream in, int version) throws IOException {
+ byte isNull = in.readByte();
+ if (isNull == NOT_NULL) {
+ return in.readUTF();
+ }
+ return null;
+ }
+
+ private static void writeString(DataOutputStream out, String val) throws IOException {
+ if (val != null) {
+ out.writeByte(NOT_NULL);
+ out.writeUTF(val);
+ } else {
+ out.writeByte(NULL);
+ }
+ }
+
+ private static BitSet readBitSet(DataInputStream in, int version) throws IOException {
+ byte isNull = in.readByte();
+ if (isNull == NOT_NULL) {
+ int length = in.readInt();
+ byte[] bytes = new byte[length];
+ in.read(bytes, 0, length);
+ return BitSet.valueOf(bytes);
+ }
+ return new BitSet();
+ }
+
+ private static void writeBitSet(DataOutputStream out, BitSet val) throws IOException {
+ if (val != null) {
+ out.writeByte(NOT_NULL);
+ byte[] byteArray = val.toByteArray();
+ out.writeInt(byteArray.length);
+ out.write(byteArray);
+ } else {
+ out.writeByte(NULL);
+ }
+ }
+}
diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk
index ef863e7..f278967 100644
--- a/packages/SettingsProvider/test/Android.mk
+++ b/packages/SettingsProvider/test/Android.mk
@@ -5,7 +5,10 @@
# Note we statically link SettingsState to do some unit tests. It's not accessible otherwise
# because this test is not an instrumentation test. (because the target runs in the system process.)
LOCAL_SRC_FILES := $(call all-subdir-java-files) \
- ../src/com/android/providers/settings/SettingsState.java
+ ../src/com/android/providers/settings/SettingsState.java \
+ ../src/com/android/providers/settings/WiFiConfigurationSerializer.java \
+ ../src/com/android/providers/settings/NetworkPolicySerializer.java
+
LOCAL_PACKAGE_NAME := SettingsProviderTest
@@ -13,4 +16,4 @@
LOCAL_CERTIFICATE := platform
-include $(BUILD_PACKAGE)
\ No newline at end of file
+include $(BUILD_PACKAGE)
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/NetworkPolicySerializerTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/NetworkPolicySerializerTest.java
new file mode 100644
index 0000000..1986596
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/NetworkPolicySerializerTest.java
@@ -0,0 +1,117 @@
+package com.android.providers.settings;
+
+import android.net.NetworkPolicy;
+import android.net.NetworkTemplate;
+import android.test.AndroidTestCase;
+
+import java.util.Random;
+
+/**
+ * Tests for NetworkPolicySerializer
+ */
+public class NetworkPolicySerializerTest extends AndroidTestCase {
+ static Random sRandom = new Random();
+
+ public void testMarshallAndUnmarshalNetworkPolicy() {
+ NetworkPolicy policy = getDummyNetworkPolicy();
+ byte[] data = NetworkPolicySerializer.marshalNetworkPolicy(policy);
+ assertNotNull("Got Null data from marshal", data);
+ assertFalse("Got back an empty byte[] from marshal", data.length == 0);
+
+ NetworkPolicy unmarshaled = NetworkPolicySerializer.unmarshalNetworkPolicy(data);
+ assertNotNull("Got Null data from unmarshaled", unmarshaled);
+ assertTrue("NetworkPolicy Marshall and Unmarshal Failed!", policy.equals(unmarshaled));
+ }
+
+ public void testMarshallNetworkPolicyEdgeCases() {
+ byte[] data = NetworkPolicySerializer.marshalNetworkPolicy(null);
+ assertNotNull("NetworkPolicy marshal returned null. Expected: byte[0]", data);
+ assertEquals("NetworkPolicy marshal returned incomplete byte array. Expected: byte[0]",
+ data.length, 0);
+ }
+
+ public void testUnmarshallNetworkPolicyEdgeCases() {
+ NetworkPolicy policy = NetworkPolicySerializer.unmarshalNetworkPolicy(null);
+ assertNull("Non null NetworkPolicy returned for null byte[] input", policy);
+
+ policy = NetworkPolicySerializer.unmarshalNetworkPolicy(new byte[0]);
+ assertNull("Non null NetworkPolicy returned for empty byte[] input", policy);
+
+ policy = NetworkPolicySerializer.unmarshalNetworkPolicy(new byte[]{10, 20, 30, 40, 50, 60});
+ assertNull("Non null NetworkPolicy returned for incomplete byte[] input", policy);
+ }
+
+ public void testMarshallAndUnmarshalNetworkPolicies() {
+ NetworkPolicy[] policies = getDummyNetworkPolicies(5);
+ byte[] data = NetworkPolicySerializer.marshalNetworkPolicies(policies);
+ assertNotNull("Got Null data from marshal", data);
+ assertFalse("Got back an empty byte[] from marshal", data.length == 0);
+
+ NetworkPolicy[] unmarshaled = NetworkPolicySerializer.unmarshalNetworkPolicies(data);
+ assertNotNull("Got Null data from unmarshaled", unmarshaled);
+ try {
+ for (int i = 0; i < policies.length; i++) {
+ assertTrue("NetworkPolicies Marshall and Unmarshal Failed!",
+ policies[i].equals(unmarshaled[i]));
+ }
+ } catch (NullPointerException npe) {
+ assertTrue("Some policies were not marshaled/unmarshaled correctly", false);
+ }
+ }
+
+ public void testMarshallNetworkPoliciesEdgeCases() {
+ byte[] data = NetworkPolicySerializer.marshalNetworkPolicies(null);
+ assertNotNull("NetworkPolicies marshal returned null!", data);
+ assertEquals("NetworkPolicies marshal returned incomplete byte array", data.length, 0);
+
+ data = NetworkPolicySerializer.marshalNetworkPolicies(new NetworkPolicy[0]);
+ assertNotNull("NetworkPolicies marshal returned null for empty NetworkPolicy[]", data);
+ assertEquals("NetworkPolicies marshal returned incomplete byte array for empty NetworkPolicy[]"
+ , data.length, 0);
+ }
+
+ public void testUnmarshalNetworkPoliciesEdgeCases() {
+ NetworkPolicy[] policies = NetworkPolicySerializer.unmarshalNetworkPolicies(null);
+ assertNotNull("NetworkPolicies unmarshal returned null for null input. Expected: byte[0] ",
+ policies);
+ assertEquals("Non Empty NetworkPolicy[] returned for null input Expected: byte[0]",
+ policies.length, 0);
+
+ policies = NetworkPolicySerializer.unmarshalNetworkPolicies(new byte[0]);
+ assertNotNull("NetworkPolicies unmarshal returned null for empty byte[] input. Expected: byte[0]",
+ policies);
+ assertEquals("Non Empty NetworkPolicy[] returned for empty byte[] input. Expected: byte[0]",
+ policies.length, 0);
+
+ policies = NetworkPolicySerializer.unmarshalNetworkPolicies(new byte[]{10, 20, 30, 40, 50, 60});
+ assertNotNull("NetworkPolicies unmarshal returned null for incomplete byte[] input. " +
+ "Expected: byte[0] ", policies);
+ assertEquals("Non Empty NetworkPolicy[] returned for incomplete byte[] input Expected: byte[0]",
+ policies.length, 0);
+
+ }
+
+ private NetworkPolicy[] getDummyNetworkPolicies(int num) {
+ NetworkPolicy[] policies = new NetworkPolicy[num];
+ for (int i = 0; i < num; i++) {
+ policies[i] = getDummyNetworkPolicy();
+ }
+ return policies;
+ }
+
+ private NetworkPolicy getDummyNetworkPolicy() {
+ NetworkTemplate template = new NetworkTemplate(NetworkTemplate.MATCH_MOBILE_ALL, "subId",
+ "GoogleGuest");
+ int cycleDay = sRandom.nextInt();
+ String cycleTimezone = "timezone";
+ long warningBytes = sRandom.nextLong();
+ long limitBytes = sRandom.nextLong();
+ long lastWarningSnooze = sRandom.nextLong();
+ long lastLimitSnooze = sRandom.nextLong();
+ boolean metered = sRandom.nextInt() % 2 == 0;
+ boolean inferred = sRandom.nextInt() % 2 == 0;
+ return new NetworkPolicy(template, cycleDay, cycleTimezone, warningBytes, limitBytes,
+ lastWarningSnooze, lastLimitSnooze, metered, inferred);
+ }
+
+}
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 78e373b..b604768 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -66,7 +66,7 @@
import android.widget.Toast;
/**
- * Service used to keep progress of bug reports processes ({@code dumpstate}).
+ * Service used to keep progress of bugreport processes ({@code dumpstate}).
* <p>
* The workflow is:
* <ol>
@@ -84,7 +84,7 @@
* <li>{@link BugreportReceiver} receives the intent and delegates it to this service, which in
* turn:
* <ol>
- * <li>Updates the system notification so user can share the bug report.
+ * <li>Updates the system notification so user can share the bugreport.
* <li>Stops monitoring that {@code dumpstate} process.
* <li>Stops itself if it doesn't have any process left to monitor.
* </ol>
@@ -96,9 +96,13 @@
private static final String AUTHORITY = "com.android.shell";
+ // External intents sent by dumpstate.
static final String INTENT_BUGREPORT_STARTED = "android.intent.action.BUGREPORT_STARTED";
static final String INTENT_BUGREPORT_FINISHED = "android.intent.action.BUGREPORT_FINISHED";
+
+ // Internal intents used on notification actions.
static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL";
+ static final String INTENT_BUGREPORT_SHARE = "android.intent.action.BUGREPORT_SHARE";
static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
@@ -111,7 +115,7 @@
private static final int MSG_POLL = 2;
/** Polling frequency, in milliseconds. */
- private static final long POLLING_FREQUENCY = 500;
+ static final long POLLING_FREQUENCY = 2000;
/** How long (in ms) a dumpstate process will be monitored if it didn't show progress. */
private static final long INACTIVITY_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
@@ -169,10 +173,16 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- writer.printf("Monitored dumpstate processes: \n");
synchronized (mProcesses) {
- for (int i = 0; i < mProcesses.size(); i++) {
- writer.printf("\t%s\n", mProcesses.valueAt(i));
+ final int size = mProcesses.size();
+ if (size == 0) {
+ writer.printf("No monitored processes");
+ return;
+ }
+ writer.printf("Monitored dumpstate processes\n");
+ writer.printf("-----------------------------\n");
+ for (int i = 0; i < size; i++) {
+ writer.printf("%s\n", mProcesses.valueAt(i));
}
}
}
@@ -180,7 +190,6 @@
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
- poll();
}
@Override
@@ -196,25 +205,24 @@
return;
}
- // At this point it's handling onStartCommand(), whose intent contains the extras
- // originally received by BugreportReceiver.
+ // At this point it's handling onStartCommand(), with the intent passed as an Extra.
if (!(msg.obj instanceof Intent)) {
// Sanity check.
Log.e(TAG, "Internal error: invalid msg.obj: " + msg.obj);
return;
}
final Parcelable parcel = ((Intent) msg.obj).getParcelableExtra(EXTRA_ORIGINAL_INTENT);
- if (!(parcel instanceof Intent)) {
- // Sanity check.
- Log.e(TAG, "Internal error: msg.obj is missing extra " + EXTRA_ORIGINAL_INTENT);
- return;
+ final Intent intent;
+ if (parcel instanceof Intent) {
+ // The real intent was passed to BugreportReceiver, which delegated to the service.
+ intent = (Intent) parcel;
+ } else {
+ intent = (Intent) msg.obj;
}
-
- final Intent intent = (Intent) parcel;
final String action = intent.getAction();
- int pid = intent.getIntExtra(EXTRA_PID, 0);
- int max = intent.getIntExtra(EXTRA_MAX, -1);
- String name = intent.getStringExtra(EXTRA_NAME);
+ final int pid = intent.getIntExtra(EXTRA_PID, 0);
+ final int max = intent.getIntExtra(EXTRA_MAX, -1);
+ final String name = intent.getStringExtra(EXTRA_NAME);
if (DEBUG) Log.v(TAG, "action: " + action + ", name: " + name + ", pid: " + pid
+ ", max: "+ max);
@@ -224,14 +232,18 @@
stopSelfWhenDone();
return;
}
+ poll();
break;
case INTENT_BUGREPORT_FINISHED:
- if (pid == -1) {
+ if (pid == 0) {
// Shouldn't happen, unless BUGREPORT_FINISHED is received from a legacy,
// out-of-sync dumpstate process.
Log.w(TAG, "Missing " + EXTRA_PID + " on intent " + intent);
}
- stopProgress(pid, intent);
+ onBugreportFinished(pid, intent);
+ break;
+ case INTENT_BUGREPORT_SHARE:
+ shareBugreport(pid);
break;
case INTENT_BUGREPORT_CANCEL:
cancel(pid);
@@ -247,6 +259,8 @@
if (pollProgress()) {
// Keep polling...
sendEmptyMessageDelayed(MSG_POLL, POLLING_FREQUENCY);
+ } else {
+ Log.i(TAG, "Stopped polling");
}
}
}
@@ -283,7 +297,7 @@
}
/**
- * Updates the system notification for a given bug report.
+ * Updates the system notification for a given bugreport.
*/
private void updateProgress(BugreportInfo info) {
if (info.max <= 0 || info.progress < 0) {
@@ -296,14 +310,8 @@
nf.setMinimumFractionDigits(2);
nf.setMaximumFractionDigits(2);
final String percentText = nf.format((double) info.progress / info.max);
-
- final Intent cancelIntent = new Intent(context, BugreportReceiver.class);
- cancelIntent.setAction(INTENT_BUGREPORT_CANCEL);
- cancelIntent.putExtra(EXTRA_PID, info.pid);
- final Action cancelAction = new Action.Builder(null,
- context.getString(com.android.internal.R.string.cancel),
- PendingIntent.getBroadcast(context, info.pid, cancelIntent,
- PendingIntent.FLAG_CANCEL_CURRENT)).build();
+ final Action cancelAction = new Action.Builder(null, context.getString(
+ com.android.internal.R.string.cancel), newCancelIntent(context, info)).build();
final String title = context.getString(R.string.bugreport_in_progress_title);
final String name =
@@ -327,9 +335,19 @@
}
/**
- * Finalizes the progress on a given process and sends the finished intent.
+ * Creates a {@link PendingIntent} for a notification action used to cancel a bugreport.
*/
- private void stopProgress(int pid, Intent intent) {
+ private static PendingIntent newCancelIntent(Context context, BugreportInfo info) {
+ final Intent intent = new Intent(INTENT_BUGREPORT_CANCEL);
+ intent.setClass(context, BugreportProgressService.class);
+ intent.putExtra(EXTRA_PID, info.pid);
+ return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+
+ /**
+ * Finalizes the progress on a given bugreport and cancel its notification.
+ */
+ private void stopProgress(int pid) {
synchronized (mProcesses) {
if (mProcesses.indexOfKey(pid) < 0) {
Log.w(TAG, "PID not watched: " + pid);
@@ -340,11 +358,6 @@
}
if (DEBUG) Log.v(TAG, "stopProgress(" + pid + "): cancel notification");
NotificationManager.from(getApplicationContext()).cancel(TAG, pid);
- if (intent != null) {
- // Bug report finished fine: send a new, different notification.
- if (DEBUG) Log.v(TAG, "stopProgress(" + pid + "): finish bug report");
- onBugreportFinished(pid, intent);
- }
}
/**
@@ -353,7 +366,7 @@
private void cancel(int pid) {
Log.i(TAG, "Cancelling PID " + pid + " on user's request");
SystemProperties.set(CTL_STOP, BUGREPORT_SERVICE);
- stopProgress(pid, null);
+ stopProgress(pid);
}
/**
@@ -363,11 +376,19 @@
*/
private boolean pollProgress() {
synchronized (mProcesses) {
- if (mProcesses.size() == 0) {
+ final int total = mProcesses.size();
+ if (total == 0) {
Log.d(TAG, "No process to poll progress.");
}
- for (int i = 0; i < mProcesses.size(); i++) {
+ int activeProcesses = 0;
+ for (int i = 0; i < total; i++) {
final int pid = mProcesses.keyAt(i);
+ final BugreportInfo info = mProcesses.valueAt(i);
+ if (info.finished) {
+ if (DEBUG) Log.v(TAG, "Skipping finished process " + pid);
+ continue;
+ }
+ activeProcesses++;
final String progressKey = DUMPSTATE_PREFIX + pid + PROGRESS_SUFFIX;
final int progress = SystemProperties.getInt(progressKey, 0);
if (progress == 0) {
@@ -375,7 +396,6 @@
continue;
}
final int max = SystemProperties.getInt(DUMPSTATE_PREFIX + pid + MAX_SUFFIX, 0);
- final BugreportInfo info = mProcesses.valueAt(i);
final boolean maxChanged = max > 0 && max != info.max;
final boolean progressChanged = progress > 0 && progress != info.progress;
@@ -397,11 +417,12 @@
if (inactiveTime >= INACTIVITY_TIMEOUT) {
Log.w(TAG, "No progress update for process " + pid + " since "
+ info.getFormattedLastUpdate());
- stopProgress(info.pid, null);
+ stopProgress(info.pid);
}
}
}
- return true;
+ if (DEBUG) Log.v(TAG, "pollProgress() total=" + total + ", actives=" + activeProcesses);
+ return activeProcesses > 0;
}
}
@@ -421,37 +442,46 @@
private void onBugreportFinished(int pid, Intent intent) {
final Context context = getApplicationContext();
- final Configuration conf = context.getResources().getConfiguration();
- final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
- final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
+ BugreportInfo info;
+ synchronized (mProcesses) {
+ info = mProcesses.get(pid);
+ if (info == null) {
+ // Happens when BUGREPORT_FINISHED was received without a BUGREPORT_STARTED
+ Log.v(TAG, "Creating info for untracked pid " + pid);
+ info = new BugreportInfo(context, pid);
+ mProcesses.put(pid, info);
+ }
+ info.bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
+ info.screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
+ }
+ final Configuration conf = context.getResources().getConfiguration();
if ((conf.uiMode & Configuration.UI_MODE_TYPE_MASK) != Configuration.UI_MODE_TYPE_WATCH) {
- triggerLocalNotification(context, pid, bugreportFile, screenshotFile);
+ triggerLocalNotification(context, info);
}
}
/**
* Responsible for triggering a notification that allows the user to start a "share" intent with
- * the bug report. On watches we have other methods to allow the user to start this intent
+ * the bugreport. On watches we have other methods to allow the user to start this intent
* (usually by triggering it on another connected device); we don't need to display the
* notification in this case.
*/
- private static void triggerLocalNotification(final Context context, final int pid,
- final File bugreportFile, final File screenshotFile) {
- if (!bugreportFile.exists() || !bugreportFile.canRead()) {
- Log.e(TAG, "Could not read bugreport file " + bugreportFile);
+ private static void triggerLocalNotification(final Context context, final BugreportInfo info) {
+ if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
+ Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
Toast.makeText(context, context.getString(R.string.bugreport_unreadable_text),
Toast.LENGTH_LONG).show();
return;
}
- boolean isPlainText = bugreportFile.getName().toLowerCase().endsWith(".txt");
+ boolean isPlainText = info.bugreportFile.getName().toLowerCase().endsWith(".txt");
if (!isPlainText) {
// Already zipped, send it right away.
- sendBugreportNotification(context, pid, bugreportFile, screenshotFile);
+ sendBugreportNotification(context, info);
} else {
// Asynchronously zip the file first, then send it.
- sendZippedBugreportNotification(context, pid, bugreportFile, screenshotFile);
+ sendZippedBugreportNotification(context, info);
}
}
@@ -498,17 +528,27 @@
}
/**
- * Sends a bugreport notitication.
+ * Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE}
+ * intent, but issuing a warning dialog the first time.
*/
- private static void sendBugreportNotification(Context context, int pid, File bugreportFile,
- File screenshotFile) {
+ private void shareBugreport(int pid) {
+ final Context context = getApplicationContext();
+ final BugreportInfo info;
+ synchronized (mProcesses) {
+ info = mProcesses.get(pid);
+ if (info == null) {
+ // Should not happen, so log if it does...
+ Log.e(TAG, "INTERNAL ERROR: no info for PID " + pid + ": " + mProcesses);
+ return;
+ }
+ }
// Files are kept on private storage, so turn into Uris that we can
// grant temporary permissions for.
- final Uri bugreportUri = getUri(context, bugreportFile);
- final Uri screenshotUri = getUri(context, screenshotFile);
+ final Uri bugreportUri = getUri(context, info.bugreportFile);
+ final Uri screenshotUri = getUri(context, info.screenshotFile);
- Intent sendIntent = buildSendIntent(context, bugreportUri, screenshotUri);
- Intent notifIntent;
+ final Intent sendIntent = buildSendIntent(context, bugreportUri, screenshotUri);
+ final Intent notifIntent;
// Send through warning dialog by default
if (getWarningState(context, STATE_SHOW) == STATE_SHOW) {
@@ -518,32 +558,48 @@
}
notifIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Send the share intent...
+ context.startActivity(notifIntent);
+
+ // ... and stop watching this process.
+ stopProgress(pid);
+ }
+
+ /**
+ * Sends a notitication indicating the bugreport has finished so use can share it.
+ */
+ private static void sendBugreportNotification(Context context, BugreportInfo info) {
+ final Intent shareIntent = new Intent(INTENT_BUGREPORT_SHARE);
+ shareIntent.setClass(context, BugreportProgressService.class);
+ shareIntent.setAction(INTENT_BUGREPORT_SHARE);
+ shareIntent.putExtra(EXTRA_PID, info.pid);
+
final String title = context.getString(R.string.bugreport_finished_title);
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setContentTitle(title)
.setTicker(title)
.setContentText(context.getString(R.string.bugreport_finished_text))
- .setContentIntent(PendingIntent.getActivity(
- context, 0, notifIntent, PendingIntent.FLAG_CANCEL_CURRENT))
- .setAutoCancel(true)
+ .setContentIntent(PendingIntent.getService(context, 0, shareIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT))
+ .setDeleteIntent(newCancelIntent(context, info))
.setLocalOnly(true)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color));
- NotificationManager.from(context).notify(TAG, pid, builder.build());
+ NotificationManager.from(context).notify(TAG, info.pid, builder.build());
}
/**
* Sends a zipped bugreport notification.
*/
private static void sendZippedBugreportNotification(final Context context,
- final int pid, final File bugreportFile, final File screenshotFile) {
+ final BugreportInfo info) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
- File zippedFile = zipBugreport(bugreportFile);
- sendBugreportNotification(context, pid, zippedFile, screenshotFile);
+ info.bugreportFile = zipBugreport(info.bugreportFile);
+ sendBugreportNotification(context, info);
return null;
}
}.execute();
@@ -629,31 +685,31 @@
}
/**
- * Information about a bug report process while its in progress.
+ * Information about a bugreport process while its in progress.
*/
private static final class BugreportInfo {
private final Context context;
/**
- * {@code pid} of the {@code dumpstate} process generating the bug report.
+ * {@code pid} of the {@code dumpstate} process generating the bugreport.
*/
final int pid;
/**
- * Name of the bug report, will be used to rename the final files.
+ * Name of the bugreport, will be used to rename the final files.
* <p>
- * Initial value is the bug report filename reported by {@code dumpstate}, but user can
+ * Initial value is the bugreport filename reported by {@code dumpstate}, but user can
* change it later to a more meaningful name.
*/
String name;
/**
- * Maximum progress of the bug report generation.
+ * Maximum progress of the bugreport generation.
*/
int max;
/**
- * Current progress of the bug report generation.
+ * Current progress of the bugreport generation.
*/
int progress;
@@ -662,6 +718,24 @@
*/
long lastUpdate = System.currentTimeMillis();
+ /**
+ * Path of the main bugreport file.
+ */
+ File bugreportFile;
+
+ /**
+ * Path of the screenshot file.
+ */
+ File screenshotFile;
+
+ /**
+ * Whether dumpstate sent an intent informing it has finished.
+ */
+ boolean finished;
+
+ /**
+ * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
+ */
BugreportInfo(Context context, int pid, String name, int max) {
this.context = context;
this.pid = pid;
@@ -669,6 +743,15 @@
this.max = max;
}
+ /**
+ * Constructor for untracked bugreports - typically called upon receiving BUGREPORT_FINISHED
+ * without a previous call to BUGREPORT_STARTED.
+ */
+ BugreportInfo(Context context, int pid) {
+ this(context, pid, null, 0);
+ this.finished = true;
+ }
+
String getFormattedLastUpdate() {
return DateUtils.formatDateTime(context, lastUpdate,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME);
@@ -677,8 +760,10 @@
@Override
public String toString() {
final float percent = ((float) progress * 100 / max);
- return "Progress for " + name + " (pid=" + pid + "): " + progress + "/" + max
- + " (" + percent + "%) Last update: " + getFormattedLastUpdate();
+ return "pid: " + pid + ", name: " + name + ", finished: " + finished
+ + "\n\tfile: " + bugreportFile + "\n\tscreenshot: " + screenshotFile
+ + "\n\tprogress: " + progress + "/" + max + "(" + percent + ")"
+ + "\n\tlast_update: " + getFormattedLastUpdate();
}
}
}
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 0e31cdf..cd0fcfe 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -40,6 +40,8 @@
import java.util.zip.ZipOutputStream;
import libcore.io.Streams;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningServiceInfo;
import android.app.Instrumentation;
import android.app.NotificationManager;
import android.content.Context;
@@ -51,6 +53,7 @@
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMultipleListener;
@@ -71,12 +74,13 @@
* <p>
* <strong>NOTE</strong>: these tests only work if the device is unlocked.
*/
+@LargeTest
public class BugreportReceiverTest extends InstrumentationTestCase {
private static final String TAG = "BugreportReceiverTest";
// Timeout for UI operations, in milliseconds.
- private static final int TIMEOUT = 1000;
+ private static final int TIMEOUT = (int) BugreportProgressService.POLLING_FREQUENCY * 4;
private static final String ROOT_DIR = "/data/data/com.android.shell/files/bugreports";
private static final String BUGREPORT_FILE = "test_bugreport.txt";
@@ -130,7 +134,8 @@
Bundle extras = sendBugreportFinishedIntent(42, PLAIN_TEXT_PATH, SCREENSHOT_PATH);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT);
- // TODO: assert service is down
+ String service = BugreportProgressService.class.getName();
+ assertFalse("Service '" + service + "' is still running", isServiceRunning(service));
}
public void testBugreportFinished_withWarning() throws Exception {
@@ -306,6 +311,17 @@
fail("Did not find entry '" + entryName + "' on file '" + uri + "'");
}
+ private boolean isServiceRunning(String name) {
+ ActivityManager manager = (ActivityManager) mContext
+ .getSystemService(Context.ACTIVITY_SERVICE);
+ for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
+ if (service.service.getClassName().equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static void createTextFile(String path, String content) throws IOException {
Log.v(TAG, "createFile(" + path + ")");
try (Writer writer = new BufferedWriter(new OutputStreamWriter(
diff --git a/packages/Shell/tests/src/com/android/shell/UiBot.java b/packages/Shell/tests/src/com/android/shell/UiBot.java
index 5e8bab1..7d37137 100644
--- a/packages/Shell/tests/src/com/android/shell/UiBot.java
+++ b/packages/Shell/tests/src/com/android/shell/UiBot.java
@@ -118,7 +118,8 @@
// TODO: UI Automator should provide such logic.
public void chooseActivity(String name) {
// First check if the activity is the default option.
- String shareText = String.format("Share with %s", name);
+ String shareText = "Share with " + name;
+ Log.v(TAG, "Waiting for ActivityChooser text: '" + shareText + "'");
boolean gotIt = mDevice.wait(Until.hasObject(By.text(shareText)), mTimeout);
if (gotIt) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 2e65656..51b84f5 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -136,6 +136,9 @@
android:protectionLevel="signature" />
<uses-permission android:name="com.android.systemui.permission.SELF" />
+ <!-- Adding Quick Settings tiles -->
+ <uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" />
+
<application
android:name=".SystemUIApplication"
android:persistent="true"
diff --git a/packages/SystemUI/res/color/remote_input_send.xml b/packages/SystemUI/res/color/remote_input_send.xml
new file mode 100644
index 0000000..fe2ffaa
--- /dev/null
+++ b/packages/SystemUI/res/color/remote_input_send.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="true" android:color="@android:color/white" />
+ <item android:color="#4dffffff" /> <!-- 30% white -->
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/remote_input_text.xml b/packages/SystemUI/res/color/remote_input_text.xml
new file mode 100644
index 0000000..11ce0b7
--- /dev/null
+++ b/packages/SystemUI/res/color/remote_input_text.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="true" android:color="@android:color/white" />
+ <item android:color="#99ffffff" /> <!-- 60% white -->
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/header_dot.xml b/packages/SystemUI/res/drawable/header_dot.xml
new file mode 100644
index 0000000..568a9c2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/header_dot.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 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.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <solid
+ android:color="#FFFFFF"/>
+
+ <size
+ android:width="3dp"
+ android:height="3dp"/>
+</shape>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 1873168..bb37b83 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -19,7 +19,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/qs_background_primary"
- android:paddingTop="8dp"
android:paddingBottom="8dp"
android:elevation="2dp">
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 8124eb7..a995ec7 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -33,16 +33,6 @@
android:focusable="true"
>
- <com.android.systemui.qs.QuickQSPanel
- android:id="@+id/quick_qs_panel"
- android:background="#0000"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentEnd="true"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_marginEnd="12dp" />
-
<LinearLayout
android:id="@+id/expanded_group"
android:layout_width="wrap_content"
@@ -52,7 +42,8 @@
android:clipToPadding="false"
android:orientation="horizontal"
android:layout_alignParentEnd="true"
- android:layout_marginEnd="12dp">
+ android:layout_marginTop="30dp"
+ android:layout_marginEnd="16dp">
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/settings_button_container"
@@ -87,48 +78,76 @@
android:tint="@android:color/white" />
</LinearLayout>
- <FrameLayout
- android:id="@+id/date_group"
+ <TextView
+ android:id="@+id/header_emergency_calls_only"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:paddingTop="8dp"
+ android:visibility="gone"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
+ android:text="@*android:string/emergency_calls_only"
+ android:singleLine="true"
+ android:gravity="center_vertical" />
+
+ <LinearLayout
+ android:id="@+id/date_time_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/clock_collapsed_bottom_margin"
- android:layout_alignParentBottom="true">
- <com.android.systemui.statusbar.policy.DateView android:id="@+id/date_collapsed"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:orientation="horizontal">
+
+ <include layout="@layout/split_clock_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
+ android:layout_marginTop="2dp"
+ android:id="@+id/clock" />
+
+ <com.android.systemui.statusbar.policy.DateView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="6dp"
+ android:layout_marginTop="8dp"
+ android:layout_alignParentTop="true"
+ android:drawableStart="@drawable/header_dot"
+ android:drawablePadding="6dp"
android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+ android:textSize="@dimen/qs_time_collapsed_size"
+ systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+
+ <com.android.systemui.statusbar.AlphaOptimizedButton
+ android:id="@+id/alarm_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:drawablePadding="6dp"
+ android:drawableStart="@drawable/ic_access_alarms_small"
+ android:textColor="#64ffffff"
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
- android:layout_below="@id/clock"
- systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm"
- />
- </FrameLayout>
+ android:minHeight="36dp"
+ android:paddingStart="6dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone" />
+ </LinearLayout>
- <include layout="@layout/split_clock_view"
- android:layout_width="wrap_content"
+ <com.android.systemui.qs.QuickQSPanel
+ android:id="@+id/quick_qs_panel"
+ android:background="#0000"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_above="@id/date_group"
- android:id="@+id/clock"
- />
-
- <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_toEndOf="@id/date_group"
- android:layout_marginBottom="4dp"
- android:drawablePadding="6dp"
- android:drawableStart="@drawable/ic_access_alarms_small"
- android:textColor="#64ffffff"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
- android:paddingEnd="6dp"
- android:paddingStart="6dp"
- android:paddingTop="16dp"
- android:paddingBottom="16dp"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone"
- />
+ android:layout_marginTop="30dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_alignParentEnd="true"
+ android:clipChildren="false"
+ android:clipToPadding="false" />
<include
android:id="@+id/qs_detail_header"
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 74092c1..83cfc76 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -36,7 +36,8 @@
android:paddingEnd="12dp"
android:gravity="start|center_vertical"
android:textAppearance="?android:attr/textAppearance"
- android:textColor="#deffffff"
+ android:textColor="@color/remote_input_text"
+ android:textColorHint="@color/remote_input_hint"
android:textSize="16sp"
android:background="@null"
android:singleLine="true"
@@ -58,8 +59,8 @@
android:paddingBottom="12dp"
android:id="@+id/remote_input_send"
android:src="@drawable/ic_send"
- android:tint="@android:color/white"
- android:tintMode="src_atop"
+ android:tint="@color/remote_input_send"
+ android:tintMode="src_in"
android:background="@drawable/ripple_drawable" />
<ProgressBar
diff --git a/packages/SystemUI/res/layout/split_clock_view.xml b/packages/SystemUI/res/layout/split_clock_view.xml
index d1269da..ae5136f 100644
--- a/packages/SystemUI/res/layout/split_clock_view.xml
+++ b/packages/SystemUI/res/layout/split_clock_view.xml
@@ -27,6 +27,7 @@
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+ android:textSize="@dimen/qs_time_collapsed_size"
/>
<TextClock
android:id="@+id/am_pm_view"
diff --git a/packages/SystemUI/res/values-h560dp/config.xml b/packages/SystemUI/res/values-h560dp/config.xml
index f210d7b..8b576b9 100644
--- a/packages/SystemUI/res/values-h560dp/config.xml
+++ b/packages/SystemUI/res/values-h560dp/config.xml
@@ -18,6 +18,6 @@
<resources>
<!-- The maximum number of items to be displayed in quick settings -->
- <integer name="quick_settings_detail_max_item_count">8</integer>
+ <integer name="quick_settings_detail_max_item_count">6</integer>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 5618e9b..7aff02d 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -144,4 +144,7 @@
<color name="docked_divider_background">#ff000000</color>
<color name="docked_divider_handle">#ffffff</color>
+
+ <color name="default_remote_input_background">@*android:color/notification_default_color</color>
+ <color name="remote_input_hint">#4dffffff</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index a0052ce..40e8b50 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -103,7 +103,7 @@
<!-- The default tiles to display in QuickSettings -->
<string name="quick_settings_tiles_default" translatable="false">
- wifi,bt,inversion,dnd,cell,airplane,rotation,flashlight,location,cast,hotspot
+ wifi,bt,flashlight,dnd,cell,battery,rotation,airplane,location,cast
</string>
<!-- The tiles to display in QuickSettings -->
@@ -117,7 +117,7 @@
<integer name="quick_settings_brightness_dialog_long_timeout">4000</integer>
<!-- The maximum number of items to be displayed in quick settings -->
- <integer name="quick_settings_detail_max_item_count">7</integer>
+ <integer name="quick_settings_detail_max_item_count">5</integer>
<!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
<bool name="config_show4GForLTE">true</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 65b0c45..4f070d6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -45,8 +45,11 @@
<!-- Height of a large notification in the status bar -->
<dimen name="notification_max_height">276dp</dimen>
- <!-- Height of a medium notification in the status bar -->
- <dimen name="notification_mid_height">128dp</dimen>
+ <!-- Height of a heads up notification in the status bar for legacy custom views -->
+ <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
+
+ <!-- Height of a heads up notification in the status bar -->
+ <dimen name="notification_max_heads_up_height">140dp</dimen>
<!-- Height of a the summary ("more card") notification on keyguard. -->
<dimen name="notification_summary_height">44dp</dimen>
@@ -95,7 +98,7 @@
<dimen name="close_handle_underlap">32dp</dimen>
<!-- Height of the status bar header bar -->
- <dimen name="status_bar_header_height">60dp</dimen>
+ <dimen name="status_bar_header_height">90dp</dimen>
<!-- Height of the status bar header bar when expanded -->
<dimen name="status_bar_header_height_expanded">116dp</dimen>
@@ -133,6 +136,7 @@
<dimen name="qs_quick_actions_padding">25dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
+ <dimen name="qs_date_anim_translation">44.5dp</dimen>
<dimen name="qs_page_indicator_size">12dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 8dcf8a7..e31927e 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -18,25 +18,10 @@
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:title="@string/system_ui_tuner">
- <PreferenceScreen
- android:title="@string/quick_settings">
-
- <PreferenceCategory
- android:title="@string/experimental">
-
- <com.android.systemui.tuner.TunerSwitch
- android:key="qs_show_brightness"
- android:title="@string/show_brightness"
- sysui:defValue="true" />
-
- <com.android.systemui.tuner.QSPagingSwitch
- android:key="qs_paged_panel"
- android:title="@string/qs_paging" />
-
- </PreferenceCategory>
-
- </PreferenceScreen>
-
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="qs_show_brightness"
+ android:title="@string/show_brightness"
+ sysui:defValue="true" />
<PreferenceScreen
android:title="@string/status_bar" >
diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
index 5b8d3d6..c9ba885 100644
--- a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.content.Context;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
@@ -35,13 +36,19 @@
private final Paint mDarkPaint = new Paint();
private final Interpolator mLinearOutSlowInInterpolator;
- private final ArrayList<View> mTargets;
private final ColorMatrix mMatrix = new ColorMatrix();
private final ColorMatrix mGrayscaleMatrix = new ColorMatrix();
private final long mFadeDuration;
+ private final ArrayList<View> mTargets = new ArrayList<>();
- public ViewInvertHelper(View target, long fadeDuration) {
- this(constructArray(target), fadeDuration);
+ public ViewInvertHelper(View v, long fadeDuration) {
+ this(v.getContext(), fadeDuration);
+ addTarget(v);
+ }
+ public ViewInvertHelper(Context context, long fadeDuration) {
+ mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ android.R.interpolator.linear_out_slow_in);
+ mFadeDuration = fadeDuration;
}
private static ArrayList<View> constructArray(View target) {
@@ -50,11 +57,12 @@
return views;
}
- public ViewInvertHelper(ArrayList<View> targets, long fadeDuration) {
- mTargets = targets;
- mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(mTargets.get(0).getContext(),
- android.R.interpolator.linear_out_slow_in);
- mFadeDuration = fadeDuration;
+ public void clearTargets() {
+ mTargets.clear();
+ }
+
+ public void addTarget(View target) {
+ mTargets.add(target);
}
public void fade(final boolean invert, long delay) {
@@ -112,4 +120,12 @@
mMatrix.preConcat(mGrayscaleMatrix);
mDarkPaint.setColorFilter(new ColorMatrixColorFilter(mMatrix));
}
+
+ public void setInverted(boolean invert, boolean fade, long delay) {
+ if (fade) {
+ fade(invert, delay);
+ } else {
+ update(invert);
+ }
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
index cb6708e..699273a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
@@ -98,7 +98,7 @@
}
}
- setMeasuredDimension(width, getDefaultSize(totalHeight, heightMeasureSpec));
+ setMeasuredDimension(width, resolveSizeAndState(totalHeight, heightMeasureSpec, 0));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5d3fbe2..ae8542a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -19,14 +19,12 @@
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
-import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
-import android.provider.Settings;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -36,7 +34,6 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
@@ -56,7 +53,6 @@
public class QSPanel extends FrameLayout implements Tunable {
public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
- public static final String QS_THE_NEW_QS = "qs_paged_panel";
protected final Context mContext;
protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
@@ -102,20 +98,25 @@
updateDetailText();
mDetail.setVisibility(GONE);
mDetail.setClickable(true);
- mBrightnessView = LayoutInflater.from(context).inflate(
- R.layout.quick_settings_brightness_dialog, this, false);
- mFooter = new QSFooter(this, context);
addView(mDetail);
mQsContainer = new LinearLayout(mContext);
mQsContainer.setOrientation(LinearLayout.VERTICAL);
mQsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
-
addView(mQsContainer);
+ mBrightnessView = LayoutInflater.from(context).inflate(
+ R.layout.quick_settings_brightness_dialog, this, false);
mQsContainer.addView(mBrightnessView);
+
+ mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
+ R.layout.qs_paged_tile_layout, mQsContainer, false);
+ mQsContainer.addView((View) mTileLayout);
+
+ mFooter = new QSFooter(this, context);
mQsContainer.addView(mFooter.getView());
+
mClipper = new QSDetailClipper(mDetail);
updateResources();
@@ -136,7 +137,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- TunerService.get(mContext).addTunable(this, QS_SHOW_BRIGHTNESS, QS_THE_NEW_QS);
+ TunerService.get(mContext).addTunable(this, QS_SHOW_BRIGHTNESS);
}
@Override
@@ -150,36 +151,15 @@
if (QS_SHOW_BRIGHTNESS.equals(key)) {
mBrightnessView.setVisibility(newValue == null || Integer.parseInt(newValue) != 0
? VISIBLE : GONE);
- } else if (QS_THE_NEW_QS.equals(key)) {
- boolean theNewQs = newValue != null && Integer.parseInt(newValue) != 0;
- if (mTileLayout != null) {
- for (int i = 0; i < mRecords.size(); i++) {
- mTileLayout.removeTile(mRecords.get(i));
- }
- mQsContainer.removeView((View) mTileLayout);
- }
- int layout = theNewQs
- ? R.layout.qs_paged_tile_layout : R.layout.qs_tile_layout;
- mTileLayout =
- (QSTileLayout) LayoutInflater.from(mContext).inflate(layout, mQsContainer, false);
- mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
- for (int i = 0; i < mRecords.size(); i++) {
- mTileLayout.addTile(mRecords.get(i));
- }
- if (theNewQs) {
- mCustomizePanel = (QSCustomizer) LayoutInflater.from(mContext)
- .inflate(R.layout.qs_customize_panel, null);
- mCustomizePanel.setHost(mHost);
- } else {
- if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
- mCustomizePanel.hide(mCustomizePanel.getWidth() / 2,
- mCustomizePanel.getHeight() / 2);
- }
- mCustomizePanel = null;
- }
}
}
+ protected void createCustomizePanel() {
+ mCustomizePanel = (QSCustomizer) LayoutInflater.from(mContext)
+ .inflate(R.layout.qs_customize_panel, null);
+ mCustomizePanel.setHost(mHost);
+ }
+
private void updateDetailText() {
mDetailDoneButton.setText(R.string.quick_settings_done);
mDetailSettingsButton.setText(R.string.quick_settings_more_settings);
@@ -200,6 +180,7 @@
public void setHost(QSTileHost host) {
mHost = host;
mFooter.setHost(host);
+ createCustomizePanel();
}
public QSTileHost getHost() {
@@ -608,9 +589,4 @@
int getOffsetTop(TileRecord tile);
void updateResources();
}
-
- public static boolean isTheNewQS(Context context) {
- return Settings.Secure.getIntForUser(context.getContentResolver(), QS_THE_NEW_QS,
- ActivityManager.getCurrentUser(), 0) != 0;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index fe8ce9b..bda4675 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -128,9 +128,10 @@
}
private LayoutParams generateLayoutParams() {
- int size =
- mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
- LayoutParams lp = new LayoutParams(size, size);
+ int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
+ LayoutParams lp = new LayoutParams(0, size);
+ lp.weight = 1;
+ lp.gravity = Gravity.CENTER;
return lp;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
index 8dda9ba..87c29735 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
@@ -81,6 +81,11 @@
}
}
+ @Override
+ protected void createCustomizePanel() {
+ // Already in CustomizePanel.
+ }
+
public void tileSelected(QSTile<?> tile, ClipData currentClip) {
String sourceSpec = getSpec(currentClip);
String destSpec = tile.getTileSpec();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index baad370..4a7d67f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -37,9 +37,7 @@
import android.widget.ListView;
import android.widget.Toolbar;
import android.widget.Toolbar.OnMenuItemClickListener;
-
import com.android.systemui.R;
-import com.android.systemui.SystemUIApplication;
import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.qs.QSTile.Host.Callback;
import com.android.systemui.qs.customize.DropButton.OnDropListener;
@@ -80,14 +78,13 @@
public QSCustomizer(Context context, AttributeSet attrs) {
super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
- mPhoneStatusBar = ((SystemUIApplication) mContext.getApplicationContext())
- .getComponent(PhoneStatusBar.class);
mClipper = new QSDetailClipper(this);
}
public void setHost(QSTileHost host) {
mHost = host;
mHost.addCallback(this);
+ mPhoneStatusBar = host.getPhoneStatusBar();
mQsPanel.setTiles(mHost.getTiles());
mQsPanel.setHost(mHost);
mQsPanel.setSavedTiles();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 5fb76c8..6c7b337 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -86,6 +86,14 @@
}
@Override
+ protected void handleSecondaryClick() {
+ boolean dataEnabled = mDataController.isMobileDataSupported()
+ && mDataController.isMobileDataEnabled();
+ MetricsLogger.action(mContext, MetricsLogger.QS_CELLULAR_TOGGLE, !dataEnabled);
+ mDataController.setMobileDataEnabled(!dataEnabled);
+ }
+
+ @Override
protected void handleUpdateState(SignalState state, Object arg) {
CallbackInfo cb = (CallbackInfo) arg;
if (cb == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 879624e..cc6a29a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -127,8 +127,8 @@
public static final boolean ENABLE_REMOTE_INPUT =
SystemProperties.getBoolean("debug.enable_remote_input", true);
- public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
- && SystemProperties.getBoolean("debug.child_notifs", false);
+ public static final boolean ENABLE_CHILD_NOTIFICATIONS
+ = SystemProperties.getBoolean("debug.child_notifs", true);
protected static final int MSG_SHOW_RECENT_APPS = 1019;
protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -1299,7 +1299,6 @@
// Since the number of notifications is determined based on the height of the view, we
// need to update them.
updateRowStates();
- mStackScroller.onHeightChanged(null, false);
}
}
@@ -1603,7 +1602,7 @@
private void applyRemoteInput(final Entry entry) {
if (!ENABLE_REMOTE_INPUT) return;
- RemoteInput remoteInput = null;
+ boolean hasRemoteInput = false;
Notification.Action[] actions = entry.notification.getNotification().actions;
if (actions != null) {
@@ -1611,7 +1610,7 @@
if (a.getRemoteInputs() != null) {
for (RemoteInput ri : a.getRemoteInputs()) {
if (ri.getAllowFreeFormInput()) {
- remoteInput = ri;
+ hasRemoteInput = true;
break;
}
}
@@ -1619,34 +1618,50 @@
}
}
- // See if we have somewhere to put that remote input
- if (remoteInput != null) {
- View bigContentView = entry.getExpandedContentView();
- if (bigContentView != null) {
- inflateRemoteInput(bigContentView, entry);
- }
- View headsUpContentView = entry.getHeadsUpContentView();
- if (headsUpContentView != null) {
- inflateRemoteInput(headsUpContentView, entry);
- }
+ View bigContentView = entry.getExpandedContentView();
+ if (bigContentView != null) {
+ applyRemoteInput(bigContentView, entry, hasRemoteInput);
+ }
+ View headsUpContentView = entry.getHeadsUpContentView();
+ if (headsUpContentView != null) {
+ applyRemoteInput(headsUpContentView, entry, hasRemoteInput);
}
}
- private RemoteInputView inflateRemoteInput(View view, Entry entry) {
+ private RemoteInputView applyRemoteInput(View view, Entry entry, boolean hasRemoteInput) {
View actionContainerCandidate = view.findViewById(
com.android.internal.R.id.actions_container);
if (actionContainerCandidate instanceof FrameLayout) {
- ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
- RemoteInputView riv = inflateRemoteInputView(actionContainer, entry);
- if (riv != null) {
- riv.setVisibility(View.INVISIBLE);
- actionContainer.addView(riv, new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT)
- );
- riv.setBackgroundColor(entry.notification.getNotification().color);
- return riv;
+ RemoteInputView existing = (RemoteInputView)
+ view.findViewWithTag(RemoteInputView.VIEW_TAG);
+
+ if (hasRemoteInput) {
+ if (existing != null) {
+ existing.onNotificationUpdate();
+ return existing;
+ }
+
+ ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
+ RemoteInputView riv = inflateRemoteInputView(actionContainer, entry);
+ if (riv != null) {
+ riv.setVisibility(View.INVISIBLE);
+ actionContainer.addView(riv, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT)
+ );
+ int color = entry.notification.getNotification().color;
+ if (color == Notification.COLOR_DEFAULT) {
+ color = mContext.getColor(R.color.default_remote_input_background);
+ }
+ riv.setBackgroundColor(color);
+ return riv;
+ }
+ } else {
+ if (existing != null) {
+ existing.onNotificationUpdate();
+ return null;
+ }
}
}
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 5c79c7d..bd143ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -51,6 +51,8 @@
private static final int COLORED_DIVIDER_ALPHA = 0x7B;
private final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
private final int mNotificationMinHeightLegacy;
+ private final int mMaxHeadsUpHeightLegacy;
+ private final int mMaxHeadsUpHeight;
private final int mNotificationMinHeight;
private final int mNotificationMaxHeight;
private int mRowMinHeight;
@@ -95,6 +97,7 @@
private boolean mIsHeadsUp;
private boolean mLastChronometerRunning = true;
private NotificationHeaderView mNotificationHeader;
+ private NotificationViewWrapper mNotificationHeaderWrapper;
private ViewStub mChildrenContainerStub;
private NotificationGroupManager mGroupManager;
private boolean mChildrenExpanded;
@@ -218,10 +221,15 @@
boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
int minHeight = customView && beforeN && !mIsSummaryWithChildren ?
mNotificationMinHeightLegacy : mNotificationMinHeight;
+ boolean headsUpCustom = getPrivateLayout().getHeadsUpChild() != null &&
+ getPrivateLayout().getHeadsUpChild().getId()
+ != com.android.internal.R.id.status_bar_latest_event_content;
+ int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
+ : mMaxHeadsUpHeight;
mRowMinHeight = minHeight;
mMaxViewHeight = mNotificationMaxHeight;
- mPrivateLayout.setSmallHeight(mRowMinHeight);
- mPublicLayout.setSmallHeight(mRowMinHeight);
+ mPrivateLayout.setHeights(mRowMinHeight, headsUpheight);
+ mPublicLayout.setHeights(mRowMinHeight, headsUpheight);
}
public StatusBarNotification getStatusBarNotification() {
@@ -385,6 +393,9 @@
}
public int getHeadsUpHeight() {
+ if (mIsSummaryWithChildren) {
+ return mChildrenContainer.getIntrinsicHeight();
+ }
return mHeadsUpHeight;
}
@@ -462,6 +473,10 @@
R.dimen.notification_min_height);
mNotificationMaxHeight = getResources().getDimensionPixelSize(
R.dimen.notification_max_height);
+ mMaxHeadsUpHeightLegacy = getResources().getDimensionPixelSize(
+ R.dimen.notification_max_heads_up_height_legacy);
+ mMaxHeadsUpHeight = getResources().getDimensionPixelSize(
+ R.dimen.notification_max_heads_up_height);
}
/**
@@ -570,6 +585,10 @@
if (showing != null) {
showing.setDark(dark, fade, delay);
}
+ if (mIsSummaryWithChildren) {
+ mChildrenContainer.setDark(dark, fade, delay);
+ mNotificationHeaderWrapper.setDark(dark, fade, delay);
+ }
}
public boolean isExpandable() {
@@ -669,6 +688,9 @@
mOnKeyguard = onKeyguard;
logExpansionEvent(false, wasExpanded);
if (wasExpanded != isExpanded()) {
+ if (mIsSummaryWithChildren) {
+ mChildrenContainer.updateGroupOverflow();
+ }
notifyHeightChanged(false /* needsAnimation */);
}
}
@@ -967,9 +989,12 @@
com.android.internal.R.id.expand_button);
expandButton.setVisibility(VISIBLE);
mNotificationHeader.setOnClickListener(mExpandClickListener);
+ mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
+ mNotificationHeader);
addView(mNotificationHeader);
} else {
header.reapply(getContext(), mNotificationHeader);
+ mNotificationHeaderWrapper.notifyContentUpdated();
}
updateHeaderExpandButton();
updateChildrenHeaderAppearance();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index da01d54..2944c4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -52,7 +52,6 @@
private static final int VISIBLE_TYPE_SINGLELINE = 3;
private final Rect mClipBounds = new Rect();
- private final int mHeadsUpHeight;
private final int mRoundRectRadius;
private final Interpolator mLinearInterpolator = new LinearInterpolator();
private final boolean mRoundRectClippingEnabled;
@@ -77,6 +76,7 @@
private boolean mShowingLegacyBackground;
private boolean mIsChildInGroup;
private int mSmallHeight;
+ private int mHeadsUpHeight;
private StatusBarNotification mStatusBarNotification;
private NotificationGroupManager mGroupManager;
@@ -103,7 +103,6 @@
super(context, attrs);
mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
- mHeadsUpHeight = getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
mRoundRectRadius = getResources().getDimensionPixelSize(
R.dimen.notification_material_rounded_rect_radius);
mRoundRectClippingEnabled = getResources().getBoolean(
@@ -112,8 +111,9 @@
setOutlineProvider(mOutlineProvider);
}
- public void setSmallHeight(int smallHeight) {
+ public void setHeights(int smallHeight, int headsUpMaxHeight) {
mSmallHeight = smallHeight;
+ mHeadsUpHeight = headsUpMaxHeight;
}
@Override
@@ -150,7 +150,7 @@
ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
if (layoutParams.height >= 0) {
// An actual height is set
- size = Math.min(maxSize, layoutParams.height);
+ size = Math.min(size, layoutParams.height);
}
mHeadsUpChild.measure(widthMeasureSpec,
MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
@@ -283,10 +283,10 @@
}
public int getMaxHeight() {
- if (mIsHeadsUp && mHeadsUpChild != null) {
- return mHeadsUpChild.getHeight();
- } else if (mExpandedChild != null) {
+ if (mExpandedChild != null) {
return mExpandedChild.getHeight();
+ } else if (mIsHeadsUp && mHeadsUpChild != null) {
+ return mHeadsUpChild.getHeight();
}
return mSmallHeight;
}
@@ -457,6 +457,9 @@
if (mDark == dark || mContractedChild == null) return;
mDark = dark;
mContractedWrapper.setDark(dark && !mShowingLegacyBackground, fade, delay);
+ if (mSingleLineView != null) {
+ mSingleLineView.setDark(dark, fade, delay);
+ }
}
public void setHeadsUp(boolean headsUp) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderViewWrapper.java
new file mode 100644
index 0000000..ddad2e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderViewWrapper.java
@@ -0,0 +1,239 @@
+/*
+ * 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.systemui.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.view.NotificationHeaderView;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+import java.util.ArrayList;
+
+/**
+ * Wraps a notification header view.
+ */
+public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
+
+ private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
+ private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
+ 0, PorterDuff.Mode.SRC_ATOP);
+ private final int mIconDarkAlpha;
+ private final int mIconDarkColor = 0xffffffff;
+ protected final Interpolator mLinearOutSlowInInterpolator;
+ protected final ViewInvertHelper mInvertHelper;
+
+ protected int mColor;
+ private ImageView mIcon;
+
+ private ImageView mExpandButton;
+ private NotificationHeaderView mNotificationHeader;
+
+ protected NotificationHeaderViewWrapper(Context ctx, View view) {
+ super(view);
+ mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
+ mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
+ android.R.interpolator.linear_out_slow_in);
+ mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
+ resolveHeaderViews();
+ }
+
+ protected void resolveHeaderViews() {
+ mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
+ mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button);
+ mColor = resolveColor(mExpandButton);
+ mNotificationHeader = (NotificationHeaderView) mView.findViewById(
+ com.android.internal.R.id.notification_header);
+ for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
+ View child = mNotificationHeader.getChildAt(i);
+ if (child != mIcon) {
+ mInvertHelper.addTarget(child);
+ }
+ }
+ }
+
+ private int resolveColor(ImageView icon) {
+ if (icon != null && icon.getDrawable() != null) {
+ ColorFilter filter = icon.getDrawable().getColorFilter();
+ if (filter instanceof PorterDuffColorFilter) {
+ return ((PorterDuffColorFilter) filter).getColor();
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public void notifyContentUpdated() {
+ mInvertHelper.clearTargets();
+ // Reinspect the notification.
+ resolveHeaderViews();
+ }
+
+ @Override
+ public void setDark(boolean dark, boolean fade, long delay) {
+ if (fade) {
+ mInvertHelper.fade(dark, delay);
+ } else {
+ mInvertHelper.update(dark);
+ }
+ if (mIcon != null) {
+ boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
+ != NotificationHeaderView.NO_COLOR;
+ if (fade) {
+ if (hadColorFilter) {
+ fadeIconColorFilter(mIcon, dark, delay);
+ fadeIconAlpha(mIcon, dark, delay);
+ } else {
+ fadeGrayscale(mIcon, dark, delay);
+ }
+ } else {
+ if (hadColorFilter) {
+ updateIconColorFilter(mIcon, dark);
+ updateIconAlpha(mIcon, dark);
+ } else {
+ updateGrayscale(mIcon, dark);
+ }
+ }
+ }
+ }
+
+ protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
+ boolean dark, long delay, Animator.AnimatorListener listener) {
+ float startIntensity = dark ? 0f : 1f;
+ float endIntensity = dark ? 1f : 0f;
+ ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+ animator.addUpdateListener(updateListener);
+ animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+ animator.setInterpolator(mLinearOutSlowInInterpolator);
+ animator.setStartDelay(delay);
+ if (listener != null) {
+ animator.addListener(listener);
+ }
+ animator.start();
+ }
+
+ private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
+ startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateIconColorFilter(target, (Float) animation.getAnimatedValue());
+ }
+ }, dark, delay, null /* listener */);
+ }
+
+ private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
+ startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = (float) animation.getAnimatedValue();
+ target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
+ }
+ }, dark, delay, null /* listener */);
+ }
+
+ protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
+ startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateGrayscaleMatrix((float) animation.getAnimatedValue());
+ target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+ }
+ }, dark, delay, new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!dark) {
+ target.setColorFilter(null);
+ }
+ }
+ });
+ }
+
+ private void updateIconColorFilter(ImageView target, boolean dark) {
+ updateIconColorFilter(target, dark ? 1f : 0f);
+ }
+
+ private void updateIconColorFilter(ImageView target, float intensity) {
+ int color = interpolateColor(mColor, mIconDarkColor, intensity);
+ mIconColorFilter.setColor(color);
+ Drawable iconDrawable = target.getDrawable();
+
+ // Also, the notification might have been modified during the animation, so background
+ // might be null here.
+ if (iconDrawable != null) {
+ iconDrawable.mutate().setColorFilter(mIconColorFilter);
+ }
+ }
+
+ private void updateIconAlpha(ImageView target, boolean dark) {
+ target.setImageAlpha(dark ? mIconDarkAlpha : 255);
+ }
+
+ protected void updateGrayscale(ImageView target, boolean dark) {
+ if (dark) {
+ updateGrayscaleMatrix(1f);
+ target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+ } else {
+ target.setColorFilter(null);
+ }
+ }
+
+ @Override
+ public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
+ mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
+ mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
+ }
+
+ private void updateGrayscaleMatrix(float intensity) {
+ mGrayscaleColorMatrix.setSaturation(1 - intensity);
+ }
+
+ private static int interpolateColor(int source, int target, float t) {
+ int aSource = Color.alpha(source);
+ int rSource = Color.red(source);
+ int gSource = Color.green(source);
+ int bSource = Color.blue(source);
+ int aTarget = Color.alpha(target);
+ int rTarget = Color.red(target);
+ int gTarget = Color.green(target);
+ int bTarget = Color.blue(target);
+ return Color.argb(
+ (int) (aSource * (1f - t) + aTarget * t),
+ (int) (rSource * (1f - t) + rTarget * t),
+ (int) (gSource * (1f - t) + gTarget * t),
+ (int) (bSource * (1f - t) + bTarget * t));
+ }
+
+ @Override
+ public NotificationHeaderView getNotificationHeader() {
+ return mNotificationHeader;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
index fb0a419..77e8c55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
@@ -16,74 +16,31 @@
package com.android.systemui.statusbar;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.view.MotionEvent;
-import android.view.NotificationHeaderView;
import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.ViewInvertHelper;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
-
-import java.util.ArrayList;
/**
* Wraps a notification view inflated from a template.
*/
-public class NotificationTemplateViewWrapper extends NotificationViewWrapper {
+public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper {
- private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
- private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
- 0, PorterDuff.Mode.SRC_ATOP);
- private final int mIconDarkAlpha;
- private final int mIconDarkColor = 0xffffffff;
- private final int mDarkProgressTint = 0xffffffff;
- private final Interpolator mLinearOutSlowInInterpolator;
+ private static final int mDarkProgressTint = 0xffffffff;
- private int mColor;
- private ViewInvertHelper mInvertHelper;
- private ImageView mIcon;
protected ImageView mPicture;
-
- private ImageView mExpandButton;
- private NotificationHeaderView mNotificationHeader;
private ProgressBar mProgressBar;
protected NotificationTemplateViewWrapper(Context ctx, View view) {
- super(view);
- mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
- mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
- android.R.interpolator.linear_out_slow_in);
-
- resolveViews();
+ super(ctx, view);
+ resolveTemplateViews();
}
- private void resolveViews() {
+ private void resolveTemplateViews() {
View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column);
- mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
- mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button);
- mColor = resolveColor(mExpandButton);
final View progress = mView.findViewById(com.android.internal.R.id.progress);
if (progress instanceof ProgressBar) {
mProgressBar = (ProgressBar) progress;
@@ -91,30 +48,9 @@
// It's still a viewstub
mProgressBar = null;
}
- mNotificationHeader = (NotificationHeaderView) mView.findViewById(
- com.android.internal.R.id.notification_header);
- ArrayList<View> viewsToInvert = new ArrayList<>();
if (mainColumn != null) {
- viewsToInvert.add(mainColumn);
+ mInvertHelper.addTarget(mainColumn);
}
- for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
- View child = mNotificationHeader.getChildAt(i);
- if (child != mIcon) {
- viewsToInvert.add(child);
- }
- }
- mInvertHelper = new ViewInvertHelper(viewsToInvert,
- NotificationPanelView.DOZE_ANIMATION_DURATION);
- }
-
- private int resolveColor(ImageView icon) {
- if (icon != null && icon.getDrawable() != null) {
- ColorFilter filter = icon.getDrawable().getColorFilter();
- if (filter instanceof PorterDuffColorFilter) {
- return ((PorterDuffColorFilter) filter).getColor();
- }
- }
- return 0;
}
@Override
@@ -122,37 +58,12 @@
super.notifyContentUpdated();
// Reinspect the notification.
- resolveViews();
+ resolveTemplateViews();
}
@Override
public void setDark(boolean dark, boolean fade, long delay) {
- if (mInvertHelper != null) {
- if (fade) {
- mInvertHelper.fade(dark, delay);
- } else {
- mInvertHelper.update(dark);
- }
- }
- if (mIcon != null) {
- boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
- != NotificationHeaderView.NO_COLOR;
- if (fade) {
- if (hadColorFilter) {
- fadeIconColorFilter(mIcon, dark, delay);
- fadeIconAlpha(mIcon, dark, delay);
- } else {
- fadeGrayscale(mIcon, dark, delay);
- }
- } else {
- if (hadColorFilter) {
- updateIconColorFilter(mIcon, dark);
- updateIconAlpha(mIcon, dark);
- } else {
- updateGrayscale(mIcon, dark);
- }
- }
- }
+ super.setDark(dark, fade, delay);
setPictureGrayscale(dark, fade, delay);
setProgressBarDark(dark, fade, delay);
}
@@ -197,96 +108,6 @@
}
}
- private void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
- boolean dark, long delay, Animator.AnimatorListener listener) {
- float startIntensity = dark ? 0f : 1f;
- float endIntensity = dark ? 1f : 0f;
- ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
- animator.addUpdateListener(updateListener);
- animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
- animator.setInterpolator(mLinearOutSlowInInterpolator);
- animator.setStartDelay(delay);
- if (listener != null) {
- animator.addListener(listener);
- }
- animator.start();
- }
-
- private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateIconColorFilter(target, (Float) animation.getAnimatedValue());
- }
- }, dark, delay, null /* listener */);
- }
-
- private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (float) animation.getAnimatedValue();
- target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
- }
- }, dark, delay, null /* listener */);
- }
-
- protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateGrayscaleMatrix((float) animation.getAnimatedValue());
- target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
- }
- }, dark, delay, new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!dark) {
- target.setColorFilter(null);
- }
- }
- });
- }
-
- private void updateIconColorFilter(ImageView target, boolean dark) {
- updateIconColorFilter(target, dark ? 1f : 0f);
- }
-
- private void updateIconColorFilter(ImageView target, float intensity) {
- int color = interpolateColor(mColor, mIconDarkColor, intensity);
- mIconColorFilter.setColor(color);
- Drawable iconDrawable = target.getDrawable();
-
- // Also, the notification might have been modified during the animation, so background
- // might be null here.
- if (iconDrawable != null) {
- iconDrawable.mutate().setColorFilter(mIconColorFilter);
- }
- }
-
- private void updateIconAlpha(ImageView target, boolean dark) {
- target.setImageAlpha(dark ? mIconDarkAlpha : 255);
- }
-
- protected void updateGrayscale(ImageView target, boolean dark) {
- if (dark) {
- updateGrayscaleMatrix(1f);
- target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
- } else {
- target.setColorFilter(null);
- }
- }
-
- @Override
- public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
- mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
- mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
- }
-
- private void updateGrayscaleMatrix(float intensity) {
- mGrayscaleColorMatrix.setSaturation(1 - intensity);
- }
-
private static int interpolateColor(int source, int target, float t) {
int aSource = Color.alpha(source);
int rSource = Color.red(source);
@@ -302,9 +123,4 @@
(int) (gSource * (1f - t) + gTarget * t),
(int) (bSource * (1f - t) + bTarget * t));
}
-
- @Override
- public NotificationHeaderView getNotificationHeader() {
- return mNotificationHeader;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
index 119d57b..61499de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
@@ -26,16 +26,13 @@
*/
public abstract class NotificationViewWrapper {
- private static final String TAG_BIG_MEDIA_NARROW = "bigMediaNarrow";
- private static final String TAG_MEDIA = "media";
- private static final String TAG_BIG_PICTURE = "bigPicture";
-
protected final View mView;
- private boolean mSubTextVisible = true;
public static NotificationViewWrapper wrap(Context ctx, View v) {
if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
return new NotificationTemplateViewWrapper(ctx, v);
+ } else if (v instanceof NotificationHeaderView) {
+ return new NotificationHeaderViewWrapper(ctx, v);
} else {
return new NotificationCustomViewWrapper(v);
}
@@ -57,9 +54,7 @@
/**
* Notifies this wrapper that the content of the view might have changed.
*/
- public void notifyContentUpdated() {
- setSubTextVisible(mSubTextVisible);
- }
+ public void notifyContentUpdated() {};
/**
* @return true if this template might need to be clipped with a round rect to make it look
@@ -70,14 +65,6 @@
}
/**
- * Change the subTextVisibility
- * @param visible Should the subtext be visible
- */
- public void setSubTextVisible(boolean visible) {
- mSubTextVisible = visible;
- }
-
- /**
* Update the appearance of the expand button.
*
* @param expandable should this view be expandable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
index fafea98..5fb6fec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
@@ -23,6 +23,8 @@
import com.android.keyguard.AlphaOptimizedLinearLayout;
import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
/**
* A hybrid view which may contain information about one ore more notifications.
@@ -31,6 +33,7 @@
protected TextView mTitleView;
protected TextView mTextView;
+ private ViewInvertHelper mInvertHelper;
public HybridNotificationView(Context context) {
this(context, null);
@@ -54,6 +57,7 @@
super.onFinishInflate();
mTitleView = (TextView) findViewById(R.id.notification_title);
mTextView = (TextView) findViewById(R.id.notification_text);
+ mInvertHelper = new ViewInvertHelper(this, NotificationPanelView.DOZE_ANIMATION_DURATION);
}
public void bind(CharSequence title) {
@@ -65,4 +69,8 @@
mTextView.setText(text);
requestLayout();
}
+
+ public void setDark(boolean dark, boolean fade, long delay) {
+ mInvertHelper.setInverted(dark, fade, delay);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index a2616fe..d35e57b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -270,7 +270,7 @@
public void onTuningChanged(String key, String newValue) {
switch (key) {
case KEY_DOCK_WINDOW_GESTURE:
- mDockWindowEnabled = (newValue != null) &&
+ mDockWindowEnabled = (newValue == null) ||
(Integer.parseInt(newValue) != 0);
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 73ee363..6a2b32d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -44,7 +44,6 @@
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
-
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.DejankUtils;
@@ -65,7 +64,6 @@
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
-import com.android.systemui.tuner.TunerService;
import java.util.List;
@@ -73,7 +71,7 @@
ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener,
- HeadsUpManager.OnHeadsUpChangedListener, TunerService.Tunable {
+ HeadsUpManager.OnHeadsUpChangedListener {
private static final boolean DEBUG = false;
@@ -221,13 +219,10 @@
private final Interpolator mTouchResponseInterpolator =
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
- private boolean mNewQs;
-
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(!DEBUG);
mFalsingManager = FalsingManager.getInstance(context);
- TunerService.get(context).addTunable(this, QSPanel.QS_THE_NEW_QS);
}
public void setStatusBar(PhoneStatusBar bar) {
@@ -235,26 +230,10 @@
}
@Override
- public void onTuningChanged(String key, String newValue) {
- if (QSPanel.QS_THE_NEW_QS.equals(key)) {
- boolean b = newValue != null && Integer.parseInt(newValue) != 0;
- if (mNewQs != b) {
- if (mHeader != null) {
- // We are too late, no good way to re-initialize yet, just die and come back up.
- android.os.Process.killProcess(android.os.Process.myPid());
- } else {
- mNewQs = b;
- }
- }
- }
- }
-
- @Override
protected void onFinishInflate() {
super.onFinishInflate();
ViewStub stub = (ViewStub) findViewById(R.id.status_bar_header);
- stub.setLayoutResource(mNewQs
- ? R.layout.quick_status_bar_expanded_header : R.layout.status_bar_expanded_header);
+ stub.setLayoutResource(R.layout.quick_status_bar_expanded_header);
mHeader = (BaseStatusBarHeader) stub.inflate();
mHeader.setOnClickListener(this);
mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index c740b08..7f27ba7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -133,7 +133,7 @@
TunerService.get(mContext).addTunable(this, TILES_SETTING);
}
- PhoneStatusBar getPhoneStatusBar() {
+ public PhoneStatusBar getPhoneStatusBar() {
return mStatusBar;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 26ff97a..1372cca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -24,12 +24,14 @@
import android.graphics.drawable.Animatable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
+import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
@@ -42,6 +44,7 @@
public class QuickStatusBarHeader extends BaseStatusBarHeader implements
NextAlarmController.NextAlarmChangeCallback, View.OnClickListener {
+ private static final String TAG = "QuickStatusBarHeader";
private ActivityStarter mActivityStarter;
private NextAlarmController mNextAlarmController;
private SettingsButton mSettingsButton;
@@ -59,11 +62,15 @@
private boolean mDetailTransitioning;
private ViewGroup mExpandedGroup;
+ private ViewGroup mDateTimeGroup;
+ private View mEmergencyOnly;
private TextView mQsDetailHeaderTitle;
private boolean mListening;
private AlarmManager.AlarmClockInfo mNextAlarm;
private QuickQSPanel mHeaderQsPanel;
+ private boolean mShowEmergencyCallsOnly;
+ private float mDateTimeTranslation;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -73,6 +80,10 @@
protected void onFinishInflate() {
super.onFinishInflate();
+ mEmergencyOnly = findViewById(R.id.header_emergency_calls_only);
+ mDateTimeTranslation = mContext.getResources().getDimension(
+ R.dimen.qs_date_anim_translation);
+ mDateTimeGroup = (ViewGroup) findViewById(R.id.date_time_group);
mExpandedGroup = (ViewGroup) findViewById(R.id.expanded_group);
mHeaderQsPanel = (QuickQSPanel) findViewById(R.id.quick_qs_panel);
@@ -118,14 +129,15 @@
@Override
public void setExpanded(boolean expanded) {
mExpanded = expanded;
+ updateEverything();
}
@Override
public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
mNextAlarm = nextAlarm;
+ Log.d(TAG, "Got alarm update " + (nextAlarm != null));
if (nextAlarm != null) {
- // TODO:...
-// mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
+ mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
}
mAlarmShowing = nextAlarm != null;
updateEverything();
@@ -137,6 +149,9 @@
mExpandedGroup.setVisibility(headerExpansionFraction > 0 ? View.VISIBLE : View.INVISIBLE);
mHeaderQsPanel.setAlpha(1 - headerExpansionFraction);
mHeaderQsPanel.setVisibility(headerExpansionFraction < 1 ? View.VISIBLE : View.INVISIBLE);
+
+ mDateTimeGroup.setTranslationY(headerExpansionFraction * mDateTimeTranslation);
+ mEmergencyOnly.setAlpha(headerExpansionFraction);
}
public void setListening(boolean listening) {
@@ -154,16 +169,20 @@
}
private void updateVisibilities() {
- mAlarmStatus.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.GONE);
+ mAlarmStatus.setVisibility(mAlarmShowing ? View.VISIBLE : View.GONE);
mQsDetailHeader.setVisibility(mExpanded && mShowingDetail ? View.VISIBLE : View.INVISIBLE);
+ mEmergencyOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly
+ ? View.VISIBLE : View.INVISIBLE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
}
private void updateListeners() {
if (mListening) {
+ Log.d(TAG, "Listening for Alarms");
mNextAlarmController.addStateChangedCallback(this);
} else {
+ Log.d(TAG, "Not listening for Alarms");
mNextAlarmController.removeStateChangedCallback(this);
}
}
@@ -193,7 +212,7 @@
host.getBatteryController());
mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
mHeaderQsPanel.setHost(myHost);
- mHeaderQsPanel.setMaxTiles(3);
+ mHeaderQsPanel.setMaxTiles(5);
mHeaderQsPanel.setTiles(myHost.getTiles());
myHost.addCallback(new QSTile.Host.Callback() {
@Override
@@ -250,8 +269,14 @@
}
@Override
- public void setEmergencyCallsOnly(boolean emergencyOnly) {
- // Don't care.
+ public void setEmergencyCallsOnly(boolean show) {
+ boolean changed = show != mShowEmergencyCallsOnly;
+ if (changed) {
+ mShowEmergencyCallsOnly = show;
+ if (mExpanded) {
+ updateEverything();
+ }
+ }
}
private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
@@ -308,6 +333,7 @@
private void handleShowingDetail(final QSTile.DetailAdapter detail) {
final boolean showingDetail = detail != null;
+ transition(mDateTimeGroup, !showingDetail);
transition(mExpandedGroup, !showingDetail);
if (mAlarmShowing) {
transition(mAlarmStatus, !showingDetail);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 6cda561..3d21f44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -59,7 +59,7 @@
*/
public class StatusBarHeaderView extends BaseStatusBarHeader implements View.OnClickListener,
BatteryController.BatteryStateChangeCallback, NextAlarmController.NextAlarmChangeCallback,
- EmergencyListener, TunerService.Tunable {
+ EmergencyListener {
private boolean mExpanded;
private boolean mListening;
@@ -234,28 +234,6 @@
updateClockCollapsedMargin();
}
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- TunerService.get(mContext).addTunable(this, QSPanel.QS_THE_NEW_QS);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- TunerService.get(mContext).removeTunable(this);
- }
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (QSPanel.QS_THE_NEW_QS.equals(key)) {
- mAllowExpand = newValue == null || Integer.parseInt(newValue) == 0;
- if (!mAllowExpand) {
- setExpanded(false);
- }
- }
- }
-
private void updateClockCollapsedMargin() {
Resources res = getResources();
int padding = res.getDimensionPixelSize(R.dimen.clock_collapsed_bottom_margin);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 22c0cb9..65053f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -28,6 +28,8 @@
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
@@ -49,7 +51,7 @@
/**
* Host for the remote input.
*/
-public class RemoteInputView extends LinearLayout implements View.OnClickListener {
+public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
private static final String TAG = "RemoteInput";
@@ -101,6 +103,7 @@
}
});
mEditText.setOnClickListener(this);
+ mEditText.addTextChangedListener(this);
mEditText.setInnerFocusable(false);
mEditText.mDefocusListener = this;
}
@@ -115,6 +118,8 @@
mEditText.setEnabled(false);
mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
+ mController.removeRemoteInput(mEntry);
+ mEditText.mShowImeOnInputConnection = false;
try {
mPendingIntent.send(mContext, 0, fillInIntent);
@@ -175,6 +180,40 @@
mEditText.setText(mEntry.remoteInputText);
mEditText.setSelection(mEditText.getText().length());
mEditText.requestFocus();
+ updateSendButton();
+ }
+
+ public void onNotificationUpdate() {
+ boolean sending = mProgressBar.getVisibility() == VISIBLE;
+
+ if (sending) {
+ // Update came in after we sent the reply, time to reset.
+ reset();
+ }
+ }
+
+ private void reset() {
+ mEditText.getText().clear();
+ mEditText.setEnabled(true);
+ mSendButton.setVisibility(VISIBLE);
+ mProgressBar.setVisibility(INVISIBLE);
+ updateSendButton();
+ onDefocus();
+ }
+
+ private void updateSendButton() {
+ mSendButton.setEnabled(mEditText.getText().length() != 0);
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ updateSendButton();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 9015a0e..2b71ce9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -23,9 +23,11 @@
import android.view.ViewGroup;
import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.HybridNotificationView;
import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
import java.util.ArrayList;
import java.util.List;
@@ -49,6 +51,7 @@
private final int mNotificatonTopPadding;
private final HybridNotificationViewManager mHybridViewManager;
private final float mCollapsedBottompadding;
+ private ViewInvertHelper mOverflowInvertHelper;
private boolean mChildrenExpanded;
private ExpandableNotificationRow mNotificationParent;
private HybridNotificationView mGroupOverflowContainer;
@@ -172,12 +175,17 @@
if (hasOverflow) {
mGroupOverflowContainer = mHybridViewManager.bindFromNotificationGroup(
mGroupOverflowContainer, mChildren, lastVisibleIndex + 1);
+ if (mOverflowInvertHelper == null) {
+ mOverflowInvertHelper= new ViewInvertHelper(mGroupOverflowContainer,
+ NotificationPanelView.DOZE_ANIMATION_DURATION);
+ }
if (mGroupOverFlowState == null) {
mGroupOverFlowState = new ViewState();
}
} else if (mGroupOverflowContainer != null) {
removeView(mGroupOverflowContainer);
mGroupOverflowContainer = null;
+ mOverflowInvertHelper = null;
mGroupOverFlowState = null;
}
}
@@ -324,7 +332,7 @@
if (!likeCollapsed && (mChildrenExpanded || mNotificationParent.isUserLocked())) {
return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
}
- if (mNotificationParent.isExpanded()) {
+ if (mNotificationParent.isExpanded() || mNotificationParent.isHeadsUp()) {
return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
}
return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
@@ -387,16 +395,17 @@
boolean withDelays, long baseDelay, long duration) {
int childCount = mChildren.size();
ViewState tmpState = new ViewState();
- int notGoneIndex = 0;
- for (int i = 0; i < childCount; i++) {
+ int delayIndex = 0;
+ int maxAllowChildCount = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
+ for (int i = childCount - 1; i >= 0; i--) {
ExpandableNotificationRow child = mChildren.get(i);
StackViewState viewState = state.getViewStateForView(child);
int difference = Math.min(StackStateAnimator.DELAY_EFFECT_MAX_INDEX_DIFFERENCE_CHILDREN,
- notGoneIndex + 1);
+ delayIndex);
long delay = withDelays
? difference * StackStateAnimator.ANIMATION_DELAY_PER_ELEMENT_EXPAND_CHILDREN
: 0;
- delay += baseDelay;
+ delay = (long) (delay * (mChildrenExpanded ? 1.0f : 0.5f) + baseDelay);
stateAnimator.startStackAnimations(child, viewState, state, -1, delay);
// layout the divider
@@ -405,11 +414,13 @@
tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
stateAnimator.startViewAnimations(divider, tmpState, delay, duration);
-
- notGoneIndex++;
+ if (i < maxAllowChildCount) {
+ delayIndex++;
+ }
}
if (mGroupOverflowContainer != null) {
- stateAnimator.startViewAnimations(mGroupOverflowContainer, mGroupOverFlowState, -1, 0);
+ stateAnimator.startViewAnimations(mGroupOverflowContainer, mGroupOverFlowState,
+ baseDelay, duration);
}
}
@@ -443,4 +454,10 @@
public int getMinHeight() {
return getIntrinsicHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */));
}
+
+ public void setDark(boolean dark, boolean fade, long delay) {
+ if (mGroupOverflowContainer != null) {
+ mOverflowInvertHelper.setInverted(dark, fade, delay);
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 11fdbb5..8cf25b3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3987,6 +3987,9 @@
private final Uri mDisplayDaltonizerUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER);
+ private final Uri mDisplayColorMatrixUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX);
+
private final Uri mHighTextContrastUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED);
@@ -4017,6 +4020,8 @@
contentResolver.registerContentObserver(
mDisplayDaltonizerUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
+ mDisplayColorMatrixUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
mHighTextContrastUri, false, this, UserHandle.USER_ALL);
}
@@ -4066,6 +4071,8 @@
if (readDisplayColorAdjustmentSettingsLocked(userState)) {
updateDisplayColorAdjustmentSettingsLocked(userState);
}
+ } else if (mDisplayColorMatrixUri.equals(uri)) {
+ updateDisplayColorAdjustmentSettingsLocked(userState);
} else if (mHighTextContrastUri.equals(uri)) {
if (readHighTextContrastEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
index d0b5898..1a7de25 100644
--- a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
@@ -107,9 +107,24 @@
setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
}
+ String matrix = Settings.Secure.getStringForUser(cr,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, userId);
+ if (matrix != null) {
+ colorMatrix = multiply(colorMatrix, getMatrix(matrix));
+ }
+
setColorTransform(colorMatrix);
}
+ private static float[] getMatrix(String matrix) {
+ String[] strValues = matrix.split(",");
+ float[] values = new float[strValues.length];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = Float.parseFloat(strValues[i]);
+ }
+ return values;
+ }
+
private static float[] multiply(float[] matrix, float[] other) {
if (matrix == null) {
return other;
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 960fb4b..5f57a76 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1233,14 +1233,6 @@
}
}
- // direct-callback alarms must be wakeup alarms (otherwise they should just be
- // posting work to a Handler)
- if (directReceiver != null) {
- if (type != RTC_WAKEUP && type != ELAPSED_REALTIME_WAKEUP) {
- throw new IllegalArgumentException("Only wakeup alarms can use AlarmReceivers");
- }
- }
-
if (workSource != null) {
getContext().enforcePermission(
android.Manifest.permission.UPDATE_DEVICE_STATS,
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index ede92fb..7fcedc6 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -788,6 +788,9 @@
@Override
public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
+ if (callback == null) {
+ return;
+ }
synchronized (this) {
op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
Callback cb = mModeWatchers.get(callback.asBinder());
@@ -816,6 +819,9 @@
@Override
public void stopWatchingMode(IAppOpsCallback callback) {
+ if (callback == null) {
+ return;
+ }
synchronized (this) {
Callback cb = mModeWatchers.remove(callback.asBinder());
if (cb != null) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c712a56..ed64c2b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -34,6 +34,7 @@
import android.annotation.Nullable;
import android.app.AlarmManager;
+import android.app.BroadcastOptions;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -72,6 +73,7 @@
import android.net.UidRange;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Handler;
@@ -1529,6 +1531,7 @@
log("sendStickyBroadcast: action=" + intent.getAction());
}
+ Bundle options = null;
final long ident = Binder.clearCallingIdentity();
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
final NetworkInfo ni = intent.getParcelableExtra(
@@ -1536,6 +1539,10 @@
if (ni.getType() == ConnectivityManager.TYPE_MOBILE_SUPL) {
intent.setAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ } else {
+ BroadcastOptions opts = BroadcastOptions.makeBasic();
+ opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
+ options = opts.toBundle();
}
final IBatteryStats bs = BatteryStatsService.getService();
try {
@@ -1546,7 +1553,7 @@
}
}
try {
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 45c1ed2..ef79cfe 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -3027,7 +3027,7 @@
final List<ImeSubtypeListItem> imList =
mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
- true /* showSubtypes */, showAuxSubtypes, isScreenLocked);
+ showAuxSubtypes, isScreenLocked);
if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
@@ -3536,6 +3536,7 @@
private static final String ATTR_LABEL = "label";
private static final String ATTR_ICON = "icon";
private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale";
+ private static final String ATTR_IME_SUBTYPE_LANGUAGE_TAG = "languageTag";
private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode";
private static final String ATTR_IME_SUBTYPE_EXTRA_VALUE = "imeSubtypeExtraValue";
private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
@@ -3629,6 +3630,8 @@
out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId()));
out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId()));
out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
+ out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG,
+ subtype.getLanguageTag());
out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode());
out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue());
out.attribute(null, ATTR_IS_AUXILIARY,
@@ -3690,6 +3693,8 @@
parser.getAttributeValue(null, ATTR_LABEL));
final String imeSubtypeLocale =
parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
+ final String languageTag =
+ parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG);
final String imeSubtypeMode =
parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE);
final String imeSubtypeExtraValue =
@@ -3700,6 +3705,7 @@
.setSubtypeNameResId(label)
.setSubtypeIconResId(icon)
.setSubtypeLocale(imeSubtypeLocale)
+ .setLanguageTag(languageTag)
.setSubtypeMode(imeSubtypeMode)
.setSubtypeExtraValue(imeSubtypeExtraValue)
.setIsAuxiliary(isAuxiliary)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 507233f..26ece54 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3484,27 +3484,29 @@
Watchdog.getInstance().processStarted(app.processName, startResult.pid);
}
- checkTime(startTime, "startProcess: building log message");
- StringBuilder buf = mStringBuilder;
- buf.setLength(0);
- buf.append("Start proc ");
- buf.append(startResult.pid);
- buf.append(':');
- buf.append(app.processName);
- buf.append('/');
- UserHandle.formatUid(buf, uid);
- if (!isActivityProcess) {
- buf.append(" [");
- buf.append(entryPoint);
- buf.append("]");
+ if (DEBUG_PROCESSES) {
+ checkTime(startTime, "startProcess: building log message");
+ StringBuilder buf = mStringBuilder;
+ buf.setLength(0);
+ buf.append("Start proc ");
+ buf.append(startResult.pid);
+ buf.append(':');
+ buf.append(app.processName);
+ buf.append('/');
+ UserHandle.formatUid(buf, uid);
+ if (!isActivityProcess) {
+ buf.append(" [");
+ buf.append(entryPoint);
+ buf.append("]");
+ }
+ buf.append(" for ");
+ buf.append(hostingType);
+ if (hostingNameStr != null) {
+ buf.append(" ");
+ buf.append(hostingNameStr);
+ }
+ Slog.i(TAG, buf.toString());
}
- buf.append(" for ");
- buf.append(hostingType);
- if (hostingNameStr != null) {
- buf.append(" ");
- buf.append(hostingNameStr);
- }
- Slog.i(TAG, buf.toString());
app.setPid(startResult.pid);
app.usingWrapper = startResult.usingWrapper;
app.removed = false;
@@ -17901,7 +17903,7 @@
if (mSupportedSystemLocales == null) {
mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
}
- final Locale locale = values.getLocales().getBestMatch(mSupportedSystemLocales);
+ final Locale locale = values.getLocales().getFirstMatch(mSupportedSystemLocales);
SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
locale));
@@ -19748,7 +19750,7 @@
} else {
numEmpty++;
if (numEmpty > emptyProcessLimit) {
- app.kill("empty #" + numEmpty, true);
+ app.kill("empty #" + numEmpty, DEBUG_PROCESSES);
}
}
break;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b052d17..9e32efa 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -74,6 +74,7 @@
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.util.EventLog;
+import android.util.Log;
import android.util.Slog;
import android.view.Display;
@@ -733,7 +734,7 @@
"Launch completed; removing icicle of " + r.icicle);
}
- private void addRecentActivityLocked(ActivityRecord r) {
+ void addRecentActivityLocked(ActivityRecord r) {
if (r != null) {
mRecentTasks.addLocked(r.task);
r.task.touchActiveTime();
@@ -2312,8 +2313,8 @@
updateTaskMovement(task, true);
}
- final void startActivityLocked(ActivityRecord r, boolean newTask,
- boolean doResume, boolean keepCurTransition, ActivityOptions options) {
+ final void startActivityLocked(ActivityRecord r, boolean newTask, boolean keepCurTransition,
+ ActivityOptions options) {
TaskRecord rTask = r.task;
final int taskId = rTask.taskId;
// mLaunchTaskBehind tasks get placed at the back of the task stack.
@@ -2459,12 +2460,6 @@
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
-
- if (doResume) {
- mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
- } else {
- addRecentActivityLocked(r);
- }
}
final void validateAppTokensLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f4ba19f..9fff0c8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2628,9 +2628,14 @@
}
ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
targetStack.mLastPausedActivity = null;
- targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
- if (!launchTaskBehind && doResume) {
- mService.setFocusedActivityLocked(r, "startedActivity");
+ targetStack.startActivityLocked(r, newTask, keepCurTransition, options);
+ if (doResume) {
+ if (!launchTaskBehind) {
+ mService.setFocusedActivityLocked(r, "startedActivity");
+ }
+ resumeTopActivitiesLocked(targetStack, r, options);
+ } else {
+ targetStack.addRecentActivityLocked(r);
}
updateUserStackLocked(r.userId, targetStack);
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 2fb71c3..622aa16 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -468,7 +468,7 @@
}
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
- BroadcastFilter filter, boolean ordered) {
+ BroadcastFilter filter, boolean ordered, int index) {
boolean skip = false;
if (filter.requiredPermission != null) {
int perm = mService.checkComponentPermission(filter.requiredPermission,
@@ -576,64 +576,70 @@
if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
r.callingPid, r.resolvedType, filter.receiverList.uid)) {
- return;
+ skip = true;
}
- if (filter.receiverList.app == null || filter.receiverList.app.crashing) {
+ if (!skip && (filter.receiverList.app == null || filter.receiverList.app.crashing)) {
Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
+ " to " + filter.receiverList + ": process crashing");
skip = true;
}
- if (!skip) {
- // If permissions need a review before any of the app components can run, we drop
- // the broadcast and if the calling app is in the foreground and the broadcast is
- // explicit we launch the review UI passing it a pending intent to send the skipped
- // broadcast.
- if (Build.PERMISSIONS_REVIEW_REQUIRED) {
- if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
- filter.owningUserId)) {
- return;
- }
- }
+ if (skip) {
+ r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
+ return;
+ }
- // If this is not being sent as an ordered broadcast, then we
- // don't want to touch the fields that keep track of the current
- // state of ordered broadcasts.
- if (ordered) {
- r.receiver = filter.receiverList.receiver.asBinder();
- r.curFilter = filter;
- filter.receiverList.curBroadcast = r;
- r.state = BroadcastRecord.CALL_IN_RECEIVE;
- if (filter.receiverList.app != null) {
- // Bump hosting application to no longer be in background
- // scheduling class. Note that we can't do that if there
- // isn't an app... but we can only be in that case for
- // things that directly call the IActivityManager API, which
- // are already core system stuff so don't matter for this.
- r.curApp = filter.receiverList.app;
- filter.receiverList.app.curReceiver = r;
- mService.updateOomAdjLocked(r.curApp);
- }
+ // If permissions need a review before any of the app components can run, we drop
+ // the broadcast and if the calling app is in the foreground and the broadcast is
+ // explicit we launch the review UI passing it a pending intent to send the skipped
+ // broadcast.
+ if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+ if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
+ filter.owningUserId)) {
+ r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
+ return;
}
- try {
- if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
- "Delivering to " + filter + " : " + r);
- performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
- new Intent(r.intent), r.resultCode, r.resultData,
- r.resultExtras, r.ordered, r.initialSticky, r.userId);
- if (ordered) {
- r.state = BroadcastRecord.CALL_DONE_RECEIVE;
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
- if (ordered) {
- r.receiver = null;
- r.curFilter = null;
- filter.receiverList.curBroadcast = null;
- if (filter.receiverList.app != null) {
- filter.receiverList.app.curReceiver = null;
- }
+ }
+
+ r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
+
+ // If this is not being sent as an ordered broadcast, then we
+ // don't want to touch the fields that keep track of the current
+ // state of ordered broadcasts.
+ if (ordered) {
+ r.receiver = filter.receiverList.receiver.asBinder();
+ r.curFilter = filter;
+ filter.receiverList.curBroadcast = r;
+ r.state = BroadcastRecord.CALL_IN_RECEIVE;
+ if (filter.receiverList.app != null) {
+ // Bump hosting application to no longer be in background
+ // scheduling class. Note that we can't do that if there
+ // isn't an app... but we can only be in that case for
+ // things that directly call the IActivityManager API, which
+ // are already core system stuff so don't matter for this.
+ r.curApp = filter.receiverList.app;
+ filter.receiverList.app.curReceiver = r;
+ mService.updateOomAdjLocked(r.curApp);
+ }
+ }
+ try {
+ if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
+ "Delivering to " + filter + " : " + r);
+ performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
+ new Intent(r.intent), r.resultCode, r.resultData,
+ r.resultExtras, r.ordered, r.initialSticky, r.userId);
+ if (ordered) {
+ r.state = BroadcastRecord.CALL_DONE_RECEIVE;
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
+ if (ordered) {
+ r.receiver = null;
+ r.curFilter = null;
+ filter.receiverList.curBroadcast = null;
+ if (filter.receiverList.app != null) {
+ filter.receiverList.app.curReceiver = null;
}
}
}
@@ -740,7 +746,7 @@
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Delivering non-ordered on [" + mQueueName + "] to registered "
+ target + ": " + r);
- deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
+ deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
@@ -897,7 +903,7 @@
"Delivering ordered ["
+ mQueueName + "] to registered "
+ filter + ": " + r);
- deliverToRegisteredReceiverLocked(r, filter, r.ordered);
+ deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
if (r.receiver == null || !r.ordered) {
// The receiver has already finished, so schedule to
// process the next one.
@@ -925,10 +931,17 @@
info.activityInfo.name);
boolean skip = false;
+ if (brOptions != null &&
+ (info.activityInfo.applicationInfo.targetSdkVersion
+ < brOptions.getMinManifestReceiverApiLevel() ||
+ info.activityInfo.applicationInfo.targetSdkVersion
+ > brOptions.getMaxManifestReceiverApiLevel())) {
+ skip = true;
+ }
int perm = mService.checkComponentPermission(info.activityInfo.permission,
r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
info.activityInfo.exported);
- if (perm != PackageManager.PERMISSION_GRANTED) {
+ if (!skip && perm != PackageManager.PERMISSION_GRANTED) {
if (!info.activityInfo.exported) {
Slog.w(TAG, "Permission Denial: broadcasting "
+ r.intent.toString()
@@ -945,7 +958,7 @@
+ " due to receiver " + component.flattenToShortString());
}
skip = true;
- } else if (info.activityInfo.permission != null) {
+ } else if (!skip && info.activityInfo.permission != null) {
final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
if (opCode != AppOpsManager.OP_NONE
&& mService.mAppOpsService.noteOperation(opCode, r.callingUid,
@@ -1075,9 +1088,18 @@
}
}
+ // This is safe to do even if we are skipping the broadcast, and we need
+ // this information now to evaluate whether it is going to be allowed to run.
+ final int receiverUid = info.activityInfo.applicationInfo.uid;
+ // If it's a singleton, it needs to be the same app or a special app
+ if (r.callingUid != Process.SYSTEM_UID && isSingleton
+ && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
+ info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
+ }
String targetProcess = info.activityInfo.processName;
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
info.activityInfo.applicationInfo.uid, false);
+
if (!skip) {
final int allowed = mService.checkAllowBackgroundLocked(
info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1);
@@ -1086,9 +1108,17 @@
// completely disabled from launches, or it is delayed and the broadcast
// was not explicitly sent to it and this would result in a new process
// for it being created.
- if (allowed == ActivityManager.APP_START_MODE_DISABLED
+ if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Background execution disabled: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString());
+ skip = true;
+ }
+ if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
|| (r.intent.getComponent() == null
- && r.intent.getPackage() == null && app == null)) {
+ && r.intent.getPackage() == null && app == null
+ && ((r.intent.getFlags()
+ & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0))) {
Slog.w(TAG, "Background execution not allowed: receiving "
+ r.intent + " to "
+ component.flattenToShortString());
@@ -1101,6 +1131,7 @@
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Skipping delivery of ordered [" + mQueueName + "] "
+ r + " for whatever reason");
+ r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
@@ -1108,14 +1139,9 @@
return;
}
+ r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
- final int receiverUid = info.activityInfo.applicationInfo.uid;
- // If it's a singleton, it needs to be the same app or a special app
- if (r.callingUid != Process.SYSTEM_UID && isSingleton
- && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
- info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
- }
r.curReceiver = info.activityInfo;
if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
@@ -1283,6 +1309,7 @@
String anrMessage = null;
Object curReceiver = r.receivers.get(r.nextReceiver-1);
+ r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
Slog.w(TAG, "Receiver during timeout: " + curReceiver);
logBroadcastReceiverDiscardLocked(r);
if (curReceiver instanceof BroadcastFilter) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index b42bcff..e99cbf9 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -57,6 +57,7 @@
final int appOp; // an app op that is associated with this broadcast
final BroadcastOptions options; // BroadcastOptions supplied by caller
final List receivers; // contains BroadcastFilter and ResolveInfo
+ final int[] delivery; // delivery state of each receiver
IIntentReceiver resultTo; // who receives final result if non-null
long enqueueClockTime; // the clock time the broadcast was enqueued
long dispatchTime; // when dispatch started on this set of receivers
@@ -79,6 +80,11 @@
static final int CALL_DONE_RECEIVE = 3;
static final int WAITING_SERVICES = 4;
+ static final int DELIVERY_PENDING = 0;
+ static final int DELIVERY_DELIVERED = 1;
+ static final int DELIVERY_SKIPPED = 2;
+ static final int DELIVERY_TIMEOUT = 3;
+
// The following are set when we are calling a receiver (one that
// was found in our list of registered receivers).
BroadcastFilter curFilter;
@@ -183,12 +189,24 @@
PrintWriterPrinter printer = new PrintWriterPrinter(pw);
for (int i = 0; i < N; i++) {
Object o = receivers.get(i);
- pw.print(prefix); pw.print("Receiver #"); pw.print(i);
- pw.print(": "); pw.println(o);
- if (o instanceof BroadcastFilter)
- ((BroadcastFilter)o).dumpBrief(pw, p2);
- else if (o instanceof ResolveInfo)
- ((ResolveInfo)o).dump(printer, p2);
+ pw.print(prefix);
+ switch (delivery[i]) {
+ case DELIVERY_PENDING: pw.print("Pending"); break;
+ case DELIVERY_DELIVERED: pw.print("Deliver"); break;
+ case DELIVERY_SKIPPED: pw.print("Skipped"); break;
+ case DELIVERY_TIMEOUT: pw.print("Timeout"); break;
+ default: pw.print("???????"); break;
+ }
+ pw.print(" #"); pw.print(i); pw.print(": ");
+ if (o instanceof BroadcastFilter) {
+ pw.println(o);
+ ((BroadcastFilter) o).dumpBrief(pw, p2);
+ } else if (o instanceof ResolveInfo) {
+ pw.println("(manifest)");
+ ((ResolveInfo) o).dump(printer, p2, 0);
+ } else {
+ pw.println(o);
+ }
}
}
@@ -211,6 +229,7 @@
appOp = _appOp;
options = _options;
receivers = _receivers;
+ delivery = new int[_receivers != null ? _receivers.size() : 0];
resultTo = _resultTo;
resultCode = _resultCode;
resultData = _resultData;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index f6f82da..62e78a4 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -253,7 +253,8 @@
final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+ bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mService.broadcastIntentLocked(null, null, bootIntent, null, null, 0, null, null,
new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index a066835..78618ce 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -345,25 +345,9 @@
}
public void updateRunningAccounts() {
- mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
-
- if (mBootCompleted) {
- doDatabaseCleanup();
- }
-
- AccountAndUser[] accounts = mRunningAccounts;
- for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
- if (!containsAccountAndUser(accounts,
- currentSyncContext.mSyncOperation.target.account,
- currentSyncContext.mSyncOperation.target.userId)) {
- Log.d(TAG, "canceling sync since the account is no longer running");
- sendSyncFinishedOrCanceledMessage(currentSyncContext,
- null /* no result since this is a cancel */);
- }
- }
- // we must do this since we don't bother scheduling alarms when
- // the accounts are not set yet
- sendCheckAlarmsMessage();
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_ACCOUNTS_UPDATED");
+ // Update accounts in handler thread.
+ mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_ACCOUNTS_UPDATED);
}
private void doDatabaseCleanup() {
@@ -2179,6 +2163,7 @@
* obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
*/
private static final int MESSAGE_MONITOR_SYNC = 8;
+ private static final int MESSAGE_ACCOUNTS_UPDATED = 9;
public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
private Long mAlarmScheduleTime = null;
@@ -2296,6 +2281,13 @@
// to also take into account the periodic syncs.
earliestFuturePollTime = scheduleReadyPeriodicSyncs();
switch (msg.what) {
+ case SyncHandler.MESSAGE_ACCOUNTS_UPDATED:
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
+ }
+ updateRunningAccountsH();
+ break;
+
case SyncHandler.MESSAGE_CANCEL:
SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
Bundle extras = msg.peekData();
@@ -2872,7 +2864,28 @@
mLocalDeviceIdleController.setSyncActive(active);
}
}
+ }
+ private void updateRunningAccountsH() {
+ mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
+
+ if (mBootCompleted) {
+ doDatabaseCleanup();
+ }
+
+ AccountAndUser[] accounts = mRunningAccounts;
+ for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
+ if (!containsAccountAndUser(accounts,
+ currentSyncContext.mSyncOperation.target.account,
+ currentSyncContext.mSyncOperation.target.userId)) {
+ Log.d(TAG, "canceling sync since the account is no longer running");
+ sendSyncFinishedOrCanceledMessage(currentSyncContext,
+ null /* no result since this is a cancel */);
+ }
+ }
+ // we must do this since we don't bother scheduling alarms when
+ // the accounts are not set yet
+ sendCheckAlarmsMessage();
}
private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 4d7df9c..309bec8 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -88,6 +88,7 @@
static final int MSG_JOB_EXPIRED = 0;
static final int MSG_CHECK_JOB = 1;
static final int MSG_STOP_JOB = 2;
+ static final int MSG_CHECK_JOB_GREEDY = 3;
// Policy constants
/**
@@ -362,7 +363,8 @@
}
void reportActive() {
- boolean active = false;
+ // active is true if pending queue contains jobs OR some job is running.
+ boolean active = mPendingJobs.size() > 0;
if (mPendingJobs.size() <= 0) {
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
@@ -372,9 +374,10 @@
}
}
}
- if (mLocalDeviceIdleController != null) {
- if (mReportedActive != active) {
- mReportedActive = active;
+
+ if (mReportedActive != active) {
+ mReportedActive = active;
+ if (mLocalDeviceIdleController != null) {
mLocalDeviceIdleController.setJobsActive(active);
}
}
@@ -628,7 +631,8 @@
JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
startTrackingJob(rescheduledPeriodic);
}
- mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ reportActive();
+ mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
}
// StateChangedListener implementations.
@@ -676,8 +680,18 @@
break;
case MSG_CHECK_JOB:
synchronized (mJobs) {
- // Check the list of jobs and run some of them if we feel inclined.
- maybeQueueReadyJobsForExecutionLockedH();
+ if (mReportedActive) {
+ // if jobs are currently being run, queue all ready jobs for execution.
+ queueReadyJobsForExecutionLockedH();
+ } else {
+ // Check the list of jobs and run some of them if we feel inclined.
+ maybeQueueReadyJobsForExecutionLockedH();
+ }
+ }
+ break;
+ case MSG_CHECK_JOB_GREEDY:
+ synchronized (mJobs) {
+ queueReadyJobsForExecutionLockedH();
}
break;
case MSG_STOP_JOB:
@@ -709,7 +723,6 @@
stopJobOnServiceContextLocked(job);
}
}
- reportActive();
if (DEBUG) {
final int queuedJobs = mPendingJobs.size();
if (queuedJobs == 0) {
@@ -786,7 +799,6 @@
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
}
}
- reportActive();
if (DEBUG) {
Slog.d(TAG, "idle=" + idleCount + " connectivity=" +
connectivityCount + " charging=" + chargingCount + " tot=" +
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index b3d7287..33b09e3 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -17,11 +17,8 @@
package com.android.server.job.controllers;
import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
+import android.app.AlarmManager.OnAlarmListener;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.os.SystemClock;
import android.util.Slog;
@@ -40,15 +37,11 @@
*/
public class TimeController extends StateController {
private static final String TAG = "JobScheduler.Time";
- private static final String ACTION_JOB_EXPIRED =
- "android.content.jobscheduler.JOB_DEADLINE_EXPIRED";
- private static final String ACTION_JOB_DELAY_EXPIRED =
- "android.content.jobscheduler.JOB_DELAY_EXPIRED";
- /** Set an alarm for the next job expiry. */
- private final PendingIntent mDeadlineExpiredAlarmIntent;
- /** Set an alarm for the next job delay expiry. This*/
- private final PendingIntent mNextDelayExpiredAlarmIntent;
+ /** Deadline alarm tag for logging purposes */
+ private final String DEADLINE_TAG = "deadline";
+ /** Delay alarm tag for logging purposes */
+ private final String DELAY_TAG = "delay";
private long mNextJobExpiredElapsedMillis;
private long mNextDelayExpiredElapsedMillis;
@@ -68,19 +61,9 @@
private TimeController(StateChangedListener stateChangedListener, Context context) {
super(stateChangedListener, context);
- mDeadlineExpiredAlarmIntent =
- PendingIntent.getBroadcast(mContext, 0 /* ignored */,
- new Intent(ACTION_JOB_EXPIRED), 0);
- mNextDelayExpiredAlarmIntent =
- PendingIntent.getBroadcast(mContext, 0 /* ignored */,
- new Intent(ACTION_JOB_DELAY_EXPIRED), 0);
+
mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
-
- // Register BR for these intents.
- IntentFilter intentFilter = new IntentFilter(ACTION_JOB_EXPIRED);
- intentFilter.addAction(ACTION_JOB_DELAY_EXPIRED);
- mContext.registerReceiver(mAlarmExpiredReceiver, intentFilter);
}
/**
@@ -224,7 +207,7 @@
private void setDelayExpiredAlarm(long alarmTimeElapsedMillis) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
- updateAlarmWithPendingIntent(mNextDelayExpiredAlarmIntent, mNextDelayExpiredElapsedMillis);
+ updateAlarmWithListener(DELAY_TAG, mNextDelayExpiredListener, mNextDelayExpiredElapsedMillis);
}
/**
@@ -235,7 +218,7 @@
private void setDeadlineExpiredAlarm(long alarmTimeElapsedMillis) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
- updateAlarmWithPendingIntent(mDeadlineExpiredAlarmIntent, mNextJobExpiredElapsedMillis);
+ updateAlarmWithListener(DEADLINE_TAG, mDeadlineExpiredListener, mNextJobExpiredElapsedMillis);
}
private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
@@ -246,31 +229,39 @@
return proposedAlarmTimeElapsedMillis;
}
- private void updateAlarmWithPendingIntent(PendingIntent pi, long alarmTimeElapsed) {
+ private void updateAlarmWithListener(String tag, OnAlarmListener listener,
+ long alarmTimeElapsed) {
ensureAlarmService();
if (alarmTimeElapsed == Long.MAX_VALUE) {
- mAlarmService.cancel(pi);
+ mAlarmService.cancel(listener);
} else {
if (DEBUG) {
- Slog.d(TAG, "Setting " + pi.getIntent().getAction() + " for: " + alarmTimeElapsed);
+ Slog.d(TAG, "Setting " + tag + " for: " + alarmTimeElapsed);
}
- mAlarmService.set(AlarmManager.ELAPSED_REALTIME, alarmTimeElapsed, pi);
+ mAlarmService.set(AlarmManager.ELAPSED_REALTIME, alarmTimeElapsed,
+ tag, listener, null);
}
}
- private final BroadcastReceiver mAlarmExpiredReceiver = new BroadcastReceiver() {
+ // Job/delay expiration alarm handling
+
+ private final OnAlarmListener mDeadlineExpiredListener = new OnAlarmListener() {
@Override
- public void onReceive(Context context, Intent intent) {
+ public void onAlarm() {
if (DEBUG) {
- Slog.d(TAG, "Just received alarm: " + intent.getAction());
+ Slog.d(TAG, "Deadline-expired alarm fired");
}
- // A job has just expired, so we run through the list of jobs that we have and
- // notify our StateChangedListener.
- if (ACTION_JOB_EXPIRED.equals(intent.getAction())) {
- checkExpiredDeadlinesAndResetAlarm();
- } else if (ACTION_JOB_DELAY_EXPIRED.equals(intent.getAction())) {
- checkExpiredDelaysAndResetAlarm();
+ checkExpiredDeadlinesAndResetAlarm();
+ }
+ };
+
+ private final OnAlarmListener mNextDelayExpiredListener = new OnAlarmListener() {
+ @Override
+ public void onAlarm() {
+ if (DEBUG) {
+ Slog.d(TAG, "Delay-expired alarm fired");
}
+ checkExpiredDelaysAndResetAlarm();
}
};
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 33f39bc..c83012c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -159,8 +159,8 @@
public class NotificationManagerService extends SystemService {
static final String TAG = "NotificationService";
static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
- public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
- && SystemProperties.getBoolean("debug.child_notifs", false);
+ public static final boolean ENABLE_CHILD_NOTIFICATIONS
+ = SystemProperties.getBoolean("debug.child_notifs", true);
static final int MAX_PACKAGE_NOTIFICATIONS = 50;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2f8157e..3a8a988 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9797,6 +9797,15 @@
if (r1.system != r2.system) {
return r1.system ? -1 : 1;
}
+ if (r1.activityInfo != null) {
+ return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName);
+ }
+ if (r1.serviceInfo != null) {
+ return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName);
+ }
+ if (r1.providerInfo != null) {
+ return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName);
+ }
return 0;
}
};
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index f9ed760..b18c846 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -103,11 +103,11 @@
return runUninstall();
case "resolve-activity":
return runResolveActivity();
- case "query-intent-activities":
+ case "query-activities":
return runQueryIntentActivities();
- case "query-intent-services":
+ case "query-services":
return runQueryIntentServices();
- case "query-intent-receivers":
+ case "query-receivers":
return runQueryIntentReceivers();
default:
return handleDefaultCommands(cmd);
@@ -1043,13 +1043,13 @@
pw.println(" -s: short summary");
pw.println(" -d: only list dangerous permissions");
pw.println(" -u: list only the permissions users will see");
- pw.println(" resolve-intent [--user USER_ID] INTENT");
+ pw.println(" resolve-activity [--user USER_ID] INTENT");
pw.println(" Prints the activity that resolves to the given Intent.");
- pw.println(" query-intent-activities [--user USER_ID] INTENT");
+ pw.println(" query-activities [--user USER_ID] INTENT");
pw.println(" Prints all activities that can handle the given Intent.");
- pw.println(" query-intent-services [--user USER_ID] INTENT");
+ pw.println(" query-services [--user USER_ID] INTENT");
pw.println(" Prints all services that can handle the given Intent.");
- pw.println(" query-intent-receivers [--user USER_ID] INTENT");
+ pw.println(" query-receivers [--user USER_ID] INTENT");
pw.println(" Prints all broadcast receivers that can handle the given Intent.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dbbbb58..0c606fe 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2534,26 +2534,32 @@
win.mAttrs.height = bottom - top;
win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
- if (SHOW_TRANSACTIONS) {
- Slog.i(TAG_WM, ">>> OPEN TRANSACTION repositionChild");
- }
+ if (win.mHasSurface) {
+ if (SHOW_TRANSACTIONS) {
+ Slog.i(TAG_WM, ">>> OPEN TRANSACTION repositionChild");
+ }
- SurfaceControl.openTransaction();
+ SurfaceControl.openTransaction();
- win.applyGravityAndUpdateFrame();
- win.mWinAnimator.computeShownFrameLocked();
+ try {
- win.mWinAnimator.setSurfaceBoundariesLocked(false);
+ win.applyGravityAndUpdateFrame();
+ win.mWinAnimator.computeShownFrameLocked();
- if (deferTransactionUntilFrame > 0) {
- win.mWinAnimator.mSurfaceController.deferTransactionUntil(
- win.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
- deferTransactionUntilFrame);
- }
+ win.mWinAnimator.setSurfaceBoundariesLocked(false);
- SurfaceControl.closeTransaction();
- if (SHOW_TRANSACTIONS) {
- Slog.i(TAG_WM, "<<< CLOSE TRANSACTION repositionChild");
+ if (deferTransactionUntilFrame > 0) {
+ win.mWinAnimator.mSurfaceController.deferTransactionUntil(
+ win.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
+ deferTransactionUntilFrame);
+ }
+
+ } finally {
+ SurfaceControl.closeTransaction();
+ if (SHOW_TRANSACTIONS) {
+ Slog.i(TAG_WM, "<<< CLOSE TRANSACTION repositionChild");
+ }
+ }
}
outFrame = win.mCompatFrame;
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 812d9b6..2329b42 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -389,7 +389,6 @@
}
private void scheduleRenew() {
- mRenewAlarm.cancel();
if (mDhcpLeaseExpiry != 0) {
long now = SystemClock.elapsedRealtime();
long alarmTime = (now + mDhcpLeaseExpiry) / 2;
@@ -822,6 +821,11 @@
return NOT_HANDLED;
}
}
+
+ @Override
+ public void exit() {
+ mRenewAlarm.cancel();
+ }
}
class DhcpRenewingState extends PacketRetransmittingState {
diff --git a/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
index 757f1c6..13657ab 100644
--- a/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
+++ b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
@@ -21,6 +21,7 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
@@ -165,6 +166,11 @@
}
@Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+ sendBroadcast(intent);
+ }
+
+ @Override
public void removeStickyBroadcast(Intent intent) {
// ignored
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 109d214..3efd0fb 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -266,8 +266,6 @@
pw.print(" Session service="); pw.println(mInfo.getSessionService());
pw.println(" Service info:");
mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), " ");
- pw.println(" Application info:");
- mInfo.getServiceInfo().applicationInfo.dump(new PrintWriterPrinter(pw), " ");
pw.print(" Recognition service="); pw.println(mInfo.getRecognitionService());
pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity());
pw.print(" Supports assist="); pw.println(mInfo.getSupportsAssist());
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 12b36b2..6ffc026 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -385,6 +385,11 @@
public static final String KEY_VVM_TYPE_STRING = "vvm_type_string";
/**
+ * Whether cellular data is required to access visual voicemail.
+ */
+ public static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN = "vvm_cellular_data_required";
+
+ /**
* The package name of the carrier's visual voicemail app to ensure that dialer visual voicemail
* and carrier visual voicemail are not active at the same time.
*/
@@ -599,6 +604,7 @@
sDefaults.putString(KEY_VVM_DESTINATION_NUMBER_STRING, "");
sDefaults.putInt(KEY_VVM_PORT_NUMBER_INT, 0);
sDefaults.putString(KEY_VVM_TYPE_STRING, "");
+ sDefaults.putBoolean(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN,false);
sDefaults.putString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING, "");
sDefaults.putBoolean(KEY_CI_ACTION_ON_SYS_UPDATE_BOOL, false);
sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING, "");
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 4c3b598..64d2978 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -433,6 +433,12 @@
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public void sendStickyOrderedBroadcastAsUser(Intent intent,
UserHandle user, BroadcastReceiver resultReceiver,
@@ -695,22 +701,26 @@
}
@Override
- public Context createDeviceEncryptedContext(Context context) {
+ public Context createDeviceEncryptedStorageContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @SystemApi
+ @Override
+ public Context createCredentialEncryptedStorageContext() {
throw new UnsupportedOperationException();
}
@Override
- public Context createCredentialEncryptedContext(Context context) {
+ public boolean isDeviceEncryptedStorage() {
throw new UnsupportedOperationException();
}
+ /** {@hide} */
+ @SystemApi
@Override
- public boolean isDeviceEncrypted() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isCredentialEncrypted() {
+ public boolean isCredentialEncryptedStorage() {
throw new UnsupportedOperationException();
}
}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
index 69b2a9d..0c36063 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
@@ -44,4 +44,12 @@
}
return anchor.getTrustedCert();
}
+
+ public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
+ java.security.cert.TrustAnchor anchor = mIndex.findByIssuerAndSignature(cert);
+ if (anchor == null) {
+ return null;
+ }
+ return anchor.getTrustedCert();
+ }
}
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 21f47bc2..0bb88a7 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -383,6 +383,16 @@
}
}
+static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
+ printf("uses-permission-sdk-23: ");
+
+ printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
+ if (maxSdkVersion != -1) {
+ printf(" maxSdkVersion='%d'", maxSdkVersion);
+ }
+ printf("\n");
+}
+
static void printUsesImpliedPermission(const String8& name, const String8& reason) {
printf("uses-implied-permission: name='%s' reason='%s'\n",
ResTable::normalizeForOutput(name.string()).string(),
@@ -463,12 +473,20 @@
* a pre-requisite or some other reason.
*/
struct ImpliedFeature {
+ ImpliedFeature() : impliedBySdk23(false) {}
+ ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
+
/**
* Name of the implied feature.
*/
String8 name;
/**
+ * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
+ */
+ bool impliedBySdk23;
+
+ /**
* List of human-readable reasons for why this feature was implied.
*/
SortedVector<String8> reasons;
@@ -497,18 +515,24 @@
};
static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
- const char* name, const char* reason) {
+ const char* name, const char* reason, bool sdk23) {
String8 name8(name);
ssize_t idx = impliedFeatures->indexOfKey(name8);
if (idx < 0) {
- idx = impliedFeatures->add(name8, ImpliedFeature());
- impliedFeatures->editValueAt(idx).name = name8;
+ idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
}
- impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
+
+ ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
+
+ // A non-sdk 23 implied feature takes precedence.
+ if (feature->impliedBySdk23 && !sdk23) {
+ feature->impliedBySdk23 = false;
+ }
+ feature->reasons.add(String8(reason));
}
-static void printFeatureGroup(const FeatureGroup& grp,
- const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
+static void printFeatureGroupImpl(const FeatureGroup& grp,
+ const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
printf("feature-group: label='%s'\n", grp.label.string());
if (grp.openGLESVersion > 0) {
@@ -536,9 +560,11 @@
String8 printableFeatureName(ResTable::normalizeForOutput(
impliedFeature.name.string()));
- printf(" uses-feature: name='%s'\n", printableFeatureName.string());
- printf(" uses-implied-feature: name='%s' reason='",
- printableFeatureName.string());
+ const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
+
+ printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
+ printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
+ printableFeatureName.string());
const size_t numReasons = impliedFeature.reasons.size();
for (size_t j = 0; j < numReasons; j++) {
printf("%s", impliedFeature.reasons[j].string());
@@ -552,6 +578,15 @@
}
}
+static void printFeatureGroup(const FeatureGroup& grp) {
+ printFeatureGroupImpl(grp, NULL);
+}
+
+static void printDefaultFeatureGroup(const FeatureGroup& grp,
+ const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
+ printFeatureGroupImpl(grp, &impliedFeatures);
+}
+
static void addParentFeatures(FeatureGroup* grp, const String8& name) {
if (name == "android.hardware.camera.autofocus" ||
name == "android.hardware.camera.flash") {
@@ -572,6 +607,72 @@
}
}
+static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
+ KeyedVector<String8, ImpliedFeature>* impliedFeatures,
+ bool impliedBySdk23Permission) {
+ if (name == "android.permission.CAMERA") {
+ addImpliedFeature(impliedFeatures, "android.hardware.camera",
+ String8::format("requested %s permission", name.string())
+ .string(), impliedBySdk23Permission);
+ } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
+ addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
+ String8::format("requested %s permission", name.string())
+ .string(), impliedBySdk23Permission);
+ addImpliedFeature(impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string(), impliedBySdk23Permission);
+ } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
+ addImpliedFeature(impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string(), impliedBySdk23Permission);
+ } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
+ addImpliedFeature(impliedFeatures, "android.hardware.location.network",
+ String8::format("requested %s permission", name.string())
+ .string(), impliedBySdk23Permission);
+ addImpliedFeature(impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string(), impliedBySdk23Permission);
+ } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
+ name == "android.permission.INSTALL_LOCATION_PROVIDER") {
+ addImpliedFeature(impliedFeatures, "android.hardware.location",
+ String8::format("requested %s permission", name.string())
+ .string(), impliedBySdk23Permission);
+ } else if (name == "android.permission.BLUETOOTH" ||
+ name == "android.permission.BLUETOOTH_ADMIN") {
+ if (targetSdk > 4) {
+ addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
+ String8::format("requested %s permission", name.string())
+ .string(), impliedBySdk23Permission);
+ addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
+ "targetSdkVersion > 4", impliedBySdk23Permission);
+ }
+ } else if (name == "android.permission.RECORD_AUDIO") {
+ addImpliedFeature(impliedFeatures, "android.hardware.microphone",
+ String8::format("requested %s permission", name.string())
+ .string(), impliedBySdk23Permission);
+ } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
+ name == "android.permission.CHANGE_WIFI_STATE" ||
+ name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
+ addImpliedFeature(impliedFeatures, "android.hardware.wifi",
+ String8::format("requested %s permission", name.string())
+ .string(), impliedBySdk23Permission);
+ } else if (name == "android.permission.CALL_PHONE" ||
+ name == "android.permission.CALL_PRIVILEGED" ||
+ name == "android.permission.MODIFY_PHONE_STATE" ||
+ name == "android.permission.PROCESS_OUTGOING_CALLS" ||
+ name == "android.permission.READ_SMS" ||
+ name == "android.permission.RECEIVE_SMS" ||
+ name == "android.permission.RECEIVE_MMS" ||
+ name == "android.permission.RECEIVE_WAP_PUSH" ||
+ name == "android.permission.SEND_SMS" ||
+ name == "android.permission.WRITE_APN_SETTINGS" ||
+ name == "android.permission.WRITE_SMS") {
+ addImpliedFeature(impliedFeatures, "android.hardware.telephony",
+ String8("requested a telephony permission").string(),
+ impliedBySdk23Permission);
+ }
+}
+
/*
* Handle the "dump" command, to extract select data from an archive.
*/
@@ -712,7 +813,8 @@
size_t len;
ResXMLTree::event_code_t code;
int depth = 0;
- while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+ while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
+ code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
depth--;
continue;
@@ -735,25 +837,53 @@
}
String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
- } else if (depth == 2 && tag == "permission") {
- String8 error;
- String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
- if (error != "") {
- fprintf(stderr, "ERROR: %s\n", error.string());
- goto bail;
+ } else if (depth == 2) {
+ if (tag == "permission") {
+ String8 error;
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR: %s\n", error.string());
+ goto bail;
+ }
+
+ if (name == "") {
+ fprintf(stderr, "ERROR: missing 'android:name' for permission\n");
+ goto bail;
+ }
+ printf("permission: %s\n",
+ ResTable::normalizeForOutput(name.string()).string());
+ } else if (tag == "uses-permission") {
+ String8 error;
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR: %s\n", error.string());
+ goto bail;
+ }
+
+ if (name == "") {
+ fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
+ goto bail;
+ }
+ printUsesPermission(name,
+ AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+ AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
+ } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
+ String8 error;
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR: %s\n", error.string());
+ goto bail;
+ }
+
+ if (name == "") {
+ fprintf(stderr, "ERROR: missing 'android:name' for "
+ "uses-permission-sdk-23\n");
+ goto bail;
+ }
+ printUsesPermissionSdk23(
+ name,
+ AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
}
- printf("permission: %s\n",
- ResTable::normalizeForOutput(name.string()).string());
- } else if (depth == 2 && tag == "uses-permission") {
- String8 error;
- String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
- if (error != "") {
- fprintf(stderr, "ERROR: %s\n", error.string());
- goto bail;
- }
- printUsesPermission(name,
- AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
- AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
}
}
} else if (strcmp("badging", option) == 0) {
@@ -893,7 +1023,8 @@
Vector<FeatureGroup> featureGroups;
KeyedVector<String8, ImpliedFeature> impliedFeatures;
- while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+ while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
+ code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
depth--;
if (depth < 2) {
@@ -924,8 +1055,10 @@
ResTable::normalizeForOutput(aName.string()).string());
}
printf(" label='%s' icon='%s'\n",
- ResTable::normalizeForOutput(activityLabel.string()).string(),
- ResTable::normalizeForOutput(activityIcon.string()).string());
+ ResTable::normalizeForOutput(activityLabel.string())
+ .string(),
+ ResTable::normalizeForOutput(activityIcon.string())
+ .string());
}
if (isLeanbackLauncherActivity) {
printf("leanback-launchable-activity:");
@@ -934,9 +1067,12 @@
ResTable::normalizeForOutput(aName.string()).string());
}
printf(" label='%s' icon='%s' banner='%s'\n",
- ResTable::normalizeForOutput(activityLabel.string()).string(),
- ResTable::normalizeForOutput(activityIcon.string()).string(),
- ResTable::normalizeForOutput(activityBanner.string()).string());
+ ResTable::normalizeForOutput(activityLabel.string())
+ .string(),
+ ResTable::normalizeForOutput(activityIcon.string())
+ .string(),
+ ResTable::normalizeForOutput(activityBanner.string())
+ .string());
}
}
if (!hasIntentFilter) {
@@ -964,18 +1100,21 @@
hasLauncher |= catLauncher;
hasCameraActivity |= actCamera;
hasCameraSecureActivity |= actCameraSecure;
- hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
+ hasOtherActivities |=
+ !actMainActivity && !actCamera && !actCameraSecure;
} else if (withinReceiver) {
hasWidgetReceivers |= actWidgetReceivers;
hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
hasBindDeviceAdminPermission);
- hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
+ hasOtherReceivers |=
+ (!actWidgetReceivers && !actDeviceAdminEnabled);
} else if (withinService) {
hasImeService |= actImeService;
hasWallpaperService |= actWallpaperService;
hasAccessibilityService |= (actAccessibilityService &&
hasBindAccessibilityServicePermission);
- hasPrintService |= (actPrintService && hasBindPrintServicePermission);
+ hasPrintService |=
+ (actPrintService && hasBindPrintServicePermission);
hasNotificationListenerService |= actNotificationListenerService &&
hasBindNotificationListenerServicePermission;
hasDreamService |= actDreamService && hasBindDreamServicePermission;
@@ -984,7 +1123,8 @@
!actHostApduService && !actOffHostApduService &&
!actNotificationListenerService);
} else if (withinProvider) {
- hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
+ hasDocumentsProvider |=
+ actDocumentsProvider && hasRequiredSafAttributes;
}
}
withinIntentFilter = false;
@@ -1125,7 +1265,8 @@
goto bail;
}
- String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, &error);
+ String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
+ &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
error.string());
@@ -1135,7 +1276,8 @@
ResTable::normalizeForOutput(label.string()).string());
printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
if (banner != "") {
- printf(" banner='%s'", ResTable::normalizeForOutput(banner.string()).string());
+ printf(" banner='%s'",
+ ResTable::normalizeForOutput(banner.string()).string());
}
printf("\n");
if (testOnly != 0) {
@@ -1178,13 +1320,15 @@
}
}
} else if (tag == "uses-sdk") {
- int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
+ int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
+ &error);
if (error != "") {
error = "";
String8 name = AaptXml::getResolvedAttribute(res, tree,
MIN_SDK_VERSION_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
+ fprintf(stderr,
+ "ERROR getting 'android:minSdkVersion' attribute: %s\n",
error.string());
goto bail;
}
@@ -1205,7 +1349,8 @@
String8 name = AaptXml::getResolvedAttribute(res, tree,
TARGET_SDK_VERSION_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
+ fprintf(stderr,
+ "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
error.string());
goto bail;
}
@@ -1297,90 +1442,58 @@
}
} else if (tag == "uses-permission") {
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
- if (name != "" && error == "") {
- if (name == "android.permission.CAMERA") {
- addImpliedFeature(&impliedFeatures, "android.hardware.camera",
- String8::format("requested %s permission", name.string())
- .string());
- } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
- addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
- String8::format("requested %s permission", name.string())
- .string());
- addImpliedFeature(&impliedFeatures, "android.hardware.location",
- String8::format("requested %s permission", name.string())
- .string());
- } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
- addImpliedFeature(&impliedFeatures, "android.hardware.location",
- String8::format("requested %s permission", name.string())
- .string());
- } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
- addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
- String8::format("requested %s permission", name.string())
- .string());
- addImpliedFeature(&impliedFeatures, "android.hardware.location",
- String8::format("requested %s permission", name.string())
- .string());
- } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
- name == "android.permission.INSTALL_LOCATION_PROVIDER") {
- addImpliedFeature(&impliedFeatures, "android.hardware.location",
- String8::format("requested %s permission", name.string())
- .string());
- } else if (name == "android.permission.BLUETOOTH" ||
- name == "android.permission.BLUETOOTH_ADMIN") {
- if (targetSdk > 4) {
- addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
- String8::format("requested %s permission", name.string())
- .string());
- addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
- "targetSdkVersion > 4");
- }
- } else if (name == "android.permission.RECORD_AUDIO") {
- addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
- String8::format("requested %s permission", name.string())
- .string());
- } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
- name == "android.permission.CHANGE_WIFI_STATE" ||
- name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
- addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
- String8::format("requested %s permission", name.string())
- .string());
- } else if (name == "android.permission.CALL_PHONE" ||
- name == "android.permission.CALL_PRIVILEGED" ||
- name == "android.permission.MODIFY_PHONE_STATE" ||
- name == "android.permission.PROCESS_OUTGOING_CALLS" ||
- name == "android.permission.READ_SMS" ||
- name == "android.permission.RECEIVE_SMS" ||
- name == "android.permission.RECEIVE_MMS" ||
- name == "android.permission.RECEIVE_WAP_PUSH" ||
- name == "android.permission.SEND_SMS" ||
- name == "android.permission.WRITE_APN_SETTINGS" ||
- name == "android.permission.WRITE_SMS") {
- addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
- String8("requested a telephony permission").string());
- } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
- hasWriteExternalStoragePermission = true;
- } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
- hasReadExternalStoragePermission = true;
- } else if (name == "android.permission.READ_PHONE_STATE") {
- hasReadPhoneStatePermission = true;
- } else if (name == "android.permission.READ_CONTACTS") {
- hasReadContactsPermission = true;
- } else if (name == "android.permission.WRITE_CONTACTS") {
- hasWriteContactsPermission = true;
- } else if (name == "android.permission.READ_CALL_LOG") {
- hasReadCallLogPermission = true;
- } else if (name == "android.permission.WRITE_CALL_LOG") {
- hasWriteCallLogPermission = true;
- }
-
- printUsesPermission(name,
- AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
- AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
- } else {
+ if (error != "") {
fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
error.string());
goto bail;
}
+
+ if (name == "") {
+ fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
+ goto bail;
+ }
+
+ addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
+
+ if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
+ hasWriteExternalStoragePermission = true;
+ } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
+ hasReadExternalStoragePermission = true;
+ } else if (name == "android.permission.READ_PHONE_STATE") {
+ hasReadPhoneStatePermission = true;
+ } else if (name == "android.permission.READ_CONTACTS") {
+ hasReadContactsPermission = true;
+ } else if (name == "android.permission.WRITE_CONTACTS") {
+ hasWriteContactsPermission = true;
+ } else if (name == "android.permission.READ_CALL_LOG") {
+ hasReadCallLogPermission = true;
+ } else if (name == "android.permission.WRITE_CALL_LOG") {
+ hasWriteCallLogPermission = true;
+ }
+
+ printUsesPermission(name,
+ AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+ AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
+
+ } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
+ String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
+ error.string());
+ goto bail;
+ }
+
+ if (name == "") {
+ fprintf(stderr, "ERROR: missing 'android:name' for "
+ "uses-permission-sdk-23\n");
+ goto bail;
+ }
+
+ addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
+
+ printUsesPermissionSdk23(
+ name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
+
} else if (tag == "uses-package") {
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
@@ -1422,7 +1535,8 @@
} else if (tag == "package-verifier") {
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
+ String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
+ &error);
if (publicKey != "" && error == "") {
printf("package-verifier: name='%s' publicKey='%s'\n",
ResTable::normalizeForOutput(name.string()).string(),
@@ -1485,12 +1599,18 @@
if (error == "") {
if (orien == 0 || orien == 6 || orien == 8) {
// Requests landscape, sensorLandscape, or reverseLandscape.
- addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
- "one or more activities have specified a landscape orientation");
+ addImpliedFeature(&impliedFeatures,
+ "android.hardware.screen.landscape",
+ "one or more activities have specified a "
+ "landscape orientation",
+ false);
} else if (orien == 1 || orien == 7 || orien == 9) {
// Requests portrait, sensorPortrait, or reversePortrait.
- addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
- "one or more activities have specified a portrait orientation");
+ addImpliedFeature(&impliedFeatures,
+ "android.hardware.screen.portrait",
+ "one or more activities have specified a "
+ "portrait orientation",
+ false);
}
}
} else if (tag == "uses-library") {
@@ -1524,8 +1644,10 @@
hasBindDeviceAdminPermission = true;
}
} else {
- fprintf(stderr, "ERROR getting 'android:permission' attribute for"
- " receiver '%s': %s\n", receiverName.string(), error.string());
+ fprintf(stderr,
+ "ERROR getting 'android:permission' attribute for"
+ " receiver '%s': %s\n",
+ receiverName.string(), error.string());
}
} else if (tag == "service") {
withinService = true;
@@ -1542,20 +1664,24 @@
if (error == "") {
if (permission == "android.permission.BIND_INPUT_METHOD") {
hasBindInputMethodPermission = true;
- } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
+ } else if (permission ==
+ "android.permission.BIND_ACCESSIBILITY_SERVICE") {
hasBindAccessibilityServicePermission = true;
- } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
+ } else if (permission ==
+ "android.permission.BIND_PRINT_SERVICE") {
hasBindPrintServicePermission = true;
- } else if (permission == "android.permission.BIND_NFC_SERVICE") {
+ } else if (permission ==
+ "android.permission.BIND_NFC_SERVICE") {
hasBindNfcServicePermission = true;
- } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
+ } else if (permission ==
+ "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
hasBindNotificationListenerServicePermission = true;
} else if (permission == "android.permission.BIND_DREAM_SERVICE") {
hasBindDreamServicePermission = true;
}
} else {
- fprintf(stderr, "ERROR getting 'android:permission' attribute for"
- " service '%s': %s\n", serviceName.string(), error.string());
+ fprintf(stderr, "ERROR getting 'android:permission' attribute for "
+ "service '%s': %s\n", serviceName.string(), error.string());
}
} else if (tag == "provider") {
withinProvider = true;
@@ -1563,7 +1689,8 @@
bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
EXPORTED_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
+ fprintf(stderr,
+ "ERROR getting 'android:exported' attribute for provider:"
" %s\n", error.string());
goto bail;
}
@@ -1571,16 +1698,17 @@
bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
- " %s\n", error.string());
+ fprintf(stderr,
+ "ERROR getting 'android:grantUriPermissions' attribute for "
+ "provider: %s\n", error.string());
goto bail;
}
String8 permission = AaptXml::getResolvedAttribute(res, tree,
PERMISSION_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
- " %s\n", error.string());
+ fprintf(stderr, "ERROR getting 'android:permission' attribute for "
+ "provider: %s\n", error.string());
goto bail;
}
@@ -1661,8 +1789,9 @@
} else if (withinService && tag == "meta-data") {
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:name' attribute for"
- " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
+ fprintf(stderr, "ERROR getting 'android:name' attribute for "
+ "meta-data tag in service '%s': %s\n", serviceName.string(),
+ error.string());
goto bail;
}
@@ -1676,8 +1805,9 @@
String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
RESOURCE_ATTR, &error);
if (error != "") {
- fprintf(stderr, "ERROR getting 'android:resource' attribute for"
- " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
+ fprintf(stderr, "ERROR getting 'android:resource' attribute for "
+ "meta-data tag in service '%s': %s\n",
+ serviceName.string(), error.string());
goto bail;
}
@@ -1731,15 +1861,19 @@
actImeService = true;
} else if (action == "android.service.wallpaper.WallpaperService") {
actWallpaperService = true;
- } else if (action == "android.accessibilityservice.AccessibilityService") {
+ } else if (action ==
+ "android.accessibilityservice.AccessibilityService") {
actAccessibilityService = true;
- } else if (action == "android.printservice.PrintService") {
+ } else if (action =="android.printservice.PrintService") {
actPrintService = true;
- } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
+ } else if (action ==
+ "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
actHostApduService = true;
- } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
+ } else if (action ==
+ "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
actOffHostApduService = true;
- } else if (action == "android.service.notification.NotificationListenerService") {
+ } else if (action ==
+ "android.service.notification.NotificationListenerService") {
actNotificationListenerService = true;
} else if (action == "android.service.dreams.DreamService") {
actDreamService = true;
@@ -1814,12 +1948,12 @@
}
addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
- "default feature for all apps");
+ "default feature for all apps", false);
const size_t numFeatureGroups = featureGroups.size();
if (numFeatureGroups == 0) {
// If no <feature-group> tags were defined, apply auto-implied features.
- printFeatureGroup(commonFeatures, &impliedFeatures);
+ printDefaultFeatureGroup(commonFeatures, impliedFeatures);
} else {
// <feature-group> tags are defined, so we ignore implied features and
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index d8e0aac..0f83980 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -31,6 +31,8 @@
flatten/Archive.cpp \
flatten/TableFlattener.cpp \
flatten/XmlFlattener.cpp \
+ io/FileSystem.cpp \
+ io/ZipArchive.cpp \
link/AutoVersioner.cpp \
link/ManifestFixer.cpp \
link/PrivateAttributeMover.cpp \
diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h
index 6d752bb..054b9ee 100644
--- a/tools/aapt2/NameMangler.h
+++ b/tools/aapt2/NameMangler.h
@@ -60,7 +60,7 @@
};
}
- bool shouldMangle(const std::u16string& package) {
+ bool shouldMangle(const std::u16string& package) const {
if (package.empty() || mPolicy.targetPackageName == package) {
return false;
}
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 34dc1d5..9328b69 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -85,4 +85,12 @@
return &iter->second;
}
+bool operator<(const ResourceKey& a, const ResourceKey& b) {
+ return std::tie(a.name, a.config) < std::tie(b.name, b.config);
+}
+
+bool operator<(const ResourceKeyRef& a, const ResourceKeyRef& b) {
+ return std::tie(a.name, a.config) < std::tie(b.name, b.config);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index a7afbb5..c71e249 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -165,6 +165,36 @@
std::vector<SourcedResourceName> exportedSymbols;
};
+/**
+ * Useful struct used as a key to represent a unique resource in associative containers.
+ */
+struct ResourceKey {
+ ResourceName name;
+ ConfigDescription config;
+};
+
+bool operator<(const ResourceKey& a, const ResourceKey& b);
+
+/**
+ * Useful struct used as a key to represent a unique resource in associative containers.
+ * Holds a reference to the name, so that name better live longer than this key!
+ */
+struct ResourceKeyRef {
+ ResourceNameRef name;
+ ConfigDescription config;
+
+ ResourceKeyRef() = default;
+ ResourceKeyRef(const ResourceNameRef& n, const ConfigDescription& c) : name(n), config(c) {
+ }
+
+ /**
+ * Prevent taking a reference to a temporary. This is bad.
+ */
+ ResourceKeyRef(ResourceName&& n, const ConfigDescription& c) = delete;
+};
+
+bool operator<(const ResourceKeyRef& a, const ResourceKeyRef& b);
+
//
// ResourceId implementation.
//
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index c2ddb5c..d4c536f 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -173,7 +173,7 @@
ResourceName name;
Source source;
ResourceId id;
- SymbolState symbolState = SymbolState::kUndefined;
+ Maybe<SymbolState> symbolState;
std::u16string comment;
std::unique_ptr<Value> value;
std::list<ParsedResource> childResources;
@@ -182,9 +182,9 @@
// Recursively adds resources to the ResourceTable.
static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& config,
IDiagnostics* diag, ParsedResource* res) {
- if (res->symbolState != SymbolState::kUndefined) {
+ if (res->symbolState) {
Symbol symbol;
- symbol.state = res->symbolState;
+ symbol.state = res->symbolState.value();
symbol.source = res->source;
symbol.comment = res->comment;
if (!table->setSymbolState(res->name, res->id, symbol, diag)) {
@@ -325,6 +325,8 @@
result = parseSymbol(parser, &parsedResource);
} else if (elementName == u"public-group") {
result = parsePublicGroup(parser, &parsedResource);
+ } else if (elementName == u"add-resource") {
+ result = parseAddResource(parser, &parsedResource);
} else {
// Try parsing the elementName (or type) as a resource. These shall only be
// resources like 'layout' or 'xml' and they can only be references.
@@ -643,7 +645,7 @@
return !error;
}
-bool ResourceParser::parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource) {
const Source source = mSource.withLine(parser->getLineNumber());
Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
@@ -661,10 +663,25 @@
}
outResource->name.type = *parsedType;
- outResource->symbolState = SymbolState::kPrivate;
return true;
}
+bool ResourceParser::parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource) {
+ if (parseSymbolImpl(parser, outResource)) {
+ outResource->symbolState = SymbolState::kPrivate;
+ return true;
+ }
+ return false;
+}
+
+bool ResourceParser::parseAddResource(xml::XmlPullParser* parser, ParsedResource* outResource) {
+ if (parseSymbolImpl(parser, outResource)) {
+ outResource->symbolState = SymbolState::kUndefined;
+ return true;
+ }
+ return false;
+}
+
static uint32_t parseFormatType(const StringPiece16& piece) {
if (piece == u"reference") return android::ResTable_map::TYPE_REFERENCE;
else if (piece == u"string") return android::ResTable_map::TYPE_STRING;
@@ -870,7 +887,7 @@
}
return Attribute::Symbol{
- Reference(ResourceName({}, ResourceType::kId, maybeName.value().toString())),
+ Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())),
val.data };
}
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 1150758..04db577 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -83,7 +83,9 @@
bool parsePrimitive(xml::XmlPullParser* parser, ParsedResource* outResource);
bool parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource);
bool parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource);
bool parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseAddResource(xml::XmlPullParser* parser, ParsedResource* outResource);
bool parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource);
bool parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource, bool weak);
Maybe<Attribute::Symbol> parseEnumOrFlagItem(xml::XmlPullParser* parser,
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 6e0812b..84f67c6 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -563,4 +563,16 @@
ASSERT_FALSE(testParse(input));
}
+TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
+ std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
+ ASSERT_TRUE(testParse(input));
+
+ Maybe<ResourceTable::SearchResult> result = mTable.findResource(
+ test::parseNameOrDie(u"@string/bar"));
+ AAPT_ASSERT_TRUE(result);
+ const ResourceEntry* entry = result.value().entry;
+ ASSERT_NE(nullptr, entry);
+ EXPECT_EQ(SymbolState::kUndefined, entry->symbolStatus.state);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 73d8585..8a3d047 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -101,7 +101,7 @@
if (iter != last && (*iter)->type == type) {
return iter->get();
}
- return types.emplace(iter, new ResourceTableType{ type })->get();
+ return types.emplace(iter, new ResourceTableType(type))->get();
}
ResourceEntry* ResourceTableType::findEntry(const StringPiece16& name) {
@@ -121,7 +121,7 @@
if (iter != last && name == (*iter)->name) {
return iter->get();
}
- return entries.emplace(iter, new ResourceEntry{ name })->get();
+ return entries.emplace(iter, new ResourceEntry(name))->get();
}
/**
@@ -342,11 +342,6 @@
IDiagnostics* diag) {
assert(diag && "diagnostics can't be nullptr");
- if (symbol.state == SymbolState::kUndefined) {
- // Nothing to do.
- return true;
- }
-
auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
if (badCharIter != name.entry.end()) {
diag->error(DiagMessage(symbol.source)
@@ -400,23 +395,30 @@
return false;
}
- // Only mark the type state as public, it doesn't care about being private.
- if (symbol.state == SymbolState::kPublic) {
- type->symbolStatus.state = SymbolState::kPublic;
- }
-
- // Downgrading to a private symbol from a public one is not allowed.
- if (entry->symbolStatus.state != SymbolState::kPublic) {
- if (entry->symbolStatus.state != symbol.state) {
- entry->symbolStatus = std::move(symbol);
- }
- }
-
if (resId.isValid()) {
package->id = resId.packageId();
type->id = resId.typeId();
entry->id = resId.entryId();
}
+
+ // Only mark the type state as public, it doesn't care about being private.
+ if (symbol.state == SymbolState::kPublic) {
+ type->symbolStatus.state = SymbolState::kPublic;
+ }
+
+ if (symbol.state == SymbolState::kUndefined &&
+ entry->symbolStatus.state != SymbolState::kUndefined) {
+ // We can't undefine a symbol (remove its visibility). Ignore.
+ return true;
+ }
+
+ if (symbol.state == SymbolState::kPrivate &&
+ entry->symbolStatus.state == SymbolState::kPublic) {
+ // We can't downgrade public to private. Ignore.
+ return true;
+ }
+
+ entry->symbolStatus = std::move(symbol);
return true;
}
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index ffe6595..36c3e70 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -14,8 +14,10 @@
* limitations under the License.
*/
+#include "NameMangler.h"
#include "ResourceUtils.h"
#include "flatten/ResourceTypeExtensions.h"
+#include "util/Files.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
@@ -554,5 +556,22 @@
return {};
}
+std::string buildResourceFileName(const ResourceFile& resFile, const NameMangler* mangler) {
+ std::stringstream out;
+ out << "res/" << resFile.name.type;
+ if (resFile.config != ConfigDescription{}) {
+ out << "-" << resFile.config;
+ }
+ out << "/";
+
+ if (mangler && mangler->shouldMangle(resFile.name.package)) {
+ out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
+ } else {
+ out << resFile.name.entry;
+ }
+ out << file::getExtension(resFile.source.path);
+ return out.str();
+}
+
} // namespace ResourceUtils
} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index f93a4c7..64ca971 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -17,6 +17,7 @@
#ifndef AAPT_RESOURCEUTILS_H
#define AAPT_RESOURCEUTILS_H
+#include "NameMangler.h"
#include "Resource.h"
#include "ResourceValues.h"
#include "util/StringPiece.h"
@@ -154,6 +155,16 @@
uint32_t androidTypeToAttributeTypeMask(uint16_t type);
+/**
+ * Returns a string path suitable for use within an APK. The path will look like:
+ *
+ * res/type[-config]/<name>.<ext>
+ *
+ * Then name may be mangled if a NameMangler is supplied (can be nullptr) and the package
+ * requires mangling.
+ */
+std::string buildResourceFileName(const ResourceFile& resFile, const NameMangler* mangler);
+
} // namespace ResourceUtils
} // namespace aapt
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index 46ee914..a2f53e1 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -583,8 +583,8 @@
publicEntry->state = Public_entry::kPublic;
break;
- default:
- assert(false && "should not serialize any other state");
+ case SymbolState::kUndefined:
+ publicEntry->state = Public_entry::kUndefined;
break;
}
diff --git a/tools/aapt2/io/File.h b/tools/aapt2/io/File.h
index 9fca398..b4d4971 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -52,6 +52,14 @@
virtual const Source& getSource() const = 0;
};
+class IFileCollectionIterator {
+public:
+ virtual ~IFileCollectionIterator() = default;
+
+ virtual bool hasNext() = 0;
+ virtual IFile* next() = 0;
+};
+
/**
* Interface for a collection of files, all of which share a common source. That source may
* simply be the filesystem, or a ZIP archive.
@@ -60,10 +68,8 @@
public:
virtual ~IFileCollection() = default;
- using const_iterator = std::vector<std::unique_ptr<IFile>>::const_iterator;
-
- virtual const_iterator begin() const = 0;
- virtual const_iterator end() const = 0;
+ virtual IFile* findFile(const StringPiece& path) = 0;
+ virtual std::unique_ptr<IFileCollectionIterator> iterator() = 0;
};
} // namespace io
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
new file mode 100644
index 0000000..76f87ae
--- /dev/null
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#include "Source.h"
+#include "io/FileSystem.h"
+#include "util/Files.h"
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <utils/FileMap.h>
+
+namespace aapt {
+namespace io {
+
+RegularFile::RegularFile(const Source& source) : mSource(source) {
+}
+
+std::unique_ptr<IData> RegularFile::openAsData() {
+ android::FileMap map;
+ if (Maybe<android::FileMap> map = file::mmapPath(mSource.path, nullptr)) {
+ return util::make_unique<MmappedData>(std::move(map.value()));
+ }
+ return {};
+}
+
+const Source& RegularFile::getSource() const {
+ return mSource;
+}
+
+FileCollectionIterator::FileCollectionIterator(FileCollection* collection) :
+ mCurrent(collection->mFiles.begin()), mEnd(collection->mFiles.end()) {
+}
+
+bool FileCollectionIterator::hasNext() {
+ return mCurrent != mEnd;
+}
+
+IFile* FileCollectionIterator::next() {
+ IFile* result = mCurrent->second.get();
+ ++mCurrent;
+ return result;
+}
+
+IFile* FileCollection::insertFile(const StringPiece& path) {
+ return (mFiles[path.toString()] = util::make_unique<RegularFile>(Source(path))).get();
+}
+
+IFile* FileCollection::findFile(const StringPiece& path) {
+ auto iter = mFiles.find(path.toString());
+ if (iter != mFiles.end()) {
+ return iter->second.get();
+ }
+ return nullptr;
+}
+
+std::unique_ptr<IFileCollectionIterator> FileCollection::iterator() {
+ return util::make_unique<FileCollectionIterator>(this);
+}
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h
index 5dbefcc..f0559c0 100644
--- a/tools/aapt2/io/FileSystem.h
+++ b/tools/aapt2/io/FileSystem.h
@@ -18,7 +18,8 @@
#define AAPT_IO_FILESYSTEM_H
#include "io/File.h"
-#include "util/Files.h"
+
+#include <map>
namespace aapt {
namespace io {
@@ -28,25 +29,28 @@
*/
class RegularFile : public IFile {
public:
- RegularFile(const Source& source) : mSource(source) {
- }
+ RegularFile(const Source& source);
- std::unique_ptr<IData> openAsData() override {
- android::FileMap map;
- if (Maybe<android::FileMap> map = file::mmapPath(mSource.path, nullptr)) {
- return util::make_unique<MmappedData>(std::move(map.value()));
- }
- return {};
- }
-
- const Source& getSource() const override {
- return mSource;
- }
+ std::unique_ptr<IData> openAsData() override;
+ const Source& getSource() const override;
private:
Source mSource;
};
+class FileCollection;
+
+class FileCollectionIterator : public IFileCollectionIterator {
+public:
+ FileCollectionIterator(FileCollection* collection);
+
+ bool hasNext() override;
+ io::IFile* next() override;
+
+private:
+ std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
+};
+
/**
* An IFileCollection representing the file system.
*/
@@ -55,21 +59,13 @@
/**
* Adds a file located at path. Returns the IFile representation of that file.
*/
- IFile* insertFile(const StringPiece& path) {
- mFiles.push_back(util::make_unique<RegularFile>(Source(path)));
- return mFiles.back().get();
- }
-
- const_iterator begin() const override {
- return mFiles.begin();
- }
-
- const_iterator end() const override {
- return mFiles.end();
- }
+ IFile* insertFile(const StringPiece& path);
+ IFile* findFile(const StringPiece& path) override;
+ std::unique_ptr<IFileCollectionIterator> iterator() override;
private:
- std::vector<std::unique_ptr<IFile>> mFiles;
+ friend class FileCollectionIterator;
+ std::map<std::string, std::unique_ptr<IFile>> mFiles;
};
} // namespace io
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
new file mode 100644
index 0000000..bf0f4aa
--- /dev/null
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+#include "Source.h"
+#include "io/ZipArchive.h"
+#include "util/Util.h"
+
+#include <utils/FileMap.h>
+#include <ziparchive/zip_archive.h>
+
+namespace aapt {
+namespace io {
+
+ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source) :
+ mZipHandle(handle), mZipEntry(entry), mSource(source) {
+}
+
+std::unique_ptr<IData> ZipFile::openAsData() {
+ if (mZipEntry.method == kCompressStored) {
+ int fd = GetFileDescriptor(mZipHandle);
+
+ android::FileMap fileMap;
+ bool result = fileMap.create(nullptr, fd, mZipEntry.offset,
+ mZipEntry.uncompressed_length, true);
+ if (!result) {
+ return {};
+ }
+ return util::make_unique<MmappedData>(std::move(fileMap));
+
+ } else {
+ std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(
+ new uint8_t[mZipEntry.uncompressed_length]);
+ int32_t result = ExtractToMemory(mZipHandle, &mZipEntry, data.get(),
+ static_cast<uint32_t>(mZipEntry.uncompressed_length));
+ if (result != 0) {
+ return {};
+ }
+ return util::make_unique<MallocData>(std::move(data), mZipEntry.uncompressed_length);
+ }
+}
+
+const Source& ZipFile::getSource() const {
+ return mSource;
+}
+
+ZipFileCollectionIterator::ZipFileCollectionIterator(ZipFileCollection* collection) :
+ mCurrent(collection->mFiles.begin()), mEnd(collection->mFiles.end()) {
+}
+
+bool ZipFileCollectionIterator::hasNext() {
+ return mCurrent != mEnd;
+}
+
+IFile* ZipFileCollectionIterator::next() {
+ IFile* result = mCurrent->second.get();
+ ++mCurrent;
+ return result;
+}
+
+ZipFileCollection::ZipFileCollection() : mHandle(nullptr) {
+}
+
+std::unique_ptr<ZipFileCollection> ZipFileCollection::create(const StringPiece& path,
+ std::string* outError) {
+ std::unique_ptr<ZipFileCollection> collection = std::unique_ptr<ZipFileCollection>(
+ new ZipFileCollection());
+
+ int32_t result = OpenArchive(path.data(), &collection->mHandle);
+ if (result != 0) {
+ if (outError) *outError = ErrorCodeString(result);
+ return {};
+ }
+
+ ZipString suffix(".flat");
+ void* cookie = nullptr;
+ result = StartIteration(collection->mHandle, &cookie, nullptr, &suffix);
+ if (result != 0) {
+ if (outError) *outError = ErrorCodeString(result);
+ return {};
+ }
+
+ using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
+ IterationEnder iterationEnder(cookie, EndIteration);
+
+ ZipString zipEntryName;
+ ZipEntry zipData;
+ while ((result = Next(cookie, &zipData, &zipEntryName)) == 0) {
+ std::string zipEntryPath = std::string(reinterpret_cast<const char*>(zipEntryName.name),
+ zipEntryName.name_length);
+ std::string nestedPath = path.toString() + "@" + zipEntryPath;
+ collection->mFiles[zipEntryPath] = util::make_unique<ZipFile>(collection->mHandle,
+ zipData,
+ Source(nestedPath));
+ }
+
+ if (result != -1) {
+ if (outError) *outError = ErrorCodeString(result);
+ return {};
+ }
+ return collection;
+}
+
+IFile* ZipFileCollection::findFile(const StringPiece& path) {
+ auto iter = mFiles.find(path.toString());
+ if (iter != mFiles.end()) {
+ return iter->second.get();
+ }
+ return nullptr;
+}
+
+std::unique_ptr<IFileCollectionIterator> ZipFileCollection::iterator() {
+ return util::make_unique<ZipFileCollectionIterator>(this);
+}
+
+ZipFileCollection::~ZipFileCollection() {
+ if (mHandle) {
+ CloseArchive(mHandle);
+ }
+}
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h
index 98afc49..5ad119d 100644
--- a/tools/aapt2/io/ZipArchive.h
+++ b/tools/aapt2/io/ZipArchive.h
@@ -20,7 +20,7 @@
#include "io/File.h"
#include "util/StringPiece.h"
-#include <utils/FileMap.h>
+#include <map>
#include <ziparchive/zip_archive.h>
namespace aapt {
@@ -32,37 +32,10 @@
*/
class ZipFile : public IFile {
public:
- ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source) :
- mZipHandle(handle), mZipEntry(entry), mSource(source) {
- }
+ ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source);
- std::unique_ptr<IData> openAsData() override {
- if (mZipEntry.method == kCompressStored) {
- int fd = GetFileDescriptor(mZipHandle);
-
- android::FileMap fileMap;
- bool result = fileMap.create(nullptr, fd, mZipEntry.offset,
- mZipEntry.uncompressed_length, true);
- if (!result) {
- return {};
- }
- return util::make_unique<MmappedData>(std::move(fileMap));
-
- } else {
- std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(
- new uint8_t[mZipEntry.uncompressed_length]);
- int32_t result = ExtractToMemory(mZipHandle, &mZipEntry, data.get(),
- static_cast<uint32_t>(mZipEntry.uncompressed_length));
- if (result != 0) {
- return {};
- }
- return util::make_unique<MallocData>(std::move(data), mZipEntry.uncompressed_length);
- }
- }
-
- const Source& getSource() const override {
- return mSource;
- }
+ std::unique_ptr<IData> openAsData() override;
+ const Source& getSource() const override;
private:
ZipArchiveHandle mZipHandle;
@@ -70,71 +43,38 @@
Source mSource;
};
+class ZipFileCollection;
+
+class ZipFileCollectionIterator : public IFileCollectionIterator {
+public:
+ ZipFileCollectionIterator(ZipFileCollection* collection);
+
+ bool hasNext() override;
+ io::IFile* next() override;
+
+private:
+ std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
+};
+
/**
* An IFileCollection that represents a ZIP archive and the entries within it.
*/
class ZipFileCollection : public IFileCollection {
public:
static std::unique_ptr<ZipFileCollection> create(const StringPiece& path,
- std::string* outError) {
- std::unique_ptr<ZipFileCollection> collection = std::unique_ptr<ZipFileCollection>(
- new ZipFileCollection());
+ std::string* outError);
- int32_t result = OpenArchive(path.data(), &collection->mHandle);
- if (result != 0) {
- if (outError) *outError = ErrorCodeString(result);
- return {};
- }
+ io::IFile* findFile(const StringPiece& path) override;
+ std::unique_ptr<IFileCollectionIterator> iterator() override;
- ZipString suffix(".flat");
- void* cookie = nullptr;
- result = StartIteration(collection->mHandle, &cookie, nullptr, &suffix);
- if (result != 0) {
- if (outError) *outError = ErrorCodeString(result);
- return {};
- }
-
- using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
- IterationEnder iterationEnder(cookie, EndIteration);
-
- ZipString zipEntryName;
- ZipEntry zipData;
- while ((result = Next(cookie, &zipData, &zipEntryName)) == 0) {
- std::string nestedPath = path.toString();
- nestedPath += "@" + std::string(reinterpret_cast<const char*>(zipEntryName.name),
- zipEntryName.name_length);
- collection->mFiles.push_back(util::make_unique<ZipFile>(collection->mHandle,
- zipData,
- Source(nestedPath)));
- }
-
- if (result != -1) {
- if (outError) *outError = ErrorCodeString(result);
- return {};
- }
- return collection;
- }
-
- const_iterator begin() const override {
- return mFiles.begin();
- }
-
- const_iterator end() const override {
- return mFiles.end();
- }
-
- ~ZipFileCollection() override {
- if (mHandle) {
- CloseArchive(mHandle);
- }
- }
+ ~ZipFileCollection() override;
private:
- ZipFileCollection() : mHandle(nullptr) {
- }
+ friend class ZipFileCollectionIterator;
+ ZipFileCollection();
ZipArchiveHandle mHandle;
- std::vector<std::unique_ptr<IFile>> mFiles;
+ std::map<std::string, std::unique_ptr<IFile>> mFiles;
};
} // namespace io
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 33d9272..652e52f 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -57,6 +57,7 @@
bool staticLib = false;
bool verbose = false;
bool outputToDirectory = false;
+ bool autoAddOverlay = false;
Maybe<std::u16string> privateSymbols;
Maybe<std::u16string> minSdkVersionDefault;
Maybe<std::u16string> targetSdkVersionDefault;
@@ -104,23 +105,6 @@
mCollections.push_back(std::move(fileCollection));
}
- std::string buildResourceFileName(const ResourceFile& resFile) {
- std::stringstream out;
- out << "res/" << resFile.name.type;
- if (resFile.config != ConfigDescription{}) {
- out << "-" << resFile.config;
- }
- out << "/";
-
- if (mContext.getNameMangler()->shouldMangle(resFile.name.package)) {
- out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
- } else {
- out << resFile.name.entry;
- }
- out << file::getExtension(resFile.source.path);
- return out.str();
- }
-
/**
* Creates a SymbolTable that loads symbols from the various APKs and caches the
* results for faster lookup.
@@ -425,45 +409,28 @@
return false;
}
- if (!mTableMerger->merge(file->getSource(), table.get(), override)) {
- return false;
+ bool result = false;
+ if (override) {
+ result = mTableMerger->mergeOverlay(file->getSource(), table.get());
+ } else {
+ result = mTableMerger->merge(file->getSource(), table.get());
}
- return true;
+ return result;
}
- bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool override) {
- // Apply the package name used for this compilation phase if none was specified.
- if (fileDesc->name.package.empty()) {
- fileDesc->name.package = mContext.getCompilationPackage().toString();
+ bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) {
+ if (mOptions.verbose) {
+ mContext.getDiagnostics()->note(DiagMessage() << "adding " << file->getSource());
}
- // Mangle the name if necessary.
- ResourceNameRef resName = fileDesc->name;
- Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(fileDesc->name);
- if (mangledName) {
- resName = mangledName.value();
- }
-
- // If we are overriding resources, we supply a custom resolver function.
- std::function<int(Value*,Value*)> resolver;
- if (override) {
- resolver = [](Value* a, Value* b) -> int {
- int result = ResourceTable::resolveValueCollision(a, b);
- if (result == 0) {
- // Always accept the new value if it would normally conflict (override).
- result = 1;
- }
- return result;
- };
+ bool result = false;
+ if (overlay) {
+ result = mTableMerger->mergeFileOverlay(*fileDesc, file);
} else {
- // Otherwise use the default resolution.
- resolver = ResourceTable::resolveValueCollision;
+ result = mTableMerger->mergeFile(*fileDesc, file);
}
- // Add this file to the table.
- if (!mFinalTable.addFileReference(resName, fileDesc->config, fileDesc->source,
- util::utf8ToUtf16(buildResourceFileName(*fileDesc)),
- resolver, mContext.getDiagnostics())) {
+ if (!result) {
return false;
}
@@ -489,10 +456,6 @@
return false;
}
}
-
- // Now add this file for later processing. Once the table is assigned IDs, we can compile
- // this file.
- mFilesToProcess.insert(FileToProcess{ std::move(fileDesc), file });
return true;
}
@@ -509,8 +472,8 @@
}
bool error = false;
- for (const std::unique_ptr<io::IFile>& file : *collection) {
- if (!processFile(file.get(), override)) {
+ for (auto iter = collection->iterator(); iter->hasNext(); ) {
+ if (!processFile(iter->next(), override)) {
error = true;
}
}
@@ -588,7 +551,9 @@
return 1;
}
- mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable);
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
+ mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable, tableMergerOptions);
if (mOptions.verbose) {
mContext.getDiagnostics()->note(
@@ -698,31 +663,47 @@
return 1;
}
- for (const FileToProcess& file : mFilesToProcess) {
- const StringPiece path = file.file->getSource().path;
+ for (auto& mergeEntry : mTableMerger->getFilesToMerge()) {
+ const ResourceKeyRef& key = mergeEntry.first;
+ const FileToMerge& fileToMerge = mergeEntry.second;
- if (file.fileExport->name.type != ResourceType::kRaw &&
- util::stringEndsWith<char>(path, ".xml.flat")) {
+ const StringPiece path = fileToMerge.file->getSource().path;
+
+ if (key.name.type != ResourceType::kRaw &&
+ (util::stringEndsWith<char>(path, ".xml.flat") ||
+ util::stringEndsWith<char>(path, ".xml"))) {
if (mOptions.verbose) {
mContext.getDiagnostics()->note(DiagMessage() << "linking " << path);
}
- std::unique_ptr<io::IData> data = file.file->openAsData();
+ io::IFile* file = fileToMerge.file;
+ std::unique_ptr<io::IData> data = file->openAsData();
if (!data) {
- mContext.getDiagnostics()->error(DiagMessage(file.file->getSource())
+ mContext.getDiagnostics()->error(DiagMessage(file->getSource())
<< "failed to open file");
return 1;
}
- std::unique_ptr<xml::XmlResource> xmlRes = loadBinaryXmlSkipFileExport(
- file.file->getSource(), data->data(), data->size(),
- mContext.getDiagnostics());
+ std::unique_ptr<xml::XmlResource> xmlRes;
+ if (util::stringEndsWith<char>(path, ".flat")) {
+ xmlRes = loadBinaryXmlSkipFileExport(file->getSource(),
+ data->data(), data->size(),
+ mContext.getDiagnostics());
+ } else {
+ xmlRes = xml::inflate(data->data(), data->size(), mContext.getDiagnostics(),
+ file->getSource());
+ }
+
if (!xmlRes) {
return 1;
}
- // Move the file description over.
- xmlRes->file = std::move(*file.fileExport);
+ // Create the file description header.
+ xmlRes->file = ResourceFile{
+ key.name.toResourceName(),
+ key.config,
+ fileToMerge.originalSource,
+ };
XmlReferenceLinker xmlLinker;
if (xmlLinker.consume(&mContext, xmlRes.get())) {
@@ -736,7 +717,7 @@
maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
}
- if (!flattenXml(xmlRes.get(), buildResourceFileName(xmlRes->file), maxSdkLevel,
+ if (!flattenXml(xmlRes.get(), fileToMerge.dstPath, maxSdkLevel,
archiveWriter.get())) {
error = true;
}
@@ -750,19 +731,23 @@
xmlRes->file.config,
sdkLevel)) {
xmlRes->file.config.sdkVersion = sdkLevel;
+
+ std::string genResourcePath = ResourceUtils::buildResourceFileName(
+ xmlRes->file, mContext.getNameMangler());
+
bool added = mFinalTable.addFileReference(
xmlRes->file.name,
xmlRes->file.config,
xmlRes->file.source,
- util::utf8ToUtf16(buildResourceFileName(xmlRes->file)),
+ util::utf8ToUtf16(genResourcePath),
mContext.getDiagnostics());
if (!added) {
error = true;
continue;
}
- if (!flattenXml(xmlRes.get(), buildResourceFileName(xmlRes->file),
- sdkLevel, archiveWriter.get())) {
+ if (!flattenXml(xmlRes.get(), genResourcePath, sdkLevel,
+ archiveWriter.get())) {
error = true;
}
}
@@ -777,7 +762,7 @@
mContext.getDiagnostics()->note(DiagMessage() << "copying " << path);
}
- if (!copyFileToArchive(file.file, buildResourceFileName(*file.fileExport), 0,
+ if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath, 0,
archiveWriter.get())) {
error = true;
}
@@ -845,15 +830,7 @@
if (mOptions.verbose) {
Debug::printTable(&mFinalTable);
- for (; !mTableMerger->getFileMergeQueue()->empty();
- mTableMerger->getFileMergeQueue()->pop()) {
- const FileToMerge& f = mTableMerger->getFileMergeQueue()->front();
- mContext.getDiagnostics()->note(
- DiagMessage() << f.srcPath << " -> " << f.dstPath << " from (0x"
- << std::hex << (uintptr_t) f.srcTable << std::dec);
- }
}
-
return 0;
}
@@ -861,24 +838,15 @@
LinkOptions mOptions;
LinkContext mContext;
ResourceTable mFinalTable;
+
+ ResourceTable mLocalFileTable;
std::unique_ptr<TableMerger> mTableMerger;
+ // A pointer to the FileCollection representing the filesystem (not archives).
io::FileCollection* mFileCollection;
+
+ // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
-
- struct FileToProcess {
- std::unique_ptr<ResourceFile> fileExport;
- io::IFile* file;
- };
-
- struct FileToProcessComparator {
- bool operator()(const FileToProcess& a, const FileToProcess& b) {
- return std::tie(a.fileExport->name, a.fileExport->config) <
- std::tie(b.fileExport->name, b.fileExport->config);
- }
- };
-
- std::set<FileToProcess, FileToProcessComparator> mFilesToProcess;
};
int link(const std::vector<StringPiece>& args) {
@@ -915,6 +883,8 @@
"package name", &privateSymbolsPackage)
.optionalFlagList("--extra-packages", "Generate the same R.java but with different "
"package names", &extraJavaPackages)
+ .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
+ "overlays without <add-resource> tags", &options.autoAddOverlay)
.optionalSwitch("-v", "Enables verbose logging", &options.verbose);
if (!flags.parse("aapt2 link", args, &std::cerr)) {
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index a06a1bf..27a23bd 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -15,6 +15,7 @@
*/
#include "ResourceTable.h"
+#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
@@ -26,8 +27,9 @@
namespace aapt {
-TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable) :
- mContext(context), mMasterTable(outTable) {
+TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable,
+ const TableMergerOptions& options) :
+ mContext(context), mMasterTable(outTable), mOptions(options) {
// Create the desired package that all tables will be merged into.
mMasterPackage = mMasterTable->createPackage(
mContext->getCompilationPackage(), mContext->getPackageId());
@@ -37,7 +39,8 @@
/**
* This will merge packages with the same package name (or no package name).
*/
-bool TableMerger::merge(const Source& src, ResourceTable* table, bool overrideExisting) {
+bool TableMerger::mergeImpl(const Source& src, ResourceTable* table,
+ bool overlay, bool allowNew) {
const uint8_t desiredPackageId = mContext->getPackageId();
bool error = false;
@@ -55,19 +58,26 @@
// mangled, then looked up at resolution time.
// Also, when linking, we convert references with no package name to use
// the compilation package name.
- if (!doMerge(src, table, package.get(), false, overrideExisting)) {
- error = true;
- }
+ error |= !doMerge(src, table, package.get(),
+ false /* mangle */, overlay, allowNew, {});
}
}
return !error;
}
+bool TableMerger::merge(const Source& src, ResourceTable* table) {
+ return mergeImpl(src, table, false /* overlay */, true /* allow new */);
+}
+
+bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table) {
+ return mergeImpl(src, table, true /* overlay */, mOptions.autoAddOverlay);
+}
+
/**
* This will merge and mangle resources from a static library.
*/
bool TableMerger::mergeAndMangle(const Source& src, const StringPiece16& packageName,
- ResourceTable* table) {
+ ResourceTable* table, io::IFileCollection* collection) {
bool error = false;
for (auto& package : table->packages) {
// Warn of packages with an unrelated ID.
@@ -79,16 +89,35 @@
bool mangle = packageName != mContext->getCompilationPackage();
mMergedPackages.insert(package->name);
- if (!doMerge(src, table, package.get(), mangle, false)) {
- error = true;
- }
+
+ auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
+ FileReference* newFile, FileReference* oldFile) -> bool {
+ // The old file's path points inside the APK, so we can use it as is.
+ io::IFile* f = collection->findFile(util::utf16ToUtf8(*oldFile->path));
+ if (!f) {
+ mContext->getDiagnostics()->error(DiagMessage(src) << "file '" << *oldFile->path
+ << "' not found");
+ return false;
+ }
+
+ mFilesToMerge[ResourceKeyRef{ name, config }] = FileToMerge{
+ f, oldFile->getSource(), util::utf16ToUtf8(*newFile->path) };
+ return true;
+ };
+
+ error |= !doMerge(src, table, package.get(),
+ mangle, false /* overlay */, true /* allow new */, callback);
}
return !error;
}
-bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable,
- ResourceTablePackage* srcPackage, const bool manglePackage,
- const bool overrideExisting) {
+bool TableMerger::doMerge(const Source& src,
+ ResourceTable* srcTable,
+ ResourceTablePackage* srcPackage,
+ const bool manglePackage,
+ const bool overlay,
+ const bool allowNewResources,
+ FileMergeCallback callback) {
bool error = false;
for (auto& srcType : srcPackage->types) {
@@ -112,10 +141,33 @@
for (auto& srcEntry : srcType->entries) {
ResourceEntry* dstEntry;
if (manglePackage) {
- dstEntry = dstType->findOrCreateEntry(NameMangler::mangleEntry(
- srcPackage->name, srcEntry->name));
+ std::u16string mangledName = NameMangler::mangleEntry(srcPackage->name,
+ srcEntry->name);
+ if (allowNewResources) {
+ dstEntry = dstType->findOrCreateEntry(mangledName);
+ } else {
+ dstEntry = dstType->findEntry(mangledName);
+ }
} else {
- dstEntry = dstType->findOrCreateEntry(srcEntry->name);
+ if (allowNewResources) {
+ dstEntry = dstType->findOrCreateEntry(srcEntry->name);
+ } else {
+ dstEntry = dstType->findEntry(srcEntry->name);
+ }
+ }
+
+ if (!dstEntry) {
+ mContext->getDiagnostics()->error(DiagMessage(src)
+ << "resource "
+ << ResourceNameRef(srcPackage->name,
+ srcType->type,
+ srcEntry->name)
+ << " does not override an existing resource");
+ mContext->getDiagnostics()->note(DiagMessage(src)
+ << "define an <add-resource> tag or use "
+ "--auto-add-overlay");
+ error = true;
+ continue;
}
if (srcEntry->symbolStatus.state != SymbolState::kUndefined) {
@@ -143,6 +195,8 @@
}
}
+ ResourceNameRef resName(mMasterPackage->name, dstType->type, dstEntry->name);
+
for (ResourceConfigValue& srcValue : srcEntry->values) {
auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
srcValue.config, cmp::lessThanConfig);
@@ -150,7 +204,7 @@
if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
const int collisionResult = ResourceTable::resolveValueCollision(
iter->value.get(), srcValue.value.get());
- if (collisionResult == 0 && !overrideExisting) {
+ if (collisionResult == 0 && !overlay) {
// Error!
ResourceNameRef resourceName(srcPackage->name,
srcType->type,
@@ -175,10 +229,26 @@
iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
}
- if (manglePackage) {
- iter->value = cloneAndMangle(srcTable, srcPackage->name, srcValue.value.get());
+ if (FileReference* f = valueCast<FileReference>(srcValue.value.get())) {
+ std::unique_ptr<FileReference> newFileRef;
+ if (manglePackage) {
+ newFileRef = cloneAndMangleFile(srcPackage->name, *f);
+ } else {
+ newFileRef = std::unique_ptr<FileReference>(f->clone(
+ &mMasterTable->stringPool));
+ }
+
+ if (callback) {
+ if (!callback(resName, iter->config, newFileRef.get(), f)) {
+ error = true;
+ continue;
+ }
+ }
+ iter->value = std::move(newFileRef);
+
} else {
- iter->value = clone(srcValue.value.get());
+ iter->value = std::unique_ptr<Value>(srcValue.value->clone(
+ &mMasterTable->stringPool));
}
}
}
@@ -186,28 +256,52 @@
return !error;
}
-std::unique_ptr<Value> TableMerger::cloneAndMangle(ResourceTable* table,
- const std::u16string& package,
- Value* value) {
- if (FileReference* f = valueCast<FileReference>(value)) {
- // Mangle the path.
- StringPiece16 prefix, entry, suffix;
- if (util::extractResFilePathParts(*f->path, &prefix, &entry, &suffix)) {
- std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
- std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
- mFilesToMerge.push(FileToMerge{ table, *f->path, newPath });
- std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
- mMasterTable->stringPool.makeRef(newPath));
- fileRef->setComment(f->getComment());
- fileRef->setSource(f->getSource());
- return std::move(fileRef);
- }
+std::unique_ptr<FileReference> TableMerger::cloneAndMangleFile(const std::u16string& package,
+ const FileReference& fileRef) {
+
+ StringPiece16 prefix, entry, suffix;
+ if (util::extractResFilePathParts(*fileRef.path, &prefix, &entry, &suffix)) {
+ std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
+ std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
+ std::unique_ptr<FileReference> newFileRef = util::make_unique<FileReference>(
+ mMasterTable->stringPool.makeRef(newPath));
+ newFileRef->setComment(fileRef.getComment());
+ newFileRef->setSource(fileRef.getSource());
+ return newFileRef;
}
- return clone(value);
+ return std::unique_ptr<FileReference>(fileRef.clone(&mMasterTable->stringPool));
}
-std::unique_ptr<Value> TableMerger::clone(Value* value) {
- return std::unique_ptr<Value>(value->clone(&mMasterTable->stringPool));
+bool TableMerger::mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay) {
+ ResourceTable table;
+ std::u16string path = util::utf8ToUtf16(ResourceUtils::buildResourceFileName(fileDesc,
+ nullptr));
+ std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
+ table.stringPool.makeRef(path));
+ fileRef->setSource(fileDesc.source);
+
+ ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
+ pkg->findOrCreateType(fileDesc.name.type)
+ ->findOrCreateEntry(fileDesc.name.entry)
+ ->values.push_back(ResourceConfigValue{ fileDesc.config, std::move(fileRef) });
+
+ auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
+ FileReference* newFile, FileReference* oldFile) -> bool {
+ mFilesToMerge[ResourceKeyRef{ name, config }] = FileToMerge{
+ file, oldFile->getSource(), util::utf16ToUtf8(*newFile->path) };
+ return true;
+ };
+
+ return doMerge(file->getSource(), &table, pkg,
+ false /* mangle */, overlay /* overlay */, true /* allow new */, callback);
+}
+
+bool TableMerger::mergeFile(const ResourceFile& fileDesc, io::IFile* file) {
+ return mergeFileImpl(fileDesc, file, false /* overlay */);
+}
+
+bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file) {
+ return mergeFileImpl(fileDesc, file, true /* overlay */);
}
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index a2c9dbf..e1be5d5 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -17,21 +17,40 @@
#ifndef AAPT_TABLEMERGER_H
#define AAPT_TABLEMERGER_H
+#include "Resource.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
+#include "io/File.h"
+#include "process/IResourceTableConsumer.h"
#include "util/Util.h"
-#include "process/IResourceTableConsumer.h"
-
-#include <queue>
-#include <set>
+#include <functional>
+#include <map>
namespace aapt {
struct FileToMerge {
- ResourceTable* srcTable;
- std::u16string srcPath;
- std::u16string dstPath;
+ /**
+ * The compiled file from which to read the data.
+ */
+ io::IFile* file;
+
+ /**
+ * Where the original, uncompiled file came from.
+ */
+ Source originalSource;
+
+ /**
+ * The destination path within the APK/archive.
+ */
+ std::string dstPath;
+};
+
+struct TableMergerOptions {
+ /**
+ * If true, resources in overlays can be added without previously having existed.
+ */
+ bool autoAddOverlay = false;
};
/**
@@ -50,40 +69,74 @@
*/
class TableMerger {
public:
- TableMerger(IAaptContext* context, ResourceTable* outTable);
+ /**
+ * Note: The outTable ResourceTable must live longer than this TableMerger. References
+ * are made to this ResourceTable for efficiency reasons.
+ */
+ TableMerger(IAaptContext* context, ResourceTable* outTable, const TableMergerOptions& options);
- inline std::queue<FileToMerge>* getFileMergeQueue() {
- return &mFilesToMerge;
+ const std::map<ResourceKeyRef, FileToMerge>& getFilesToMerge() {
+ return mFilesToMerge;
}
- inline const std::set<std::u16string>& getMergedPackages() const {
+ const std::set<std::u16string>& getMergedPackages() const {
return mMergedPackages;
}
/**
* Merges resources from the same or empty package. This is for local sources.
*/
- bool merge(const Source& src, ResourceTable* table, bool overrideExisting);
+ bool merge(const Source& src, ResourceTable* table);
+
+ /**
+ * Merges resources from an overlay ResourceTable.
+ */
+ bool mergeOverlay(const Source& src, ResourceTable* table);
/**
* Merges resources from the given package, mangling the name. This is for static libraries.
+ * An io::IFileCollection is needed in order to find the referenced Files and process them.
*/
- bool mergeAndMangle(const Source& src, const StringPiece16& package, ResourceTable* table);
+ bool mergeAndMangle(const Source& src, const StringPiece16& package, ResourceTable* table,
+ io::IFileCollection* collection);
+
+ /**
+ * Merges a compiled file that belongs to this same or empty package. This is for local sources.
+ */
+ bool mergeFile(const ResourceFile& fileDesc, io::IFile* file);
+
+ /**
+ * Merges a compiled file from an overlay, overriding an existing definition.
+ */
+ bool mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file);
private:
+ using FileMergeCallback = std::function<bool(const ResourceNameRef&,
+ const ConfigDescription& config,
+ FileReference*,
+ FileReference*)>;
+
IAaptContext* mContext;
ResourceTable* mMasterTable;
+ TableMergerOptions mOptions;
ResourceTablePackage* mMasterPackage;
std::set<std::u16string> mMergedPackages;
- std::queue<FileToMerge> mFilesToMerge;
+ std::map<ResourceKeyRef, FileToMerge> mFilesToMerge;
+
+ bool mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay);
+
+ bool mergeImpl(const Source& src, ResourceTable* srcTable,
+ bool overlay, bool allowNew);
bool doMerge(const Source& src, ResourceTable* srcTable, ResourceTablePackage* srcPackage,
- const bool manglePackage, const bool overrideExisting);
+ const bool manglePackage,
+ const bool overlay,
+ const bool allowNewResources,
+ FileMergeCallback callback);
- std::unique_ptr<Value> cloneAndMangle(ResourceTable* table, const std::u16string& package,
- Value* value);
- std::unique_ptr<Value> clone(Value* value);
+ std::unique_ptr<FileReference> cloneAndMangleFile(const std::u16string& package,
+ const FileReference& value);
};
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index b7ffba7..fa4afd3 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
+#include "io/FileSystem.h"
#include "link/TableMerger.h"
-
#include "test/Builders.h"
#include "test/Context.h"
@@ -57,10 +57,11 @@
.build();
ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable);
+ TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+ io::FileCollection collection;
- ASSERT_TRUE(merger.merge({}, tableA.get(), false));
- ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get(), &collection));
EXPECT_TRUE(merger.getMergedPackages().count(u"com.app.b") != 0);
@@ -76,6 +77,60 @@
AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/com.app.b$foo")));
}
+TEST_F(TableMergerTest, MergeFile) {
+ ResourceTable finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, options);
+
+ ResourceFile fileDesc;
+ fileDesc.config = test::parseConfigOrDie("hdpi-v4");
+ fileDesc.name = test::parseNameOrDie(u"@layout/main");
+ fileDesc.source = Source("res/layout-hdpi/main.xml");
+ test::TestFile testFile("path/to/res/layout-hdpi/main.xml.flat");
+
+ ASSERT_TRUE(merger.mergeFile(fileDesc, &testFile));
+
+ FileReference* file = test::getValueForConfig<FileReference>(&finalTable,
+ u"@com.app.a:layout/main",
+ test::parseConfigOrDie("hdpi-v4"));
+ ASSERT_NE(nullptr, file);
+ EXPECT_EQ(std::u16string(u"res/layout-hdpi-v4/main.xml"), *file->path);
+
+ ResourceName name = test::parseNameOrDie(u"@com.app.a:layout/main");
+ ResourceKeyRef key = { name, test::parseConfigOrDie("hdpi-v4") };
+
+ auto iter = merger.getFilesToMerge().find(key);
+ ASSERT_NE(merger.getFilesToMerge().end(), iter);
+
+ const FileToMerge& actualFileToMerge = iter->second;
+ EXPECT_EQ(&testFile, actualFileToMerge.file);
+ EXPECT_EQ(std::string("res/layout-hdpi-v4/main.xml"), actualFileToMerge.dstPath);
+}
+
+TEST_F(TableMergerTest, MergeFileOverlay) {
+ ResourceTable finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+
+ ResourceFile fileDesc;
+ fileDesc.name = test::parseNameOrDie(u"@xml/foo");
+ test::TestFile fileA("path/to/fileA.xml.flat");
+ test::TestFile fileB("path/to/fileB.xml.flat");
+
+ ASSERT_TRUE(merger.mergeFile(fileDesc, &fileA));
+ ASSERT_TRUE(merger.mergeFileOverlay(fileDesc, &fileB));
+
+ ResourceName name = test::parseNameOrDie(u"@com.app.a:xml/foo");
+ ResourceKeyRef key = { name, ConfigDescription{} };
+ auto iter = merger.getFilesToMerge().find(key);
+ ASSERT_NE(merger.getFilesToMerge().end(), iter);
+
+ const FileToMerge& actualFileToMerge = iter->second;
+ EXPECT_EQ(&fileB, actualFileToMerge.file);
+}
+
TEST_F(TableMergerTest, MergeFileReferences) {
std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
.setPackageId(u"com.app.a", 0x7f)
@@ -87,10 +142,12 @@
.build();
ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable);
+ TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+ io::FileCollection collection;
+ collection.insertFile("res/xml/file.xml");
- ASSERT_TRUE(merger.merge({}, tableA.get(), false));
- ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get(), &collection));
FileReference* f = test::getValue<FileReference>(&finalTable, u"@com.app.a:xml/file");
ASSERT_NE(f, nullptr);
@@ -100,13 +157,90 @@
ASSERT_NE(f, nullptr);
EXPECT_EQ(std::u16string(u"res/xml/com.app.b$file.xml"), *f->path);
- std::queue<FileToMerge>* filesToMerge = merger.getFileMergeQueue();
- ASSERT_FALSE(filesToMerge->empty());
+ ResourceName name = test::parseNameOrDie(u"@com.app.a:xml/com.app.b$file");
+ ResourceKeyRef key = { name, ConfigDescription{} };
+ auto iter = merger.getFilesToMerge().find(key);
+ ASSERT_NE(merger.getFilesToMerge().end(), iter);
- FileToMerge& fileToMerge = filesToMerge->front();
- EXPECT_EQ(fileToMerge.srcTable, tableB.get());
- EXPECT_EQ(fileToMerge.srcPath, u"res/xml/file.xml");
- EXPECT_EQ(fileToMerge.dstPath, u"res/xml/com.app.b$file.xml");
+ const FileToMerge& actualFileToMerge = iter->second;
+ EXPECT_EQ(Source("res/xml/file.xml"), actualFileToMerge.file->getSource());
+ EXPECT_EQ(std::string("res/xml/com.app.b$file.xml"), actualFileToMerge.dstPath);
+}
+
+TEST_F(TableMergerTest, OverrideResourceWithOverlay) {
+ std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x00)
+ .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+ .build();
+ std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x00)
+ .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"false"))
+ .build();
+
+ ResourceTable finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+
+ ASSERT_TRUE(merger.merge({}, base.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, overlay.get()));
+
+ BinaryPrimitive* foo = test::getValue<BinaryPrimitive>(&finalTable, u"@com.app.a:bool/foo");
+ ASSERT_NE(nullptr, foo);
+ EXPECT_EQ(0x0u, foo->value.data);
+}
+
+TEST_F(TableMergerTest, MergeAddResourceFromOverlay) {
+ std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .setSymbolState(u"@bool/foo", {}, SymbolState::kUndefined)
+ .build();
+ std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+ .build();
+
+ ResourceTable finalTable;
+ TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+}
+
+TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) {
+ std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .build();
+ std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+ .build();
+
+ ResourceTable finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = true;
+ TableMerger merger(mContext.get(), &finalTable, options);
+
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+}
+
+TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) {
+ std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .build();
+ std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+ .build();
+
+ ResourceTable finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, options);
+
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_FALSE(merger.mergeOverlay({}, tableB.get()));
}
} // namespace aapt
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 6fdaebb..51e2dd4 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -22,7 +22,7 @@
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ValueVisitor.h"
-
+#include "io/File.h"
#include "process/IResourceTableConsumer.h"
#include "util/StringPiece.h"
@@ -87,6 +87,22 @@
return getValueForConfig<T>(table, resName, {});
}
+class TestFile : public io::IFile {
+private:
+ Source mSource;
+
+public:
+ TestFile(const StringPiece& path) : mSource(path) {}
+
+ std::unique_ptr<io::IData> openAsData() override {
+ return {};
+ }
+
+ const Source& getSource() const override {
+ return mSource;
+ }
+};
+
} // namespace test
} // namespace aapt
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 46b5205..21e476f 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -447,6 +447,10 @@
case Public_entry::kPublic:
symbol.state = SymbolState::kPublic;
break;
+
+ case Public_entry::kUndefined:
+ symbol.state = SymbolState::kUndefined;
+ break;
}
if (!mTable->setSymbolStateAllowMangled(name, resId, symbol, mContext->getDiagnostics())) {
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index aa409ea..10a2803 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -285,6 +285,8 @@
-> decltype(std::declval<T> == std::declval<U>) {
if (a && b) {
return a.value() == b.value();
+ } else if (!a && !b) {
+ return true;
}
return false;
}
diff --git a/tools/aapt2/util/Maybe_test.cpp b/tools/aapt2/util/Maybe_test.cpp
index 9cca40e..5d42dc3 100644
--- a/tools/aapt2/util/Maybe_test.cpp
+++ b/tools/aapt2/util/Maybe_test.cpp
@@ -124,9 +124,12 @@
Maybe<int> b = 1;
Maybe<int> c;
+ Maybe<int> emptyA, emptyB;
+
EXPECT_EQ(a, b);
EXPECT_EQ(b, a);
EXPECT_NE(a, c);
+ EXPECT_EQ(emptyA, emptyB);
}
} // namespace aapt
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 bd5335e..63411b0 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
@@ -1634,6 +1634,11 @@
}
@Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+ // pass
+ }
+
+ @Override
public void sendStickyOrderedBroadcastAsUser(Intent intent,
UserHandle user, BroadcastReceiver resultReceiver,
Handler scheduler, int initialCode, String initialData,
@@ -1804,24 +1809,24 @@
}
@Override
- public Context createDeviceEncryptedContext(Context context) {
+ public Context createDeviceEncryptedStorageContext() {
// pass
return null;
}
@Override
- public Context createCredentialEncryptedContext(Context context) {
+ public Context createCredentialEncryptedStorageContext() {
// pass
return null;
}
@Override
- public boolean isDeviceEncrypted() {
+ public boolean isDeviceEncryptedStorage() {
return false;
}
@Override
- public boolean isCredentialEncrypted() {
+ public boolean isCredentialEncryptedStorage() {
return false;
}
}