diff options
35 files changed, 774 insertions, 344 deletions
diff --git a/api/current.txt b/api/current.txt index 3230bd893b1a..d7664b97fea3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10632,6 +10632,7 @@ package android.graphics.drawable { method public void setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode); method public final void setTileModeY(android.graphics.Shader.TileMode); method public void setTint(android.content.res.ColorStateList); + method public void setTintMode(android.graphics.PorterDuff.Mode); } public class ClipDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { @@ -10909,6 +10910,7 @@ package android.graphics.drawable { method public void setTargetDensity(android.util.DisplayMetrics); method public void setTargetDensity(int); method public void setTint(android.content.res.ColorStateList); + method public void setTintMode(android.graphics.PorterDuff.Mode); } public class PaintDrawable extends android.graphics.drawable.ShapeDrawable { @@ -10999,6 +11001,9 @@ package android.graphics.drawable { public class TouchFeedbackDrawable extends android.graphics.drawable.LayerDrawable { method public android.graphics.Rect getDirtyBounds(); + method public android.content.res.ColorStateList getTint(); + method public void setTint(android.content.res.ColorStateList); + method public void setTintMode(android.graphics.PorterDuff.Mode); } public class TransitionDrawable extends android.graphics.drawable.LayerDrawable implements android.graphics.drawable.Drawable.Callback { diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 0d4a4cbe8c01..c5e5753b1a14 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -461,6 +461,10 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { return (T) getFaces(); } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) { return (T) getFaceRectangles(); + } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS)) { + return (T) getAvailableStreamConfigurations(); + } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS)) { + return (T) getAvailableMinFrameDurations(); } // For other keys, get() falls back to getBase() @@ -481,6 +485,50 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { return availableFormats; } + private int[] getAvailableStreamConfigurations() { + final int NUM_ELEMENTS_IN_CONFIG = 4; + int[] availableConfigs = + getBase(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + if (availableConfigs != null) { + if (availableConfigs.length % NUM_ELEMENTS_IN_CONFIG != 0) { + Log.w(TAG, "availableStreamConfigurations is malformed, length must be multiple" + + " of " + NUM_ELEMENTS_IN_CONFIG); + return availableConfigs; + } + + for (int i = 0; i < availableConfigs.length; i += NUM_ELEMENTS_IN_CONFIG) { + // JPEG has different value between native and managed side, need override. + if (availableConfigs[i] == NATIVE_JPEG_FORMAT) { + availableConfigs[i] = ImageFormat.JPEG; + } + } + } + + return availableConfigs; + } + + private long[] getAvailableMinFrameDurations() { + final int NUM_ELEMENTS_IN_DURATION = 4; + long[] availableMinDurations = + getBase(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS); + if (availableMinDurations != null) { + if (availableMinDurations.length % NUM_ELEMENTS_IN_DURATION != 0) { + Log.w(TAG, "availableStreamConfigurations is malformed, length must be multiple" + + " of " + NUM_ELEMENTS_IN_DURATION); + return availableMinDurations; + } + + for (int i = 0; i < availableMinDurations.length; i += NUM_ELEMENTS_IN_DURATION) { + // JPEG has different value between native and managed side, need override. + if (availableMinDurations[i] == NATIVE_JPEG_FORMAT) { + availableMinDurations[i] = ImageFormat.JPEG; + } + } + } + + return availableMinDurations; + } + private Face[] getFaces() { final int FACE_LANDMARK_SIZE = 6; @@ -607,12 +655,56 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { return setAvailableFormats((int[]) value); } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) { return setFaceRectangles((Rect[]) value); + } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS)) { + return setAvailableStreamConfigurations((int[])value); + } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS)) { + return setAvailableMinFrameDurations((long[])value); } // For other keys, set() falls back to setBase(). return false; } + private boolean setAvailableStreamConfigurations(int[] value) { + final int NUM_ELEMENTS_IN_CONFIG = 4; + int[] availableConfigs = value; + if (value == null) { + // Let setBase() to handle the null value case. + return false; + } + + int[] newValues = new int[availableConfigs.length]; + for (int i = 0; i < availableConfigs.length; i++) { + newValues[i] = availableConfigs[i]; + if (i % NUM_ELEMENTS_IN_CONFIG == 0 && availableConfigs[i] == ImageFormat.JPEG) { + newValues[i] = NATIVE_JPEG_FORMAT; + } + } + + setBase(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS, newValues); + return true; + } + + private boolean setAvailableMinFrameDurations(long[] value) { + final int NUM_ELEMENTS_IN_DURATION = 4; + long[] availableDurations = value; + if (value == null) { + // Let setBase() to handle the null value case. + return false; + } + + long[] newValues = new long[availableDurations.length]; + for (int i = 0; i < availableDurations.length; i++) { + newValues[i] = availableDurations[i]; + if (i % NUM_ELEMENTS_IN_DURATION == 0 && availableDurations[i] == ImageFormat.JPEG) { + newValues[i] = NATIVE_JPEG_FORMAT; + } + } + + setBase(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS, newValues); + return true; + } + private boolean setAvailableFormats(int[] value) { int[] availableFormat = value; if (value == null) { diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java index 05f3a1c4abc8..1754dce3fc68 100644 --- a/core/java/android/provider/SearchIndexablesContract.java +++ b/core/java/android/provider/SearchIndexablesContract.java @@ -58,33 +58,64 @@ public class SearchIndexablesContract { * Indexable xml resources colums. */ public static final String[] INDEXABLES_XML_RES_COLUMNS = new String[] { - XmlResource.COLUMN_RANK, - XmlResource.COLUMN_XML_RESID, - XmlResource.COLUMN_CLASS_NAME, - XmlResource.COLUMN_ICON_RESID, - XmlResource.COLUMN_INTENT_ACTION, - XmlResource.COLUMN_INTENT_TARGET_PACKAGE, - XmlResource.COLUMN_INTENT_TARGET_CLASS + XmlResource.COLUMN_RANK, // 0 + XmlResource.COLUMN_XML_RESID, // 1 + XmlResource.COLUMN_CLASS_NAME, // 2 + XmlResource.COLUMN_ICON_RESID, // 3 + XmlResource.COLUMN_INTENT_ACTION, // 4 + XmlResource.COLUMN_INTENT_TARGET_PACKAGE, // 5 + XmlResource.COLUMN_INTENT_TARGET_CLASS // 6 }; /** + * Indexable xml resources colums indices. + */ + public static final int COLUMN_INDEX_XML_RES_RANK = 0; + public static final int COLUMN_INDEX_XML_RES_RESID = 1; + public static final int COLUMN_INDEX_XML_RES_CLASS_NAME = 2; + public static final int COLUMN_INDEX_XML_RES_ICON_RESID = 3; + public static final int COLUMN_INDEX_XML_RES_INTENT_ACTION = 4; + public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE = 5; + public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS = 6; + + /** * Indexable raw data colums. */ public static final String[] INDEXABLES_RAW_COLUMNS = new String[] { - RawData.COLUMN_RANK, - RawData.COLUMN_TITLE, - RawData.COLUMN_SUMMARY_ON, - RawData.COLUMN_SUMMARY_OFF, - RawData.COLUMN_KEYWORDS, - RawData.COLUMN_SCREEN_TITLE, - RawData.COLUMN_CLASS_NAME, - RawData.COLUMN_ICON_RESID, - RawData.COLUMN_INTENT_ACTION, - RawData.COLUMN_INTENT_TARGET_PACKAGE, - RawData.COLUMN_INTENT_TARGET_CLASS, + RawData.COLUMN_RANK, // 0 + RawData.COLUMN_TITLE, // 1 + RawData.COLUMN_SUMMARY_ON, // 2 + RawData.COLUMN_SUMMARY_OFF, // 3 + RawData.COLUMN_ENTRIES, // 4 + RawData.COLUMN_KEYWORDS, // 5 + RawData.COLUMN_SCREEN_TITLE, // 6 + RawData.COLUMN_CLASS_NAME, // 7 + RawData.COLUMN_ICON_RESID, // 8 + RawData.COLUMN_INTENT_ACTION, // 9 + RawData.COLUMN_INTENT_TARGET_PACKAGE, // 10 + RawData.COLUMN_INTENT_TARGET_CLASS, // 11 + RawData.COLUMN_KEY, // 12 }; /** + * Indexable raw data colums indices. + */ + public static final int COLUMN_INDEX_RAW_RANK = 0; + public static final int COLUMN_INDEX_RAW_TITLE = 1; + public static final int COLUMN_INDEX_RAW_SUMMARY_ON = 2; + public static final int COLUMN_INDEX_RAW_SUMMARY_OFF = 3; + public static final int COLUMN_INDEX_RAW_ENTRIES = 4; + public static final int COLUMN_INDEX_RAW_KEYWORDS = 5; + public static final int COLUMN_INDEX_RAW_SCREEN_TITLE = 6; + public static final int COLUMN_INDEX_RAW_CLASS_NAME = 7; + public static final int COLUMN_INDEX_RAW_ICON_RESID = 8; + public static final int COLUMN_INDEX_RAW_INTENT_ACTION = 9; + public static final int COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE = 10; + public static final int COLUMN_INDEX_RAW_INTENT_TARGET_CLASS = 11; + public static final int COLUMN_INDEX_RAW_KEY = 12; + + + /** * Constants related to a {@link SearchIndexableResource}. * * This is a description of @@ -134,6 +165,11 @@ public class SearchIndexablesContract { public static final String COLUMN_SUMMARY_OFF = "summaryOff"; /** + * Entries associated with the raw data (when the data can can several values). + */ + public static final String COLUMN_ENTRIES = "entries"; + + /** * Keywords' raw data. */ public static final String COLUMN_KEYWORDS = "keywords"; @@ -142,6 +178,11 @@ public class SearchIndexablesContract { * Fragment's title associated with the raw data. */ public static final String COLUMN_SCREEN_TITLE = "screenTitle"; + + /** + * Key associated with the raw data. The key needs to be unique. + */ + public static final String COLUMN_KEY = "key"; } /** diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java index f90a76324dbd..b589cc2b471d 100644 --- a/core/java/android/view/GLRenderer.java +++ b/core/java/android/view/GLRenderer.java @@ -838,6 +838,11 @@ public class GLRenderer extends HardwareRenderer { } } + @Override + void pauseSurface(Surface surface) { + // No-op + } + boolean initializeEgl() { synchronized (sEglLock) { if (sEgl == null && sEglConfig == null) { diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index ea707b02b7d5..56d96e1e067e 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -234,6 +234,13 @@ public abstract class HardwareRenderer { abstract void updateSurface(Surface surface) throws OutOfResourcesException; /** + * Stops any rendering into the surface. Use this if it is unclear whether + * or not the surface used by the HardwareRenderer will be changing. It + * Suspends any rendering into the surface, but will not do any destruction + */ + abstract void pauseSurface(Surface surface); + + /** * Destroys all hardware rendering resources associated with the specified * view hierarchy. * diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 6ae730b6a540..1ecc3c63d876 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -54,28 +54,46 @@ public class ThreadedRenderer extends HardwareRenderer { private int mWidth, mHeight; private long mNativeProxy; + private boolean mInitialized = false; ThreadedRenderer(boolean translucent) { mNativeProxy = nCreateProxy(translucent); - setEnabled(mNativeProxy != 0); } @Override void destroy(boolean full) { + mInitialized = false; + updateEnabledState(null); nDestroyCanvas(mNativeProxy); } + private void updateEnabledState(Surface surface) { + if (surface == null || !surface.isValid()) { + setEnabled(false); + } else { + setEnabled(mInitialized); + } + } + @Override boolean initialize(Surface surface) throws OutOfResourcesException { + mInitialized = true; + updateEnabledState(surface); return nInitialize(mNativeProxy, surface); } @Override void updateSurface(Surface surface) throws OutOfResourcesException { + updateEnabledState(surface); nUpdateSurface(mNativeProxy, surface); } @Override + void pauseSurface(Surface surface) { + nPauseSurface(mNativeProxy, surface); + } + + @Override void destroyHardwareResources(View view) { destroyResources(view); // TODO: GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); @@ -262,6 +280,7 @@ public class ThreadedRenderer extends HardwareRenderer { private static native boolean nInitialize(long nativeProxy, Surface window); private static native void nUpdateSurface(long nativeProxy, Surface window); + private static native void nPauseSurface(long nativeProxy, Surface window); private static native void nSetup(long nativeProxy, int width, int height); private static native void nSetDisplayListData(long nativeProxy, long displayList, long newData); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 0010eeb827eb..65ac50de5ea5 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1421,6 +1421,12 @@ public final class ViewRootImpl implements ViewParent, host.getMeasuredHeight() + ", params=" + params); } + if (mAttachInfo.mHardwareRenderer != null) { + // relayoutWindow may decide to destroy mSurface. As that decision + // happens in WindowManager service, we need to be defensive here + // and stop using the surface in case it gets destroyed. + mAttachInfo.mHardwareRenderer.pauseSurface(mSurface); + } final int surfaceGenerationId = mSurface.getGenerationId(); relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); if (!mDrawDuringWindowsAnimating && diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp index 25934200e0c9..8a0eaa26acda 100644 --- a/core/jni/android_util_EventLog.cpp +++ b/core/jni/android_util_EventLog.cpp @@ -177,13 +177,13 @@ static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED, break; } if (ret < 0) { - if (errno == EINTR) { + if (ret == -EINTR) { continue; } - if (errno == EINVAL) { + if (ret == -EINVAL) { jniThrowException(env, "java/io/IOException", "Event too short"); - } else if (errno != EAGAIN) { - jniThrowIOException(env, errno); // Will throw on return + } else if (ret != -EAGAIN) { + jniThrowIOException(env, -ret); // Will throw on return } break; } diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 42cbfc94cee9..36c83572ae39 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -84,7 +84,7 @@ static jboolean android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject cl jlong proxyPtr, jobject jsurface) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); sp<ANativeWindow> window = android_view_Surface_getNativeWindow(env, jsurface); - return proxy->initialize(window.get()); + return proxy->initialize(window); } static void android_view_ThreadedRenderer_updateSurface(JNIEnv* env, jobject clazz, @@ -94,7 +94,17 @@ static void android_view_ThreadedRenderer_updateSurface(JNIEnv* env, jobject cla if (jsurface) { window = android_view_Surface_getNativeWindow(env, jsurface); } - proxy->updateSurface(window.get()); + proxy->updateSurface(window); +} + +static void android_view_ThreadedRenderer_pauseSurface(JNIEnv* env, jobject clazz, + jlong proxyPtr, jobject jsurface) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + sp<ANativeWindow> window; + if (jsurface) { + window = android_view_Surface_getNativeWindow(env, jsurface); + } + proxy->pauseSurface(window); } static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, @@ -195,6 +205,7 @@ static JNINativeMethod gMethods[] = { { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy }, { "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize }, { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface }, + { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface }, { "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup }, { "nDrawDisplayList", "(JJIIII)V", (void*) android_view_ThreadedRenderer_drawDisplayList }, { "nDestroyCanvas", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvas }, diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 46cb9b22728a..e100a4d3e0ab 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4409,14 +4409,23 @@ <!-- When a tint color is set, specifies its Porter-Duff blending mode. The default value is src_in, which treats the drawable as an alpha mask. --> <attr name="tintMode"> - <!-- [Sa * Da, Sc * Da] --> - <enum name="src_in" value="0" /> - <!-- [Da, Sc * Da + (1 - Sa) * Dc] --> - <enum name="src_atop" value="1" /> - <!-- [Sa * Da, Sc * Dc] --> - <enum name="multiply" value="2" /> + <!-- The tint is drawn on top of the drawable. + [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] --> + <enum name="src_over" value="3" /> + <!-- The tint is masked by the alpha channel of the drawable. The drawable’s + color channels are thrown out. [Sa * Da, Sc * Da] --> + <enum name="src_in" value="5" /> + <!-- The tint is drawn above the drawable, but with the drawable’s alpha + channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] --> + <enum name="src_atop" value="9" /> + <!-- Multiplies the color and alpha channels of the drawable with those of + the tint. [Sa * Da, Sc * Dc] --> + <enum name="multiply" value="14" /> <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] --> - <enum name="screen" value="3" /> + <enum name="screen" value="15" /> + <!-- Combines the tint and drawable color and alpha channels, clamping the + result to valid color values. Saturate(S + D) --> + <enum name="add" value="16" /> </attr> </declare-styleable> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 7aea81445eee..37a903a149a5 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -349,6 +349,7 @@ please see themes_device_defaults.xml. <item name="actionMenuTextAppearance">@android:style/TextAppearance.Holo.Widget.ActionBar.Menu</item> <item name="actionMenuTextColor">?android:attr/textColorPrimary</item> <item name="actionBarWidgetTheme">@null</item> + <item name="actionBarTheme">@null</item> <item name="actionBarDivider">?android:attr/dividerVertical</item> <item name="actionBarItemBackground">?android:attr/selectableItemBackground</item> @@ -1194,6 +1195,7 @@ please see themes_device_defaults.xml. <item name="actionBarSize">@dimen/action_bar_default_height</item> <item name="actionModePopupWindowStyle">@android:style/Widget.Holo.PopupWindow.ActionMode</item> <item name="actionBarWidgetTheme">@null</item> + <item name="actionBarTheme">@null</item> <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_dark</item> <item name="actionModeCopyDrawable">@android:drawable/ic_menu_copy_holo_dark</item> @@ -1526,6 +1528,7 @@ please see themes_device_defaults.xml. <item name="actionBarSize">@dimen/action_bar_default_height</item> <item name="actionModePopupWindowStyle">@android:style/Widget.Holo.Light.PopupWindow.ActionMode</item> <item name="actionBarWidgetTheme">@null</item> + <item name="actionBarTheme">@null</item> <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_light</item> <item name="actionModeCopyDrawable">@android:drawable/ic_menu_copy_holo_light</item> @@ -1585,6 +1588,7 @@ please see themes_device_defaults.xml. <item name="android:windowContentOverlay">@android:drawable/ab_solid_shadow_holo</item> <item name="android:actionBarStyle">@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse</item> <item name="actionBarWidgetTheme">@android:style/Theme.Holo</item> + <item name="actionBarTheme">@null</item> <item name="actionDropDownStyle">@android:style/Widget.Holo.Spinner.DropDown.ActionBar</item> <item name="actionButtonStyle">@android:style/Widget.Holo.ActionButton</item> diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml index b5ac8fa3946a..2a80c81bf445 100644 --- a/core/res/res/values/themes_quantum.xml +++ b/core/res/res/values/themes_quantum.xml @@ -706,6 +706,7 @@ please see themes_device_defaults.xml. with an inverse color profile. The dark action bar sharply stands out against the light content. --> <style name="Theme.Quantum.Light.DarkActionBar"> + <item name="actionBarWidgetTheme">@null</item> <item name="actionBarTheme">@style/Theme.Quantum</item> </style> diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index 19131f2922ab..66a88a2aebff 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -595,7 +595,6 @@ public class BitmapDrawable extends Drawable { * Specifies the blending mode used to apply tint. * * @param tintMode A Porter-Duff blending mode - * @hide Pending finalization of supported Modes */ public void setTintMode(Mode tintMode) { if (mBitmapState.mTintMode != tintMode) { @@ -606,10 +605,7 @@ public class BitmapDrawable extends Drawable { } /** - * Returns the tint mode for this drawable, or {@code null} if none set. - * - * @return the tint mode for this drawable, or {@code null} if none set - * @hide + * @hide only needed by a hack within ProgressBar */ public Mode getTintMode() { return mBitmapState.mTintMode; diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 077db7afab26..21cd5dbccf92 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -1248,16 +1248,14 @@ public abstract class Drawable { */ static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) { switch (value) { - case 0: - return Mode.SRC_IN; - case 1: - return Mode.SRC_ATOP; - case 2: - return Mode.MULTIPLY; - case 3: - return Mode.SCREEN; + case 3: return Mode.SRC_OVER; + case 5: return Mode.SRC_IN; + case 9: return Mode.SRC_ATOP; + case 14: return Mode.MULTIPLY; + case 15: return Mode.SCREEN; + case 16: return Mode.ADD; + default: return defaultMode; } - return defaultMode; } } diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 66193a5a3048..3e9ca0ad11b0 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -345,7 +345,6 @@ public class NinePatchDrawable extends Drawable { * Specifies the blending mode used to apply tint. * * @param tintMode A Porter-Duff blending mode - * @hide Pending finalization of supported Modes */ public void setTintMode(Mode tintMode) { if (mNinePatchState.mTintMode != tintMode) { diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java index 3323a2533d79..2810c434cf1c 100644 --- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java +++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java @@ -124,6 +124,41 @@ public class TouchFeedbackDrawable extends LayerDrawable { return super.isStateful() || mState.mTint != null && mState.mTint.isStateful(); } + /** + * Specifies a tint for drawing touch feedback ripples. + * + * @param tint Color state list to use for tinting touch feedback ripples, + * or null to clear the tint + */ + public void setTint(ColorStateList tint) { + if (mState.mTint != tint) { + mState.mTint = tint; + invalidateSelf(); + } + } + + /** + * Returns the tint color for touch feedback ripples. + * + * @return Color state list to use for tinting touch feedback ripples, or + * null if none set + */ + public ColorStateList getTint() { + return mState.mTint; + } + + /** + * Specifies the blending mode used to draw touch feedback ripples. + * + * @param tintMode A Porter-Duff blending mode + */ + public void setTintMode(Mode tintMode) { + if (mState.mTintMode != tintMode) { + mState.mTintMode = tintMode; + invalidateSelf(); + } + } + @Override public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index a84aa6b690e1..140a07afc7c5 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -192,7 +192,9 @@ status_t DisplayListRenderer::drawDisplayList(RenderNode* displayList, flags, *currentTransform()); addDrawOp(op); mDisplayListData->addChild(op); - if (displayList->isProjectionReceiver()) { + + if (displayList->stagingProperties().isProjectionReceiver()) { + // use staging property, since recording on UI thread mDisplayListData->projectionReceiveIndex = mDisplayListData->displayListOps.size() - 1; } diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index fcf3b7407e67..e5b9d7c395b6 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -105,6 +105,10 @@ public: return mDisplayListData && mDisplayListData->hasDrawOps; } + const char* getName() const { + return mName.string(); + } + void setName(const char* name) { if (name) { char* lastPeriod = strrchr(name, '.'); @@ -129,10 +133,6 @@ public: return mStagingProperties; } - bool isProjectionReceiver() { - return properties().isProjectionReceiver(); - } - int getWidth() { return properties().getWidth(); } diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index ad2e330c51b8..014c7d0332b6 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -347,6 +347,7 @@ void CanvasContext::setSurface(EGLNativeWindowType window) { if (mEglSurface != EGL_NO_SURFACE) { mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); + mGlobalContext->makeCurrent(mEglSurface); mHaveNewSurface = true; } } @@ -356,14 +357,15 @@ void CanvasContext::swapBuffers() { mHaveNewSurface = false; } -void CanvasContext::makeCurrent() { +void CanvasContext::requireSurface() { + LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, + "requireSurface() called but no surface set!"); mGlobalContext->makeCurrent(mEglSurface); } bool CanvasContext::initialize(EGLNativeWindowType window) { if (mCanvas) return false; setSurface(window); - makeCurrent(); mCanvas = new OpenGLRenderer(); mCanvas->initProperties(); return true; @@ -371,7 +373,11 @@ bool CanvasContext::initialize(EGLNativeWindowType window) { void CanvasContext::updateSurface(EGLNativeWindowType window) { setSurface(window); - makeCurrent(); +} + +void CanvasContext::pauseSurface(EGLNativeWindowType window) { + // TODO: For now we just need a fence, in the future suspend any animations + // and such to prevent from trying to render into this surface } void CanvasContext::setup(int width, int height) { @@ -456,7 +462,7 @@ void CanvasContext::invokeFunctors() { if (!mCanvas) return; - makeCurrent(); + requireSurface(); Rect dirty; mCanvas->invokeFunctors(dirty); } @@ -487,12 +493,12 @@ void CanvasContext::runWithGlContext(RenderTask* task) { } Layer* CanvasContext::createRenderLayer(int width, int height) { - requireGlContext(); + requireSurface(); return LayerRenderer::createRenderLayer(width, height); } Layer* CanvasContext::createTextureLayer() { - requireGlContext(); + requireSurface(); return LayerRenderer::createTextureLayer(); } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index c878e8fd264e..4d830babfd6c 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -63,6 +63,7 @@ public: bool initialize(EGLNativeWindowType window); void updateSurface(EGLNativeWindowType window); + void pauseSurface(EGLNativeWindowType window); void setup(int width, int height); void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters); void drawDisplayList(RenderNode* displayList, Rect* dirty); @@ -82,7 +83,7 @@ public: private: void setSurface(EGLNativeWindowType window); void swapBuffers(); - void makeCurrent(); + void requireSurface(); friend class InvokeFunctorsTask; void invokeFunctors(); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index d2384098506a..49b9acab1c17 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -92,10 +92,10 @@ CREATE_BRIDGE2(initialize, CanvasContext* context, EGLNativeWindowType window) { return (void*) args->context->initialize(args->window); } -bool RenderProxy::initialize(EGLNativeWindowType window) { +bool RenderProxy::initialize(const sp<ANativeWindow>& window) { SETUP_TASK(initialize); args->context = mContext; - args->window = window; + args->window = window.get(); return (bool) postAndWait(task); } @@ -104,11 +104,23 @@ CREATE_BRIDGE2(updateSurface, CanvasContext* context, EGLNativeWindowType window return NULL; } -void RenderProxy::updateSurface(EGLNativeWindowType window) { +void RenderProxy::updateSurface(const sp<ANativeWindow>& window) { SETUP_TASK(updateSurface); args->context = mContext; - args->window = window; - post(task); + args->window = window.get(); + postAndWait(task); +} + +CREATE_BRIDGE2(pauseSurface, CanvasContext* context, EGLNativeWindowType window) { + args->context->pauseSurface(args->window); + return NULL; +} + +void RenderProxy::pauseSurface(const sp<ANativeWindow>& window) { + SETUP_TASK(pauseSurface); + args->context = mContext; + args->window = window.get(); + postAndWait(task); } CREATE_BRIDGE3(setup, CanvasContext* context, int width, int height) { diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 8cb414dc737e..1ad3c85253c5 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -59,8 +59,9 @@ public: ANDROID_API RenderProxy(bool translucent); ANDROID_API virtual ~RenderProxy(); - ANDROID_API bool initialize(EGLNativeWindowType window); - ANDROID_API void updateSurface(EGLNativeWindowType window); + ANDROID_API bool initialize(const sp<ANativeWindow>& window); + ANDROID_API void updateSurface(const sp<ANativeWindow>& window); + ANDROID_API void pauseSurface(const sp<ANativeWindow>& window); ANDROID_API void setup(int width, int height); ANDROID_API void drawDisplayList(RenderNode* displayList, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java index f688d816e69d..664c70788890 100644 --- a/media/java/android/media/MediaFocusControl.java +++ b/media/java/android/media/MediaFocusControl.java @@ -471,10 +471,6 @@ public class MediaFocusControl implements OnFinished { final FocusRequester exFocusOwner = mFocusStack.pop(); exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS); exFocusOwner.release(); - // clear RCD - synchronized(mPRStack) { - clearRemoteControlDisplay_syncAfRcs(); - } } } } @@ -535,10 +531,6 @@ public class MediaFocusControl implements OnFinished { if (signal) { // notify the new top of the stack it gained focus notifyTopOfAudioFocusStack(); - // there's a new top of the stack, let the remote control know - synchronized(mPRStack) { - checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); - } } } else { // focus is abandoned by a client that's not at the top of the stack, @@ -582,10 +574,6 @@ public class MediaFocusControl implements OnFinished { // we removed an entry at the top of the stack: // notify the new top of the stack it gained focus. notifyTopOfAudioFocusStack(); - // there's a new top of the stack, let the remote control know - synchronized(mPRStack) { - checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); - } } } @@ -694,10 +682,6 @@ public class MediaFocusControl implements OnFinished { mFocusStack.push(new FocusRequester(mainStreamType, focusChangeHint, fd, cb, clientId, afdh, callingPackageName, Binder.getCallingUid())); - // there's a new top of the stack, let the remote control know - synchronized(mPRStack) { - checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); - } }//synchronized(mAudioFocusLock) return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; @@ -1237,11 +1221,11 @@ public class MediaFocusControl implements OnFinished { /** * Helper function: * Set the new remote control receiver at the top of the RC focus stack. - * Called synchronized on mAudioFocusLock, then mPRStack + * Called synchronized on mPRStack * precondition: mediaIntent != null * @return true if mPRStack was changed, false otherwise */ - private boolean pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, + private boolean pushMediaButtonReceiver_syncPrs(PendingIntent mediaIntent, ComponentName target, IBinder token) { // already at top of stack? if (!mPRStack.empty() && mPRStack.peek().hasMatchingMediaButtonIntent(mediaIntent)) { @@ -1285,10 +1269,10 @@ public class MediaFocusControl implements OnFinished { /** * Helper function: * Remove the remote control receiver from the RC focus stack. - * Called synchronized on mAudioFocusLock, then mPRStack + * Called synchronized on mPRStack * precondition: pi != null */ - private void removeMediaButtonReceiver_syncAfRcs(PendingIntent pi) { + private void removeMediaButtonReceiver_syncPrs(PendingIntent pi) { try { for (int index = mPRStack.size()-1; index >= 0; index--) { final PlayerRecord prse = mPRStack.elementAt(index); @@ -1470,7 +1454,7 @@ public class MediaFocusControl implements OnFinished { * Helper function: * Called synchronized on mPRStack */ - private void clearRemoteControlDisplay_syncAfRcs() { + private void clearRemoteControlDisplay_syncPrs() { synchronized(mCurrentRcLock) { mCurrentRcClient = null; } @@ -1480,20 +1464,20 @@ public class MediaFocusControl implements OnFinished { /** * Helper function for code readability: only to be called from - * checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for + * checkUpdateRemoteControlDisplay_syncPrs() which checks the preconditions for * this method. * Preconditions: - * - called synchronized mAudioFocusLock then on mPRStack + * - called synchronized on mPRStack * - mPRStack.isEmpty() is false */ - private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) { + private void updateRemoteControlDisplay_syncPrs(int infoChangedFlags) { PlayerRecord prse = mPRStack.peek(); int infoFlagsAboutToBeUsed = infoChangedFlags; // this is where we enforce opt-in for information display on the remote controls // with the new AudioManager.registerRemoteControlClient() API if (prse.getRcc() == null) { //Log.w(TAG, "Can't update remote control display with null remote control client"); - clearRemoteControlDisplay_syncAfRcs(); + clearRemoteControlDisplay_syncPrs(); return; } synchronized(mCurrentRcLock) { @@ -1511,62 +1495,25 @@ public class MediaFocusControl implements OnFinished { /** * Helper function: - * Called synchronized on mAudioFocusLock, then mPRStack + * Called synchronized on mPRStack * Check whether the remote control display should be updated, triggers the update if required * @param infoChangedFlags the flags corresponding to the remote control client information * that has changed, if applicable (checking for the update conditions might trigger a * clear, rather than an update event). */ - private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) { + private void checkUpdateRemoteControlDisplay_syncPrs(int infoChangedFlags) { // determine whether the remote control display should be refreshed - // if either stack is empty, there is a mismatch, so clear the RC display - if (mPRStack.isEmpty() || mFocusStack.isEmpty()) { - clearRemoteControlDisplay_syncAfRcs(); + // if the player record stack is empty, there is nothing to display, so clear the RC display + if (mPRStack.isEmpty()) { + clearRemoteControlDisplay_syncPrs(); return; } - // determine which entry in the AudioFocus stack to consider, and compare against the - // top of the stack for the media button event receivers : simply using the top of the - // stack would make the entry disappear from the RemoteControlDisplay in conditions such as - // notifications playing during music playback. - // Crawl the AudioFocus stack from the top until an entry is found with the following - // characteristics: - // - focus gain on STREAM_MUSIC stream - // - non-transient focus gain on a stream other than music - FocusRequester af = null; - try { - for (int index = mFocusStack.size()-1; index >= 0; index--) { - FocusRequester fr = mFocusStack.elementAt(index); - if ((fr.getStreamType() == AudioManager.STREAM_MUSIC) - || (fr.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN)) { - af = fr; - break; - } - } - } catch (ArrayIndexOutOfBoundsException e) { - Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e); - af = null; - } - if (af == null) { - clearRemoteControlDisplay_syncAfRcs(); - return; - } - - // if the audio focus and RC owners belong to different packages, there is a mismatch, clear - if (!af.hasSamePackage(mPRStack.peek().getCallingPackageName())) { - clearRemoteControlDisplay_syncAfRcs(); - return; - } - // if the audio focus didn't originate from the same Uid as the one in which the remote - // control information will be retrieved, clear - if (!af.hasSameUid(mPRStack.peek().getCallingUid())) { - clearRemoteControlDisplay_syncAfRcs(); - return; - } + // this is where more rules for refresh go // refresh conditions were verified: update the remote controls - // ok to call: synchronized mAudioFocusLock then on mPRStack, mPRStack is not empty - updateRemoteControlDisplay_syncAfRcs(infoChangedFlags); + // ok to call: synchronized on mPRStack, mPRStack is not empty + updateRemoteControlDisplay_syncPrs(infoChangedFlags); } /** @@ -1582,35 +1529,33 @@ public class MediaFocusControl implements OnFinished { private void onPromoteRcc(int rccId) { if (DEBUG_RC) { Log.d(TAG, "Promoting RCC " + rccId); } - synchronized(mAudioFocusLock) { - synchronized(mPRStack) { - // ignore if given RCC ID is already at top of remote control stack - if (!mPRStack.isEmpty() && (mPRStack.peek().getRccId() == rccId)) { - return; - } - int indexToPromote = -1; - try { - for (int index = mPRStack.size()-1; index >= 0; index--) { - final PlayerRecord prse = mPRStack.elementAt(index); - if (prse.getRccId() == rccId) { - indexToPromote = index; - break; - } - } - if (indexToPromote >= 0) { - if (DEBUG_RC) { Log.d(TAG, " moving RCC from index " + indexToPromote - + " to " + (mPRStack.size()-1)); } - final PlayerRecord prse = mPRStack.remove(indexToPromote); - mPRStack.push(prse); - // the RC stack changed, reevaluate the display - checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); + synchronized(mPRStack) { + // ignore if given RCC ID is already at top of remote control stack + if (!mPRStack.isEmpty() && (mPRStack.peek().getRccId() == rccId)) { + return; + } + int indexToPromote = -1; + try { + for (int index = mPRStack.size()-1; index >= 0; index--) { + final PlayerRecord prse = mPRStack.elementAt(index); + if (prse.getRccId() == rccId) { + indexToPromote = index; + break; } - } catch (ArrayIndexOutOfBoundsException e) { - // not expected to happen, indicates improper concurrent modification - Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e); } - }//synchronized(mPRStack) - }//synchronized(mAudioFocusLock) + if (indexToPromote >= 0) { + if (DEBUG_RC) { Log.d(TAG, " moving RCC from index " + indexToPromote + + " to " + (mPRStack.size()-1)); } + final PlayerRecord prse = mPRStack.remove(indexToPromote); + mPRStack.push(prse); + // the RC stack changed, reevaluate the display + checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL); + } + } catch (ArrayIndexOutOfBoundsException e) { + // not expected to happen, indicates improper concurrent modification + Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e); + } + }//synchronized(mPRStack) } /** @@ -1621,12 +1566,10 @@ public class MediaFocusControl implements OnFinished { IBinder token) { Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent); - synchronized(mAudioFocusLock) { - synchronized(mPRStack) { - if (pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token)) { - // new RC client, assume every type of information shall be queried - checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); - } + synchronized(mPRStack) { + if (pushMediaButtonReceiver_syncPrs(mediaIntent, eventReceiver, token)) { + // new RC client, assume every type of information shall be queried + checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL); } } } @@ -1639,14 +1582,12 @@ public class MediaFocusControl implements OnFinished { { Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent); - synchronized(mAudioFocusLock) { - synchronized(mPRStack) { - boolean topOfStackWillChange = isCurrentRcController(mediaIntent); - removeMediaButtonReceiver_syncAfRcs(mediaIntent); - if (topOfStackWillChange) { - // current RC client will change, assume every type of info needs to be queried - checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); - } + synchronized(mPRStack) { + boolean topOfStackWillChange = isCurrentRcController(mediaIntent); + removeMediaButtonReceiver_syncPrs(mediaIntent); + if (topOfStackWillChange) { + // current RC client will change, assume every type of info needs to be queried + checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL); } } } @@ -1697,42 +1638,40 @@ public class MediaFocusControl implements OnFinished { IRemoteControlClient rcClient, String callingPackageName) { if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient); int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED; - synchronized(mAudioFocusLock) { - synchronized(mPRStack) { - // store the new display information - try { - for (int index = mPRStack.size()-1; index >= 0; index--) { - final PlayerRecord prse = mPRStack.elementAt(index); - if(prse.hasMatchingMediaButtonIntent(mediaIntent)) { - prse.resetControllerInfoForRcc(rcClient, callingPackageName, - Binder.getCallingUid()); + synchronized(mPRStack) { + // store the new display information + try { + for (int index = mPRStack.size()-1; index >= 0; index--) { + final PlayerRecord prse = mPRStack.elementAt(index); + if(prse.hasMatchingMediaButtonIntent(mediaIntent)) { + prse.resetControllerInfoForRcc(rcClient, callingPackageName, + Binder.getCallingUid()); - if (rcClient == null) { - break; - } + if (rcClient == null) { + break; + } - rccId = prse.getRccId(); + rccId = prse.getRccId(); - // there is a new (non-null) client: - // give the new client the displays (if any) - if (mRcDisplays.size() > 0) { - plugRemoteControlDisplaysIntoClient_syncRcStack(prse.getRcc()); - } - break; + // there is a new (non-null) client: + // give the new client the displays (if any) + if (mRcDisplays.size() > 0) { + plugRemoteControlDisplaysIntoClient_syncPrs(prse.getRcc()); } - }//for - } catch (ArrayIndexOutOfBoundsException e) { - // not expected to happen, indicates improper concurrent modification - Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e); - } + break; + } + }//for + } catch (ArrayIndexOutOfBoundsException e) { + // not expected to happen, indicates improper concurrent modification + Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e); + } - // if the eventReceiver is at the top of the stack - // then check for potential refresh of the remote controls - if (isCurrentRcController(mediaIntent)) { - checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); - } - }//synchronized(mPRStack) - }//synchronized(mAudioFocusLock) + // if the eventReceiver is at the top of the stack + // then check for potential refresh of the remote controls + if (isCurrentRcController(mediaIntent)) { + checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL); + } + }//synchronized(mPRStack) return rccId; } @@ -1743,29 +1682,27 @@ public class MediaFocusControl implements OnFinished { protected void unregisterRemoteControlClient(PendingIntent mediaIntent, IRemoteControlClient rcClient) { if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient); - synchronized(mAudioFocusLock) { - synchronized(mPRStack) { - boolean topRccChange = false; - try { - for (int index = mPRStack.size()-1; index >= 0; index--) { - final PlayerRecord prse = mPRStack.elementAt(index); - if ((prse.hasMatchingMediaButtonIntent(mediaIntent)) - && rcClient.equals(prse.getRcc())) { - // we found the IRemoteControlClient to unregister - prse.resetControllerInfoForNoRcc(); - topRccChange = (index == mPRStack.size()-1); - // there can only be one matching RCC in the RC stack, we're done - break; - } + synchronized(mPRStack) { + boolean topRccChange = false; + try { + for (int index = mPRStack.size()-1; index >= 0; index--) { + final PlayerRecord prse = mPRStack.elementAt(index); + if ((prse.hasMatchingMediaButtonIntent(mediaIntent)) + && rcClient.equals(prse.getRcc())) { + // we found the IRemoteControlClient to unregister + prse.resetControllerInfoForNoRcc(); + topRccChange = (index == mPRStack.size()-1); + // there can only be one matching RCC in the RC stack, we're done + break; } - } catch (ArrayIndexOutOfBoundsException e) { - // not expected to happen, indicates improper concurrent modification - Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e); - } - if (topRccChange) { - // no more RCC for the RCD, check for potential refresh of the remote controls - checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); } + } catch (ArrayIndexOutOfBoundsException e) { + // not expected to happen, indicates improper concurrent modification + Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e); + } + if (topRccChange) { + // no more RCC for the RCD, check for potential refresh of the remote controls + checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL); } } } @@ -1843,7 +1780,7 @@ public class MediaFocusControl implements OnFinished { * Plug each registered display into the specified client * @param rcc, guaranteed non null */ - private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) { + private void plugRemoteControlDisplaysIntoClient_syncPrs(IRemoteControlClient rcc) { final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { final DisplayInfoForServer di = displayIterator.next(); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java index 26498ca8aa16..edfa36a5d346 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java @@ -552,29 +552,72 @@ public class CameraMetadataTest extends junit.framework.TestCase { }; int availableFormatTag = CameraMetadataNative.getTag("android.scaler.availableFormats"); - // Write - mMetadata.set(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, availableFormats); + Key<int[]> formatKey = CameraCharacteristics.SCALER_AVAILABLE_FORMATS; - byte[] availableFormatValues = mMetadata.readValues(availableFormatTag); + validateArrayMetadataReadWriteOverride(formatKey, availableFormats, + expectedIntValues, availableFormatTag); - ByteBuffer bf = ByteBuffer.wrap(availableFormatValues).order(ByteOrder.nativeOrder()); + // + // android.scaler.availableStreamConfigurations (int x n x 4 array) + // + final int OUTPUT = CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT; + int[] availableStreamConfigs = new int[] { + 0x20, 3280, 2464, OUTPUT, // RAW16 + 0x23, 3264, 2448, OUTPUT, // YCbCr_420_888 + 0x23, 3200, 2400, OUTPUT, // YCbCr_420_888 + 0x100, 3264, 2448, OUTPUT, // ImageFormat.JPEG + 0x100, 3200, 2400, OUTPUT, // ImageFormat.JPEG + 0x100, 2592, 1944, OUTPUT, // ImageFormat.JPEG + 0x100, 2048, 1536, OUTPUT, // ImageFormat.JPEG + 0x100, 1920, 1080, OUTPUT // ImageFormat.JPEG + }; + int[] expectedAvailableStreamConfigs = new int[] { + 0x20, 3280, 2464, OUTPUT, // RAW16 + 0x23, 3264, 2448, OUTPUT, // YCbCr_420_888 + 0x23, 3200, 2400, OUTPUT, // YCbCr_420_888 + 0x21, 3264, 2448, OUTPUT, // BLOB + 0x21, 3200, 2400, OUTPUT, // BLOB + 0x21, 2592, 1944, OUTPUT, // BLOB + 0x21, 2048, 1536, OUTPUT, // BLOB + 0x21, 1920, 1080, OUTPUT // BLOB + }; + int availableStreamConfigTag = + CameraMetadataNative.getTag("android.scaler.availableStreamConfigurations"); - assertEquals(expectedIntValues.length * 4, availableFormatValues.length); - for (int i = 0; i < expectedIntValues.length; ++i) { - assertEquals(expectedIntValues[i], bf.getInt()); - } - // Read - byte[] availableFormatsAsByteArray = new byte[expectedIntValues.length * 4]; - ByteBuffer availableFormatsByteBuffer = - ByteBuffer.wrap(availableFormatsAsByteArray).order(ByteOrder.nativeOrder()); - for (int value : expectedIntValues) { - availableFormatsByteBuffer.putInt(value); - } - mMetadata.writeValues(availableFormatTag, availableFormatsAsByteArray); + Key<int[]> configKey = CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS; + validateArrayMetadataReadWriteOverride(configKey, availableStreamConfigs, + expectedAvailableStreamConfigs, availableStreamConfigTag); - int[] resultFormats = mMetadata.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS); - assertNotNull("result available formats shouldn't be null", resultFormats); - assertArrayEquals(availableFormats, resultFormats); + // + // android.scaler.availableMinFrameDurations (int x n x 4 array) + + // + long[] availableMinDurations = new long[] { + 0x20, 3280, 2464, 33333336, // RAW16 + 0x23, 3264, 2448, 33333336, // YCbCr_420_888 + 0x23, 3200, 2400, 33333336, // YCbCr_420_888 + 0x100, 3264, 2448, 33333336, // ImageFormat.JPEG + 0x100, 3200, 2400, 33333336, // ImageFormat.JPEG + 0x100, 2592, 1944, 33333336, // ImageFormat.JPEG + 0x100, 2048, 1536, 33333336, // ImageFormat.JPEG + 0x100, 1920, 1080, 33333336 // ImageFormat.JPEG + }; + long[] expectedAvailableMinDurations = new long[] { + 0x20, 3280, 2464, 33333336, // RAW16 + 0x23, 3264, 2448, 33333336, // YCbCr_420_888 + 0x23, 3200, 2400, 33333336, // YCbCr_420_888 + 0x21, 3264, 2448, 33333336, // BLOB + 0x21, 3200, 2400, 33333336, // BLOB + 0x21, 2592, 1944, 33333336, // BLOB + 0x21, 2048, 1536, 33333336, // BLOB + 0x21, 1920, 1080, 33333336 // BLOB + }; + int availableMinDurationsTag = + CameraMetadataNative.getTag("android.scaler.availableMinFrameDurations"); + + Key<long[]> durationKey = CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS; + validateArrayMetadataReadWriteOverride(durationKey, availableMinDurations, + expectedAvailableMinDurations, availableMinDurationsTag); // // android.statistics.faces (Face x n array) @@ -639,4 +682,59 @@ public class CameraMetadataTest extends junit.framework.TestCase { } } + + /** + * Validate metadata array tag read/write override. + * + * <p>Only support long and int array for now, can be easily extend to support other + * primitive arrays.</p> + */ + private <T> void validateArrayMetadataReadWriteOverride(Key<T> key, T writeValues, + T readValues, int tag) { + Class<T> type = key.getType(); + if (!type.isArray()) { + throw new IllegalArgumentException("This function expects an key with array type"); + } else if (type != int[].class && type != long[].class) { + throw new IllegalArgumentException("This function expects long or int array values"); + } + + // Write + mMetadata.set(key, writeValues); + + byte[] readOutValues = mMetadata.readValues(tag); + + ByteBuffer bf = ByteBuffer.wrap(readOutValues).order(ByteOrder.nativeOrder()); + + int readValuesLength = Array.getLength(readValues); + int readValuesNumBytes = readValuesLength * 4; + if (type == long[].class) { + readValuesNumBytes = readValuesLength * 8; + } + + assertEquals(readValuesNumBytes, readOutValues.length); + for (int i = 0; i < readValuesLength; ++i) { + if (type == int[].class) { + assertEquals(Array.getInt(readValues, i), bf.getInt()); + } else if (type == long[].class) { + assertEquals(Array.getLong(readValues, i), bf.getLong()); + } + } + + // Read + byte[] readOutValuesAsByteArray = new byte[readValuesNumBytes]; + ByteBuffer readOutValuesByteBuffer = + ByteBuffer.wrap(readOutValuesAsByteArray).order(ByteOrder.nativeOrder()); + for (int i = 0; i < readValuesLength; ++i) { + if (type == int[].class) { + readOutValuesByteBuffer.putInt(Array.getInt(readValues, i)); + } else if (type == long[].class) { + readOutValuesByteBuffer.putLong(Array.getLong(readValues, i)); + } + } + mMetadata.writeValues(tag, readOutValuesAsByteArray); + + T result = mMetadata.get(key); + assertNotNull(key.getName() + " result shouldn't be null", result); + assertArrayEquals(writeValues, result); + } } diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ce05639fd21b..ad10545d28cd 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -526,4 +526,12 @@ <string name="description_direction_up">Slide up for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string> <!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] --> <string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string> + + <!-- Zen mode: Summary notification content title. [CHAR LIMIT=NONE] --> + <plurals name="zen_mode_notification_title"> + <item quantity="one">Notification hidden</item> + <item quantity="other">%d notifications hidden</item> + </plurals> + <!-- Zen mode: Summary notification content text. [CHAR LIMIT=NONE] --> + <string name="zen_mode_notification_text">Touch to show</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index dd75921c37a7..64c67b1b522b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -43,6 +43,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView boolean mVisible; boolean mTaskLaunched; + // Broadcast receiver to handle messages from our RecentsService BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -63,6 +64,14 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } }; + // Broadcast receiver to handle messages from the system + BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + finish(); + } + }; + /** Updates the set of recent tasks */ void updateRecentsTasks() { RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); @@ -111,12 +120,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView RecentsTaskLoader.initialize(this); RecentsConfiguration.reinitialize(this); - // Set the background dim - WindowManager.LayoutParams wlp = getWindow().getAttributes(); - wlp.dimAmount = Constants.Values.Window.BackgroundDim; - getWindow().setAttributes(wlp); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); - // Create the view hierarchy mRecentsView = new RecentsView(this); mRecentsView.setCallbacks(this); @@ -170,22 +173,44 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onResume]", "", Console.AnsiRed); super.onResume(); + } + + @Override + public void onAttachedToWindow() { + Console.log(Constants.DebugFlags.App.SystemUIHandshake, + "[RecentsActivity|onAttachedToWindow]", "", + Console.AnsiRed); + super.onAttachedToWindow(); // Register the broadcast receiver to handle messages from our service IntentFilter filter = new IntentFilter(); filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY); filter.addAction(RecentsService.ACTION_FINISH_RECENTS_ACTIVITY); registerReceiver(mServiceBroadcastReceiver, filter); + + // Register the broadcast receiver to handle messages when the screen is turned off + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_OFF); + registerReceiver(mScreenOffReceiver, filter); } @Override - protected void onPause() { - Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onPause]", "", + public void onDetachedFromWindow() { + Console.log(Constants.DebugFlags.App.SystemUIHandshake, + "[RecentsActivity|onDetachedFromWindow]", "", Console.AnsiRed); - super.onPause(); + super.onDetachedFromWindow(); // Unregister any broadcast receivers we have registered unregisterReceiver(mServiceBroadcastReceiver); + unregisterReceiver(mScreenOffReceiver); + } + + @Override + protected void onPause() { + Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onPause]", "", + Console.AnsiRed); + super.onPause(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 4a0de0bae118..94a655f95619 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -17,6 +17,7 @@ package com.android.systemui.recents; import android.content.Context; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.util.DisplayMetrics; @@ -62,6 +63,12 @@ public class RecentsConfiguration { DisplayMetrics dm = res.getDisplayMetrics(); mDisplayMetrics = dm; + boolean isLandscape = res.getConfiguration().orientation == + Configuration.ORIENTATION_LANDSCAPE; + Console.log(Constants.DebugFlags.UI.MeasureAndLayout, + "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait", + Console.AnsiGreen); + displayRect.set(0, 0, dm.widthPixels, dm.heightPixels); animationPxMovementPerSecond = res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java index d661f287c13c..754d95657d17 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java @@ -22,7 +22,6 @@ import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -213,6 +212,7 @@ class TaskResourceLoader implements Runnable { Console.log(Constants.DebugFlags.App.TaskDataLoader, " [TaskResourceLoader|loadThumbnail]", thumbnail); + thumbnail.setHasAlpha(false); loadThumbnail = thumbnail; mThumbnailCache.put(t.key, thumbnail); } else { @@ -331,13 +331,9 @@ public class RecentsTaskLoader { // Create the default assets Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + icon.eraseColor(0x00000000); mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(); - c.setBitmap(icon); - c.drawColor(0x00000000); - c.setBitmap(mDefaultThumbnail); - c.drawColor(0x00000000); - c.setBitmap(null); + mDefaultThumbnail.eraseColor(0x00000000); mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon); Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|defaultBitmaps]", @@ -454,6 +450,7 @@ public class RecentsTaskLoader { "[RecentsTaskLoader|loadingTaskThumbnail]"); task.thumbnail = ssp.getTaskThumbnail(task.key.id); if (task.thumbnail != null) { + task.thumbnail.setHasAlpha(false); mThumbnailCache.put(task.key, task.thumbnail); } else { task.thumbnail = mDefaultThumbnail; diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java index efcd948d830d..505238dbc4aa 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -52,9 +51,7 @@ public class SystemServicesProxy { if (Constants.DebugFlags.App.EnableSystemServicesProxy) { // Create a dummy icon mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(mDummyIcon); - c.drawColor(0xFF999999); - c.setBitmap(null); + mDummyIcon.eraseColor(0xFF999999); } } @@ -117,9 +114,7 @@ public class SystemServicesProxy { // If we are mocking, then just return a dummy thumbnail if (Constants.DebugFlags.App.EnableSystemServicesProxy) { Bitmap thumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(thumbnail); - c.drawColor(0xff333333); - c.setBitmap(null); + thumbnail.eraseColor(0xff333333); return thumbnail; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 1ebe2317883e..141d870ebf79 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -108,6 +108,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); @@ -118,6 +119,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // We measure our stack views sans the status bar. It will handle the nav bar itself. RecentsConfiguration config = RecentsConfiguration.getInstance(); + int childWidth = width - config.systemInsets.right; int childHeight = height - config.systemInsets.top; // Measure each child @@ -125,7 +127,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { - child.measure(widthMeasureSpec, + child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode), MeasureSpec.makeMeasureSpec(childHeight, heightMode)); } } @@ -255,11 +257,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV | Intent.FLAG_ACTIVITY_TASK_ON_HOME | Intent.FLAG_ACTIVITY_NEW_TASK); try { + UserHandle taskUser = new UserHandle(task.userId); if (opts != null) { - getContext().startActivityAsUser(i, opts.toBundle(), - new UserHandle(task.userId)); + getContext().startActivityAsUser(i, opts.toBundle(), taskUser); } else { - getContext().startActivityAsUser(i, new UserHandle(task.userId)); + getContext().startActivityAsUser(i, taskUser); } } catch (ActivityNotFoundException anfe) { Console.logError(getContext(), "Could not start Activity"); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index fa06764e9b3e..c9a491e6cd5f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -524,8 +524,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal (Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height())); int size = Math.min(minHeight, Math.min(mStackRect.width(), mStackRect.height())); int centerX = mStackRect.centerX(); - mTaskRect.set(centerX - size / 2, mStackRectSansPeek.top, - centerX + size / 2, mStackRectSansPeek.top + size); + mTaskRect.set(mStackRect.left, mStackRectSansPeek.top, + mStackRect.right, mStackRectSansPeek.top + size); // Update the scroll bounds updateMinMaxScroll(false); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 844b96480d87..f1299fed96c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -109,9 +109,6 @@ public abstract class BaseStatusBar extends SystemUI implements public static final int EXPANDED_LEAVE_ALONE = -10000; public static final int EXPANDED_FULL_OPEN = -10001; - private static final String EXTRA_INTERCEPT = "android.intercept"; - private static final float INTERCEPTED_ALPHA = .2f; - protected CommandQueue mCommandQueue; protected IStatusBarService mBarService; protected H mHandler = createHandler(); @@ -1049,7 +1046,6 @@ public abstract class BaseStatusBar extends SystemUI implements if (DEBUG) { Log.d(TAG, "addNotificationViews: added at " + pos); } - updateInterceptedState(entry); updateExpansionStates(); updateNotificationIcons(); } @@ -1082,32 +1078,10 @@ public abstract class BaseStatusBar extends SystemUI implements protected void setZenMode(int mode) { if (!isDeviceProvisioned()) return; - final boolean change = mZenMode != mode; mZenMode = mode; - final int N = mNotificationData.size(); - for (int i = 0; i < N; i++) { - final NotificationData.Entry entry = mNotificationData.get(i); - if (change && !shouldIntercept()) { - entry.notification.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false); - } - updateInterceptedState(entry); - } updateNotificationIcons(); } - private boolean shouldIntercept() { - return mZenMode != Settings.Global.ZEN_MODE_OFF; - } - - protected boolean shouldIntercept(Notification n) { - return shouldIntercept() && n.extras.getBoolean(EXTRA_INTERCEPT); - } - - private void updateInterceptedState(NotificationData.Entry entry) { - final boolean intercepted = shouldIntercept(entry.notification.getNotification()); - entry.row.findViewById(R.id.container).setAlpha(intercepted ? INTERCEPTED_ALPHA : 1); - } - protected abstract void haltTicker(); protected abstract void setAreThereNotifications(); protected abstract void updateNotificationIcons(); @@ -1312,7 +1286,6 @@ public abstract class BaseStatusBar extends SystemUI implements } else { entry.content.setOnClickListener(null); } - updateInterceptedState(entry); } protected void notifyHeadsUpScreenOn(boolean screenOn) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java new file mode 100644 index 000000000000..d563968a5908 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import android.app.Notification; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.Process; +import android.service.notification.StatusBarNotification; +import android.util.ArrayMap; +import android.view.View; + +import com.android.systemui.R; +import com.android.systemui.statusbar.NotificationData.Entry; +import com.android.systemui.statusbar.phone.PhoneStatusBar; + +public class InterceptedNotifications { + private static final String TAG = "InterceptedNotifications"; + private static final String EXTRA_INTERCEPT = "android.intercept"; + + private final Context mContext; + private final PhoneStatusBar mBar; + private final ArrayMap<IBinder, StatusBarNotification> mIntercepted + = new ArrayMap<IBinder, StatusBarNotification>(); + + private Binder mSynKey; + + public InterceptedNotifications(Context context, PhoneStatusBar bar) { + mContext = context; + mBar = bar; + } + + public void releaseIntercepted() { + final int n = mIntercepted.size(); + for (int i = 0; i < n; i++) { + final IBinder key = mIntercepted.keyAt(i); + final StatusBarNotification sbn = mIntercepted.valueAt(i); + sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false); + mBar.addNotification(key, sbn); + } + mIntercepted.clear(); + updateSyntheticNotification(); + } + + public boolean tryIntercept(IBinder key, StatusBarNotification notification) { + if (!notification.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) return false; + mIntercepted.put(key, notification); + updateSyntheticNotification(); + return true; + } + + public void remove(IBinder key) { + if (mIntercepted.remove(key) != null) { + updateSyntheticNotification(); + } + } + + public boolean isSyntheticEntry(Entry ent) { + return mSynKey != null && ent.key.equals(mSynKey); + } + + public void update(IBinder key, StatusBarNotification notification) { + if (mIntercepted.containsKey(key)) { + mIntercepted.put(key, notification); + } + } + + private void updateSyntheticNotification() { + if (mIntercepted.isEmpty()) { + if (mSynKey != null) { + mBar.removeNotification(mSynKey); + mSynKey = null; + } + return; + } + final Notification n = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.stat_sys_zen_limited) + .setContentTitle(mContext.getResources().getQuantityString( + R.plurals.zen_mode_notification_title, + mIntercepted.size(), mIntercepted.size())) + .setContentText(mContext.getString(R.string.zen_mode_notification_text)) + .setOngoing(true) + .build(); + final StatusBarNotification sbn = new StatusBarNotification(mContext.getPackageName(), + mContext.getBasePackageName(), + TAG.hashCode(), TAG, Process.myUid(), Process.myPid(), 0, n, + mBar.getCurrentUserHandle()); + if (mSynKey == null) { + mSynKey = new Binder(); + mBar.addNotification(mSynKey, sbn); + } else { + mBar.updateNotification(mSynKey, sbn); + } + final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey); + entry.content.setOnClickListener(mSynClickListener); + } + + private final View.OnClickListener mSynClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + releaseIntercepted(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 4730f2fd0613..f3dd7e3dbe04 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -59,6 +59,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.provider.Settings.Global; import android.service.notification.StatusBarNotification; import android.util.DisplayMetrics; import android.util.EventLog; @@ -89,11 +90,11 @@ import com.android.systemui.R; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.GestureRecorder; +import com.android.systemui.statusbar.InterceptedNotifications; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.SignalClusterView; import com.android.systemui.statusbar.StatusBarIconView; - import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.DateView; @@ -101,7 +102,6 @@ import com.android.systemui.statusbar.policy.HeadsUpNotificationView; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.RotationLockController; - import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import java.io.FileDescriptor; @@ -347,6 +347,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { }}; private Runnable mOnFlipRunnable; + private InterceptedNotifications mIntercepted; public void setOnFlipRunnable(Runnable onFlipRunnable) { mOnFlipRunnable = onFlipRunnable; @@ -357,7 +358,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { super.setZenMode(mode); if (mModeIcon == null) return; if (!isDeviceProvisioned()) return; - mModeIcon.setVisibility(mode != Settings.Global.ZEN_MODE_OFF ? View.VISIBLE : View.GONE); + final boolean zen = mode != Settings.Global.ZEN_MODE_OFF; + mModeIcon.setVisibility(zen ? View.VISIBLE : View.GONE); + if (!zen) { + mIntercepted.releaseIntercepted(); + } } @Override @@ -365,7 +370,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); updateDisplaySize(); - + mIntercepted = new InterceptedNotifications(mContext, this); super.start(); // calls createAndAddWindows() addNavigationBar(); @@ -931,49 +936,54 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { mStatusIcons.removeViewAt(viewIndex); } + public UserHandle getCurrentUserHandle() { + return new UserHandle(mCurrentUserId); + } + public void addNotification(IBinder key, StatusBarNotification notification) { if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore()); Entry shadeEntry = createNotificationViews(key, notification); if (shadeEntry == null) { return; } - if (!shouldIntercept(notification.getNotification())) { - if (mUseHeadsUp && shouldInterrupt(notification)) { - if (DEBUG) Log.d(TAG, "launching notification in heads up mode"); - Entry interruptionCandidate = new Entry(key, notification, null); - ViewGroup holder = mHeadsUpNotificationView.getHolder(); - if (inflateViewsForHeadsUp(interruptionCandidate, holder)) { - mInterruptingNotificationTime = System.currentTimeMillis(); - mInterruptingNotificationEntry = interruptionCandidate; - shadeEntry.setInterruption(); + if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(key, notification)) { + return; + } + if (mUseHeadsUp && shouldInterrupt(notification)) { + if (DEBUG) Log.d(TAG, "launching notification in heads up mode"); + Entry interruptionCandidate = new Entry(key, notification, null); + ViewGroup holder = mHeadsUpNotificationView.getHolder(); + if (inflateViewsForHeadsUp(interruptionCandidate, holder)) { + mInterruptingNotificationTime = System.currentTimeMillis(); + mInterruptingNotificationEntry = interruptionCandidate; + shadeEntry.setInterruption(); - // 1. Populate mHeadsUpNotificationView - mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry); + // 1. Populate mHeadsUpNotificationView + mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry); - // 2. Animate mHeadsUpNotificationView in - mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP); + // 2. Animate mHeadsUpNotificationView in + mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP); - // 3. Set alarm to age the notification off - resetHeadsUpDecayTimer(); - } - } else if (notification.getNotification().fullScreenIntent != null) { - // Stop screensaver if the notification has a full-screen intent. - // (like an incoming phone call) - awakenDreams(); + // 3. Set alarm to age the notification off + resetHeadsUpDecayTimer(); + } + } else if (notification.getNotification().fullScreenIntent != null) { + // Stop screensaver if the notification has a full-screen intent. + // (like an incoming phone call) + awakenDreams(); - // not immersive & a full-screen alert should be shown - if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); - try { - notification.getNotification().fullScreenIntent.send(); - } catch (PendingIntent.CanceledException e) { - } - } else { - // usual case: status bar visible & not immersive + // not immersive & a full-screen alert should be shown + if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); + try { + notification.getNotification().fullScreenIntent.send(); + } catch (PendingIntent.CanceledException e) { + } + } else { + // usual case: status bar visible & not immersive - // show the ticker if there isn't already a heads up - if (mInterruptingNotificationEntry == null) { - tick(null, notification, true); - } + // show the ticker if there isn't already a heads up + if (mInterruptingNotificationEntry == null) { + tick(null, notification, true); } } addNotificationViews(shadeEntry); @@ -991,6 +1001,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } } + @Override + public void updateNotification(IBinder key, StatusBarNotification notification) { + super.updateNotification(key, notification); + mIntercepted.update(key, notification); + } + public void removeNotification(IBinder key) { StatusBarNotification old = removeNotificationViews(key); if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); @@ -1012,7 +1028,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { animateCollapsePanels(); } } - + mIntercepted.remove(key); setAreThereNotifications(); } @@ -1129,7 +1145,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { // in "public" mode (atop a secure keyguard), secret notifs are totally hidden continue; } - if (shouldIntercept(ent.notification.getNotification())) { + if (mIntercepted.isSyntheticEntry(ent)) { continue; } toShow.add(ent.icon); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 972b0883c727..79c4a50f35b3 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -151,8 +151,11 @@ public final class TvInputManagerService extends SystemService { private void removeUser(int userId) { synchronized (mLock) { + UserState userState = mUserStates.get(userId); + if (userState == null) { + return; + } // Release created sessions. - UserState userState = getUserStateLocked(userId); for (SessionState state : userState.sessionStateMap.values()) { if (state.session != null) { try { |