diff options
67 files changed, 1343 insertions, 475 deletions
diff --git a/api/current.txt b/api/current.txt index 85a3fc5..ffdd9f7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5511,8 +5511,8 @@ package android.app.admin { field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.DEVICE_ADMIN_PACKAGE_CHECKSUM"; field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER"; field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION"; - field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.DEVICE_ADMIN_PACKAGE_NAME"; - field public static final java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.MANAGED_PROFILE_EMAIL_ADDRESS"; + field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.deviceAdminPackageName"; + field public static final java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.ManagedProfileEmailAddress"; field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.LOCALE"; field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.LOCAL_TIME"; field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.TIME_ZONE"; @@ -8902,6 +8902,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass"; field public static final java.lang.String FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope"; field public static final java.lang.String FEATURE_SENSOR_HEART_RATE = "android.hardware.sensor.heartrate"; + field public static final java.lang.String FEATURE_SENSOR_HEART_RATE_ECG = "android.hardware.sensor.heartrate.ecg"; field public static final java.lang.String FEATURE_SENSOR_LIGHT = "android.hardware.sensor.light"; field public static final java.lang.String FEATURE_SENSOR_PROXIMITY = "android.hardware.sensor.proximity"; field public static final java.lang.String FEATURE_SENSOR_RELATIVE_HUMIDITY = "android.hardware.sensor.relative_humidity"; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 2667bcd..2173647 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -796,6 +796,12 @@ public class ActivityManager { public static final int RECENT_INCLUDE_PROFILES = 0x0004; /** + * Ignores all tasks that are on the home stack. + * @hide + */ + public static final int RECENT_IGNORE_HOME_STACK_TASKS = 0x0008; + + /** * <p></p>Return a list of the tasks that the user has recently launched, with * the most recent being first and older ones after in order. * diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 0653ca9..9dd4605 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -128,6 +128,10 @@ public class ActivityOptions { public static final int ANIM_DEFAULT = 6; /** @hide */ public static final int ANIM_LAUNCH_TASK_BEHIND = 7; + /** @hide */ + public static final int ANIM_THUMBNAIL_ASPECT_SCALE_UP = 8; + /** @hide */ + public static final int ANIM_THUMBNAIL_ASPECT_SCALE_DOWN = 9; private String mPackageName; private int mAnimationType = ANIM_NONE; @@ -338,6 +342,67 @@ public class ActivityOptions { } /** + * Create an ActivityOptions specifying an animation where the new activity + * window and a thumbnail is aspect-scaled to a new location. + * + * @param source The View that this thumbnail is animating from. This + * defines the coordinate space for <var>startX</var> and <var>startY</var>. + * @param thumbnail The bitmap that will be shown as the initial thumbnail + * of the animation. + * @param startX The x starting location of the bitmap, relative to <var>source</var>. + * @param startY The y starting location of the bitmap, relative to <var>source</var>. + * @param listener Optional OnAnimationStartedListener to find out when the + * requested animation has started running. If for some reason the animation + * is not executed, the callback will happen immediately. + * @return Returns a new ActivityOptions object that you can use to + * supply these options as the options Bundle when starting an activity. + * @hide + */ + public static ActivityOptions makeThumbnailAspectScaleUpAnimation(View source, + Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { + return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, listener, + true); + } + + /** + * Create an ActivityOptions specifying an animation where the new activity + * window and a thumbnail is aspect-scaled to a new location. + * + * @param source The View that this thumbnail is animating to. This + * defines the coordinate space for <var>startX</var> and <var>startY</var>. + * @param thumbnail The bitmap that will be shown as the final thumbnail + * of the animation. + * @param startX The x end location of the bitmap, relative to <var>source</var>. + * @param startY The y end location of the bitmap, relative to <var>source</var>. + * @param listener Optional OnAnimationStartedListener to find out when the + * requested animation has started running. If for some reason the animation + * is not executed, the callback will happen immediately. + * @return Returns a new ActivityOptions object that you can use to + * supply these options as the options Bundle when starting an activity. + * @hide + */ + public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source, + Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { + return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, listener, + false); + } + + private static ActivityOptions makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail, + int startX, int startY, OnAnimationStartedListener listener, boolean scaleUp) { + ActivityOptions opts = new ActivityOptions(); + opts.mPackageName = source.getContext().getPackageName(); + opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_ASPECT_SCALE_UP : + ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; + opts.mThumbnail = thumbnail; + int[] pts = new int[2]; + source.getLocationOnScreen(pts); + opts.mStartX = pts[0] + startX; + opts.mStartY = pts[1] + startY; + opts.setOnAnimationStartedListener(source.getHandler(), listener); + return opts; + } + + /** * Create an ActivityOptions to transition between Activities using cross-Activity scene * animations. This method carries the position of one shared element to the started Activity. * The position of <code>sharedElement</code> will be used as the epicenter for the @@ -484,7 +549,9 @@ public class ActivityOptions { case ANIM_THUMBNAIL_SCALE_UP: case ANIM_THUMBNAIL_SCALE_DOWN: - mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL); + case ANIM_THUMBNAIL_ASPECT_SCALE_UP: + case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: + mThumbnail = (Bitmap) opts.getParcelable(KEY_ANIM_THUMBNAIL); mStartX = opts.getInt(KEY_ANIM_START_X, 0); mStartY = opts.getInt(KEY_ANIM_START_Y, 0); mAnimationStartedListener = IRemoteCallback.Stub.asInterface( @@ -635,6 +702,8 @@ public class ActivityOptions { break; case ANIM_THUMBNAIL_SCALE_UP: case ANIM_THUMBNAIL_SCALE_DOWN: + case ANIM_THUMBNAIL_ASPECT_SCALE_UP: + case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: mThumbnail = otherOptions.mThumbnail; mStartX = otherOptions.mStartX; mStartY = otherOptions.mStartY; @@ -691,6 +760,8 @@ public class ActivityOptions { break; case ANIM_THUMBNAIL_SCALE_UP: case ANIM_THUMBNAIL_SCALE_DOWN: + case ANIM_THUMBNAIL_ASPECT_SCALE_UP: + case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail); b.putInt(KEY_ANIM_START_X, mStartX); b.putInt(KEY_ANIM_START_Y, mStartY); diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index e0c7816..d143b86 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -51,8 +51,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.Collections; @@ -594,55 +594,6 @@ public final class LoadedApk { return app; } - private void rewriteIntField(Field field, int packageId) throws IllegalAccessException { - int requiredModifiers = Modifier.STATIC | Modifier.PUBLIC; - int bannedModifiers = Modifier.FINAL; - - int mod = field.getModifiers(); - if ((mod & requiredModifiers) != requiredModifiers || - (mod & bannedModifiers) != 0) { - throw new IllegalArgumentException("Field " + field.getName() + - " is not rewritable"); - } - - if (field.getType() != int.class && field.getType() != Integer.class) { - throw new IllegalArgumentException("Field " + field.getName() + - " is not an integer"); - } - - try { - int resId = field.getInt(null); - field.setInt(null, (resId & 0x00ffffff) | (packageId << 24)); - } catch (IllegalAccessException e) { - // This should not occur (we check above if we can write to it) - throw new IllegalArgumentException(e); - } - } - - private void rewriteIntArrayField(Field field, int packageId) { - int requiredModifiers = Modifier.STATIC | Modifier.PUBLIC; - - if ((field.getModifiers() & requiredModifiers) != requiredModifiers) { - throw new IllegalArgumentException("Field " + field.getName() + - " is not rewritable"); - } - - if (field.getType() != int[].class) { - throw new IllegalArgumentException("Field " + field.getName() + - " is not an integer array"); - } - - try { - int[] array = (int[]) field.get(null); - for (int i = 0; i < array.length; i++) { - array[i] = (array[i] & 0x00ffffff) | (packageId << 24); - } - } catch (IllegalAccessException e) { - // This should not occur (we check above if we can write to it) - throw new IllegalArgumentException(e); - } - } - private void rewriteRValues(ClassLoader cl, String packageName, int id) { final Class<?> rClazz; try { @@ -650,35 +601,30 @@ public final class LoadedApk { } catch (ClassNotFoundException e) { // This is not necessarily an error, as some packages do not ship with resources // (or they do not need rewriting). - Log.i(TAG, "Could not find R class for package '" + packageName + "'"); + Log.i(TAG, "No resource references to update in package " + packageName); return; } + final Method callback; try { - Class<?>[] declaredClasses = rClazz.getDeclaredClasses(); - for (Class<?> clazz : declaredClasses) { - try { - if (clazz.getSimpleName().equals("styleable")) { - for (Field field : clazz.getDeclaredFields()) { - if (field.getType() == int[].class) { - rewriteIntArrayField(field, id); - } - } - - } else { - for (Field field : clazz.getDeclaredFields()) { - rewriteIntField(field, id); - } - } - } catch (Exception e) { - throw new IllegalArgumentException("Failed to rewrite R values for " + - clazz.getName(), e); - } - } + callback = rClazz.getMethod("onResourcesLoaded", int.class); + } catch (NoSuchMethodException e) { + // No rewriting to be done. + return; + } - } catch (Exception e) { - throw new IllegalArgumentException("Failed to rewrite R values", e); + Throwable cause; + try { + callback.invoke(null, id); + return; + } catch (IllegalAccessException e) { + cause = e; + } catch (InvocationTargetException e) { + cause = e.getCause(); } + + throw new RuntimeException("Failed to rewrite resource references for " + packageName, + cause); } public void removeContextRegistrations(Context context, diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 41bbb87..b17309f 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -126,7 +126,7 @@ public class DevicePolicyManager { * message containing an Nfc record with MIME type {@link #MIME_TYPE_PROVISIONING_NFC}. */ public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME - = "android.app.extra.DEVICE_ADMIN_PACKAGE_NAME"; + = "android.app.extra.deviceAdminPackageName"; /** * A String extra holding the default name of the profile that is created during managed profile @@ -149,7 +149,7 @@ public class DevicePolicyManager { * It is usually used to avoid that the user has to enter their email address twice. */ public static final String EXTRA_PROVISIONING_EMAIL_ADDRESS - = "android.app.extra.MANAGED_PROFILE_EMAIL_ADDRESS"; + = "android.app.extra.ManagedProfileEmailAddress"; /** * A String extra holding the time zone {@link android.app.AlarmManager} that the device diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 56b7164..5492775 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1217,6 +1217,14 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The heart rate sensor on this device is an Electrocargiogram. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SENSOR_HEART_RATE_ECG = + "android.hardware.sensor.heartrate.ecg"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device includes a relative humidity sensor. */ @SdkConstant(SdkConstantType.FEATURE) diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index d19418b..e63fd07 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -533,6 +533,18 @@ public final class Configuration implements Parcelable, Comparable<Configuration public static final int DENSITY_DPI_UNDEFINED = 0; /** + * Value for {@link #densityDpi} for resources that scale to any density (vector drawables). + * {@hide} + */ + public static final int DENSITY_DPI_ANY = 0xfffe; + + /** + * Value for {@link #densityDpi} for resources that are not meant to be scaled. + * {@hide} + */ + public static final int DENSITY_DPI_NONE = 0xffff; + + /** * The target screen density being rendered to, * corresponding to * <a href="{@docRoot}guide/topics/resources/providing-resources.html#DensityQualifier">density</a> @@ -1453,7 +1465,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration } switch (config.densityDpi) { - case 0: + case DENSITY_DPI_UNDEFINED: break; case 120: parts.add("ldpi"); @@ -1476,6 +1488,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration case 640: parts.add("xxxhdpi"); break; + case DENSITY_DPI_ANY: + parts.add("anydpi"); + break; + case DENSITY_DPI_NONE: + parts.add("nodpi"); default: parts.add(config.densityDpi + "dpi"); break; diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 7deb9c8..5d6d998 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -93,6 +93,8 @@ interface IWindowManager int startHeight); void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY, IRemoteCallback startedCallback, boolean scaleUp); + void overridePendingAppTransitionAspectScaledThumb(in Bitmap srcThumb, int startX, + int startY, IRemoteCallback startedCallback, boolean scaleUp); void executeAppTransition(); void setAppStartingWindow(IBinder token, String pkg, int theme, in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index e02aa5e..6146fff 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -66,6 +66,7 @@ static void Shader_setLocalMatrix(JNIEnv* env, jobject o, jlong shaderHandle, jl } else { shader->resetLocalMatrix(); } + shader->setGenerationID(shader->getGenerationID() + 1); } } diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml index 3180e58..c0d9995 100644 --- a/core/res/res/values-sw600dp/dimens.xml +++ b/core/res/res/values-sw600dp/dimens.xml @@ -19,9 +19,9 @@ --> <resources> <!-- The width that is used when creating thumbnails of applications. --> - <dimen name="thumbnail_width">512dp</dimen> + <dimen name="thumbnail_width">360dp</dimen> <!-- The height that is used when creating thumbnails of applications. --> - <dimen name="thumbnail_height">512dp</dimen> + <dimen name="thumbnail_height">360dp</dimen> <!-- The maximum number of action buttons that should be permitted within an action bar/action mode. This will be used to determine how many showAsAction="ifRoom" items can fit. "always" items can override this. --> diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index 9ac6927..042da5b 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -119,7 +119,7 @@ import java.util.Stack; * <dd>Defines path string. This is using exactly same format as "d" attribute * in the SVG's path data. This is defined in the viewport space.</dd> * <dt><code>android:fillColor</code></dt> - * <dd>Defines the color to fill the path (black if not present).</dd> + * <dd>Defines the color to fill the path (none if not present).</dd> * <dt><code>android:strokeColor</code></dt> * <dd>Defines the color to draw the path outline (none if not present).</dd> * <dt><code>android:strokeWidth</code></dt> @@ -862,7 +862,7 @@ public class VectorDrawable extends Drawable { } mRenderPath.addPath(path, mFinalPathMatrix); - if (fullPath.mFillColor != 0) { + if (fullPath.mFillColor != Color.TRANSPARENT) { if (mFillPaint == null) { mFillPaint = new Paint(); mFillPaint.setColorFilter(mColorFilter); @@ -873,7 +873,7 @@ public class VectorDrawable extends Drawable { canvas.drawPath(mRenderPath, mFillPaint); } - if (fullPath.mStrokeColor != 0) { + if (fullPath.mStrokeColor != Color.TRANSPARENT) { if (mStrokePaint == null) { mStrokePaint = new Paint(); mStrokePaint.setColorFilter(mColorFilter); @@ -1237,9 +1237,9 @@ public class VectorDrawable extends Drawable { // Variables below need to be copied (deep copy if applicable) for mutation. private int[] mThemeAttrs; - int mStrokeColor = 0; + int mStrokeColor = Color.TRANSPARENT; float mStrokeWidth = 0; - int mFillColor = Color.BLACK; + int mFillColor = Color.TRANSPARENT; int mFillRule; float mTrimPathStart = 0; float mTrimPathEnd = 1; diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index 1af497c..11568d2 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -954,6 +954,7 @@ struct ResTable_config DENSITY_XHIGH = ACONFIGURATION_DENSITY_XHIGH, DENSITY_XXHIGH = ACONFIGURATION_DENSITY_XXHIGH, DENSITY_XXXHIGH = ACONFIGURATION_DENSITY_XXXHIGH, + DENSITY_ANY = ACONFIGURATION_DENSITY_ANY, DENSITY_NONE = ACONFIGURATION_DENSITY_NONE }; diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 239d682..3f014ef 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -2206,13 +2206,30 @@ bool ResTable_config::isBetterThan(const ResTable_config& o, if (screenType || o.screenType) { if (density != o.density) { - // density is tough. Any density is potentially useful + // Use the system default density (DENSITY_MEDIUM, 160dpi) if none specified. + const int thisDensity = density ? density : int(ResTable_config::DENSITY_MEDIUM); + const int otherDensity = o.density ? o.density : int(ResTable_config::DENSITY_MEDIUM); + + // We always prefer DENSITY_ANY over scaling a density bucket. + if (thisDensity == ResTable_config::DENSITY_ANY) { + return true; + } else if (otherDensity == ResTable_config::DENSITY_ANY) { + return false; + } + + int requestedDensity = requested->density; + if (requested->density == 0 || + requested->density == ResTable_config::DENSITY_ANY) { + requestedDensity = ResTable_config::DENSITY_MEDIUM; + } + + // DENSITY_ANY is now dealt with. We should look to + // pick a density bucket and potentially scale it. + // Any density is potentially useful // because the system will scale it. Scaling down // is generally better than scaling up. - // Default density counts as 160dpi (the system default) - // TODO - remove 160 constants - int h = (density?density:160); - int l = (o.density?o.density:160); + int h = thisDensity; + int l = otherDensity; bool bImBigger = true; if (l > h) { int t = h; @@ -2221,17 +2238,16 @@ bool ResTable_config::isBetterThan(const ResTable_config& o, bImBigger = false; } - int reqValue = (requested->density?requested->density:160); - if (reqValue >= h) { + if (requestedDensity >= h) { // requested value higher than both l and h, give h return bImBigger; } - if (l >= reqValue) { + if (l >= requestedDensity) { // requested value lower than both l and h, give l return !bImBigger; } // saying that scaling down is 2x better than up - if (((2 * l) - reqValue) * h > reqValue * reqValue) { + if (((2 * l) - requestedDensity) * h > requestedDensity * requestedDensity) { return !bImBigger; } else { return bImBigger; @@ -2702,6 +2718,9 @@ String8 ResTable_config::toString() const { case ResTable_config::DENSITY_NONE: res.append("nodpi"); break; + case ResTable_config::DENSITY_ANY: + res.append("anydpi"); + break; default: res.appendFormat("%ddpi", dtohs(density)); break; diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index 4ff6eec..a10c387 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -21,8 +21,9 @@ LOCAL_PATH:= $(call my-dir) testFiles := \ ByteBucketArray_test.cpp \ + Config_test.cpp \ + ConfigLocale_test.cpp \ Idmap_test.cpp \ - ResourceTypes_test.cpp \ ResTable_test.cpp \ Split_test.cpp \ TypeWrappers_test.cpp \ diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp index f00a2d9..4999594 100644 --- a/libs/androidfw/tests/ResourceTypes_test.cpp +++ b/libs/androidfw/tests/ConfigLocale_test.cpp @@ -21,7 +21,7 @@ #include <gtest/gtest.h> namespace android { -TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterLanguage) { +TEST(ConfigLocaleTest, packAndUnpack2LetterLanguage) { ResTable_config config; config.packLanguage("en"); @@ -44,7 +44,7 @@ TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterLanguage) { EXPECT_EQ(0, out[3]); } -TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterRegion) { +TEST(ConfigLocaleTest, packAndUnpack2LetterRegion) { ResTable_config config; config.packRegion("US"); @@ -59,7 +59,7 @@ TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterRegion) { EXPECT_EQ(0, out[3]); } -TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguage) { +TEST(ConfigLocaleTest, packAndUnpack3LetterLanguage) { ResTable_config config; config.packLanguage("eng"); @@ -75,7 +75,7 @@ TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguage) { EXPECT_EQ(0, out[3]); } -TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguageAtOffset16) { +TEST(ConfigLocaleTest, packAndUnpack3LetterLanguageAtOffset16) { ResTable_config config; config.packLanguage("tgp"); @@ -88,8 +88,8 @@ TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguageAtOffset16) { // which is equivalent to: // 1 [0] [1] [2] // 1-01111-00110-10011 - EXPECT_EQ(0xbc, config.language[0]); - EXPECT_EQ(0xd3, config.language[1]); + EXPECT_EQ(char(0xbc), config.language[0]); + EXPECT_EQ(char(0xd3), config.language[1]); char out[4] = { 1, 1, 1, 1}; config.unpackLanguage(out); @@ -99,7 +99,7 @@ TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguageAtOffset16) { EXPECT_EQ(0, out[3]); } -TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterRegion) { +TEST(ConfigLocaleTest, packAndUnpack3LetterRegion) { ResTable_config config; config.packRegion("419"); @@ -131,7 +131,7 @@ TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterRegion) { } } -TEST(ResourceTypesTest, IsMoreSpecificThan) { +TEST(ConfigLocaleTest, IsMoreSpecificThan) { ResTable_config l; ResTable_config r; @@ -170,7 +170,7 @@ TEST(ResourceTypesTest, IsMoreSpecificThan) { EXPECT_TRUE(r.isMoreSpecificThan(l)); } -TEST(ResourceTypesTest, setLocale) { +TEST(ConfigLocaleTest, setLocale) { ResTable_config test; test.setBcp47Locale("en-US"); EXPECT_EQ('e', test.language[0]); diff --git a/libs/androidfw/tests/Config_test.cpp b/libs/androidfw/tests/Config_test.cpp new file mode 100644 index 0000000..ef30df4 --- /dev/null +++ b/libs/androidfw/tests/Config_test.cpp @@ -0,0 +1,103 @@ +/* + * 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. + */ + +#include <androidfw/ResourceTypes.h> +#include <utils/Log.h> +#include <utils/String8.h> +#include <utils/Vector.h> + +#include "TestHelpers.h" +#include <gtest/gtest.h> + +namespace android { + +static ResTable_config selectBest(const ResTable_config& target, + const Vector<ResTable_config>& configs) { + ResTable_config bestConfig; + memset(&bestConfig, 0, sizeof(bestConfig)); + const size_t configCount = configs.size(); + for (size_t i = 0; i < configCount; i++) { + const ResTable_config& thisConfig = configs[i]; + if (!thisConfig.match(target)) { + continue; + } + + if (thisConfig.isBetterThan(bestConfig, &target)) { + bestConfig = thisConfig; + } + } + return bestConfig; +} + +static ResTable_config buildDensityConfig(int density) { + ResTable_config config; + memset(&config, 0, sizeof(config)); + config.density = uint16_t(density); + config.sdkVersion = 4; + return config; +} + +TEST(ConfigTest, shouldSelectBestDensity) { + ResTable_config deviceConfig; + memset(&deviceConfig, 0, sizeof(deviceConfig)); + deviceConfig.density = ResTable_config::DENSITY_XHIGH; + deviceConfig.sdkVersion = 21; + + Vector<ResTable_config> configs; + + ResTable_config expectedBest = buildDensityConfig(ResTable_config::DENSITY_HIGH); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + + expectedBest = buildDensityConfig(ResTable_config::DENSITY_XXHIGH); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + + expectedBest = buildDensityConfig(int(ResTable_config::DENSITY_XXHIGH) - 20); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + + configs.add(buildDensityConfig(int(ResTable_config::DENSITY_HIGH) + 20)); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + + expectedBest = buildDensityConfig(ResTable_config::DENSITY_XHIGH); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + + expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY); + expectedBest.sdkVersion = 21; + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); +} + +TEST(ConfigTest, shouldSelectBestDensityWhenNoneSpecified) { + ResTable_config deviceConfig; + memset(&deviceConfig, 0, sizeof(deviceConfig)); + deviceConfig.sdkVersion = 21; + + Vector<ResTable_config> configs; + configs.add(buildDensityConfig(ResTable_config::DENSITY_HIGH)); + + ResTable_config expectedBest = buildDensityConfig(ResTable_config::DENSITY_MEDIUM); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); + + expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY); + configs.add(expectedBest); + ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs)); +} + +} // namespace android. diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index 75a233a..fe2e5ce 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -3,6 +3,7 @@ #include <ostream> +#include <androidfw/ResourceTypes.h> #include <utils/String8.h> #include <utils/String16.h> @@ -14,4 +15,16 @@ static inline ::std::ostream& operator<<(::std::ostream& out, const android::Str return out << android::String8(str).string(); } +namespace android { + +static inline bool operator==(const android::ResTable_config& a, const android::ResTable_config& b) { + return memcmp(&a, &b, sizeof(a)) == 0; +} + +static inline ::std::ostream& operator<<(::std::ostream& out, const android::ResTable_config& c) { + return out << c.toString().string(); +} + +} // namespace android + #endif // __TEST_HELPERS_H diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 7350082..e9c937c 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -215,11 +215,17 @@ private: if (!paint) return NULL; const SkPaint* paintCopy = mPaintMap.valueFor(paint); - if (paintCopy == NULL || paintCopy->getGenerationID() != paint->getGenerationID()) { - paintCopy = new SkPaint(*paint); + if (paintCopy == NULL + || paintCopy->getGenerationID() != paint->getGenerationID() + // We can't compare shader pointers because that will always + // change as we do partial copying via wrapping. However, if the + // shader changes the paint generationID will have changed and + // so we don't hit this comparison anyway + || !(paint->getShader() && paintCopy->getShader() + && paint->getShader()->getGenerationID() == paintCopy->getShader()->getGenerationID())) { + paintCopy = copyPaint(paint); // replaceValueFor() performs an add if the entry doesn't exist mPaintMap.replaceValueFor(paint, paintCopy); - mDisplayListData->paints.add(paintCopy); } return paintCopy; @@ -228,8 +234,15 @@ private: inline SkPaint* copyPaint(const SkPaint* paint) { if (!paint) return NULL; SkPaint* paintCopy = new SkPaint(*paint); + if (paint->getShader()) { + SkShader* shaderCopy = SkShader::CreateLocalMatrixShader( + paint->getShader(), paint->getShader()->getLocalMatrix()); + paintCopy->setShader(shaderCopy); + paintCopy->setGenerationID(paint->getGenerationID()); + shaderCopy->setGenerationID(paint->getShader()->getGenerationID()); + shaderCopy->unref(); + } mDisplayListData->paints.add(paintCopy); - return paintCopy; } diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index cb20a0b..2178cc7 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -647,7 +647,7 @@ float SpotShadow::projectCasterToOutline(Vector2& outline, if (lightToPolyZ != 0) { // If any caster's vertex is almost above the light, we just keep it as 95% // of the height of the light. - ratioZ = MathUtils::min(polyVertex.z / lightToPolyZ, CASTER_Z_CAP_RATIO); + ratioZ = MathUtils::clamp(polyVertex.z / lightToPolyZ, 0.0f, CASTER_Z_CAP_RATIO); } outline.x = polyVertex.x - ratioZ * (lightCenter.x - polyVertex.x); @@ -669,6 +669,10 @@ float SpotShadow::projectCasterToOutline(Vector2& outline, void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCenter, float lightSize, const Vector3* poly, int polyLength, const Vector3& polyCentroid, VertexBuffer& shadowTriangleStrip) { + if (CC_UNLIKELY(lightCenter.z <= 0)) { + ALOGW("Relative Light Z is not positive. No spot shadow!"); + return; + } OutlineData outlineData[polyLength]; Vector2 outlineCentroid; // Calculate the projected outline for each polygon's vertices from the light center. @@ -787,7 +791,7 @@ void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCente // The ratio can be simulated by using the inverse of maximum of ratioVI for // all (V). distOutline = (outlineData[i].position - outlineCentroid).length(); - if (distOutline == 0) { + if (CC_UNLIKELY(distOutline == 0)) { // If the outline has 0 area, then there is no spot shadow anyway. ALOGW("Outline has 0 area, no spot shadow!"); return; diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h index 00448b8..d89859b 100644 --- a/libs/hwui/utils/MathUtils.h +++ b/libs/hwui/utils/MathUtils.h @@ -76,6 +76,11 @@ public: return a < b ? a : b; } + template<typename T> + static inline T clamp(T a, T minValue, T maxValue) { + return min(max(a, minValue), maxValue); + } + inline static float lerp(float v1, float v2, float t) { return v1 + ((v2 - v1) * t); } diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index 7110db9..d058d64 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -858,6 +858,7 @@ public class TvView extends ViewGroup { mSession.setStreamVolume(mStreamVolume); } } else { + mSessionCallback = null; if (mListener != null) { mListener.onConnectionFailed(mInputId); } @@ -869,6 +870,8 @@ public class TvView extends ViewGroup { if (this != mSessionCallback) { return; } + mOverlayViewCreated = false; + mOverlayViewFrame = null; mSessionCallback = null; mSession = null; if (mListener != null) { diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml index 783aa03..469b776 100644 --- a/packages/SettingsProvider/AndroidManifest.xml +++ b/packages/SettingsProvider/AndroidManifest.xml @@ -16,6 +16,7 @@ android:multiprocess="false" android:exported="true" android:writePermission="android.permission.WRITE_SETTINGS" + android:singleUser="true" android:initOrder="100" /> </application> </manifest> diff --git a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml index 305a82f..4e1d66d 100644 --- a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml +++ b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml @@ -20,9 +20,9 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="normal"> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@android:interpolator/linear" - android:duration="200"/> + android:duration="100"/> </set> diff --git a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml index 863591f..afab78d 100644 --- a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml +++ b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml @@ -24,5 +24,5 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@android:interpolator/linear_out_slow_in" - android:duration="200"/> + android:duration="100"/> </set> diff --git a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml index adcefe0..4e1d66d 100644 --- a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml +++ b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml @@ -23,6 +23,6 @@ <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/fast_out_linear_in" - android:duration="200"/> + android:interpolator="@android:interpolator/linear" + android:duration="100"/> </set> diff --git a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml index 863591f..afab78d 100644 --- a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml +++ b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml @@ -24,5 +24,5 @@ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" android:interpolator="@android:interpolator/linear_out_slow_in" - android:duration="200"/> + android:duration="100"/> </set> diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml index 5253ee0..4cb8498 100644 --- a/packages/SystemUI/res/layout/recents_task_view.xml +++ b/packages/SystemUI/res/layout/recents_task_view.xml @@ -23,45 +23,7 @@ android:id="@+id/task_view_thumbnail" android:layout_width="match_parent" android:layout_height="match_parent" /> - <com.android.systemui.recents.views.TaskViewHeader - android:id="@+id/task_view_bar" - android:layout_width="match_parent" - android:layout_height="@dimen/recents_task_bar_height" - android:layout_gravity="top|center_horizontal"> - <com.android.systemui.recents.views.FixedSizeImageView - android:id="@+id/application_icon" - android:layout_width="@dimen/recents_task_view_application_icon_size" - android:layout_height="@dimen/recents_task_view_application_icon_size" - android:layout_marginStart="8dp" - android:layout_gravity="center_vertical|start" - android:padding="8dp" - android:background="@drawable/recents_button_bg" /> - <TextView - android:id="@+id/activity_description" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical|start" - android:layout_marginStart="64dp" - android:layout_marginEnd="64dp" - android:textSize="16sp" - android:textColor="#ffffffff" - android:text="@string/recents_empty_message" - android:fontFamily="sans-serif-medium" - android:singleLine="true" - android:maxLines="2" - android:ellipsize="marquee" - android:fadingEdge="horizontal" /> - <com.android.systemui.recents.views.FixedSizeImageView - android:id="@+id/dismiss_task" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_marginEnd="4dp" - android:layout_gravity="center_vertical|end" - android:padding="12dp" - android:background="@drawable/recents_button_bg" - android:visibility="invisible" - android:src="@drawable/recents_dismiss_light" /> - </com.android.systemui.recents.views.TaskViewHeader> + <include layout="@layout/recents_task_view_header" /> <FrameLayout android:id="@+id/lock_to_app_fab" android:layout_width="48dp" diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml new file mode 100644 index 0000000..f1d8ad0 --- /dev/null +++ b/packages/SystemUI/res/layout/recents_task_view_header.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<com.android.systemui.recents.views.TaskViewHeader + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/task_view_bar" + android:layout_width="match_parent" + android:layout_height="@dimen/recents_task_bar_height" + android:layout_gravity="top|center_horizontal"> + <com.android.systemui.recents.views.FixedSizeImageView + android:id="@+id/application_icon" + android:layout_width="@dimen/recents_task_view_application_icon_size" + android:layout_height="@dimen/recents_task_view_application_icon_size" + android:layout_marginStart="8dp" + android:layout_gravity="center_vertical|start" + android:padding="8dp" + android:background="@drawable/recents_button_bg" /> + <TextView + android:id="@+id/activity_description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="64dp" + android:layout_marginEnd="64dp" + android:textSize="16sp" + android:textColor="#ffffffff" + android:text="@string/recents_empty_message" + android:fontFamily="sans-serif-medium" + android:singleLine="true" + android:maxLines="2" + android:ellipsize="marquee" + android:fadingEdge="horizontal" /> + <com.android.systemui.recents.views.FixedSizeImageView + android:id="@+id/dismiss_task" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_marginEnd="4dp" + android:layout_gravity="center_vertical|end" + android:padding="12dp" + android:background="@drawable/recents_button_bg" + android:visibility="invisible" + android:src="@drawable/recents_dismiss_light" /> +</com.android.systemui.recents.views.TaskViewHeader>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 52dc000..fa00ebf 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -126,7 +126,7 @@ <integer name="recents_animate_task_bar_enter_duration">275</integer> <!-- The animation delay for animating the first task in. This should roughly be the animation duration of the transition in to recents. --> - <integer name="recents_animate_task_bar_enter_delay">225</integer> + <integer name="recents_animate_task_bar_enter_delay">300</integer> <!-- The min animation duration for animating the task bar out. --> <integer name="recents_animate_task_exit_to_home_duration">225</integer> <!-- The min animation duration for animating the task bar out. --> diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java index 354eb55..8710aa2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java @@ -22,6 +22,7 @@ import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; @@ -29,6 +30,7 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.os.Handler; import android.os.UserHandle; +import android.view.LayoutInflater; import android.view.View; import com.android.systemui.R; import com.android.systemui.RecentsComponent; @@ -41,6 +43,7 @@ import com.android.systemui.recents.model.TaskGrouping; import com.android.systemui.recents.model.TaskStack; import com.android.systemui.recents.views.TaskStackView; import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm; +import com.android.systemui.recents.views.TaskViewHeader; import com.android.systemui.recents.views.TaskViewTransform; import java.util.ArrayList; @@ -86,6 +89,10 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta int mNavBarHeight; int mNavBarWidth; + // Header (for transition) + TaskViewHeader mHeaderBar; + TaskStackView mDummyStackView; + // Variables to keep track of if we need to start recents after binding View mStatusBarView; boolean mTriggeredFromAltTab; @@ -114,6 +121,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta public void onStart() { // Initialize some static datastructures TaskStackViewLayoutAlgorithm.initializeCurve(); + reloadHeaderBarLayout(); } public void onBootCompleted() { @@ -169,7 +177,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta void showRelativeAffiliatedTask(boolean showNextTask) { TaskStack stack = RecentsTaskLoader.getShallowTaskStack(mSystemServicesProxy, - Integer.MAX_VALUE); + Integer.MAX_VALUE, mContext.getResources()); // Return early if there are no tasks if (stack.getTaskCount() == 0) return; @@ -250,6 +258,28 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight); } sLastScreenshot = null; + reloadHeaderBarLayout(); + } + + /** Prepares the header bar layout. */ + void reloadHeaderBarLayout() { + // Inflate the header bar layout so that we can rebind and draw it for the transition + Resources res = mContext.getResources(); + TaskStack stack = new TaskStack(); + mDummyStackView = new TaskStackView(mContext, stack); + TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm(); + Rect taskStackBounds = new Rect(mTaskStackBounds); + taskStackBounds.bottom -= mSystemInsets.bottom; + algo.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds); + Rect taskViewSize = algo.getUntransformedTaskViewSize(); + int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height); + LayoutInflater inflater = LayoutInflater.from(mContext); + mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header, null, + false); + mHeaderBar.measure( + View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY)); + mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight); } /** Gets the top task. */ @@ -361,27 +391,37 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta } } - // If the screenshot fails, then load the first task thumbnail and use that - Bitmap firstThumbnail = mSystemServicesProxy.getTaskThumbnail(topTask.id); - if (firstThumbnail != null) { - // Update the destination rect - Rect toTaskRect = getThumbnailTransitionRect(topTask.id, isTopTaskHome); - if (toTaskRect.width() > 0 && toTaskRect.height() > 0) { - // Create the new thumbnail for the animation down - // XXX: We should find a way to optimize this so we don't need to create a new bitmap - Bitmap thumbnail = Bitmap.createBitmap(toTaskRect.width(), toTaskRect.height(), - Bitmap.Config.ARGB_8888); - int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight()); + // Update the destination rect + Task toTask = new Task(); + TaskViewTransform toTransform = getThumbnailTransitionTransform(topTask.id, isTopTaskHome, + toTask); + if (toTransform != null && toTask.key != null) { + Rect toTaskRect = toTransform.rect; + ActivityInfo info = mSystemServicesProxy.getActivityInfo( + toTask.key.baseIntent.getComponent(), toTask.key.userId); + if (toTask.activityIcon == null) { + toTask.activityIcon = mSystemServicesProxy.getActivityIcon(info, + toTask.key.userId); + } + if (toTask.activityLabel == null) { + toTask.activityLabel = mSystemServicesProxy.getActivityLabel(info); + } + + Bitmap thumbnail = Bitmap.createBitmap(toTaskRect.width(), toTaskRect.height(), + Bitmap.Config.ARGB_8888); + if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) { + thumbnail.eraseColor(0xFFff0000); + } else { Canvas c = new Canvas(thumbnail); - c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size), - new Rect(0, 0, toTaskRect.width(), toTaskRect.height()), null); + c.scale(toTransform.scale, toTransform.scale); + mHeaderBar.rebindToTask(toTask); + mHeaderBar.draw(c); c.setBitmap(null); - // Recycle the old thumbnail - firstThumbnail.recycle(); - mStartAnimationTriggered = false; - return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView, - thumbnail, toTaskRect.left, toTaskRect.top, this); } + + mStartAnimationTriggered = false; + return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mStatusBarView, + thumbnail, toTaskRect.left, toTaskRect.top, this); } // If both the screenshot and thumbnail fails, then just fall back to the default transition @@ -389,21 +429,18 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta } /** Returns the transition rect for the given task id. */ - Rect getThumbnailTransitionRect(int runningTaskId, boolean isTopTaskHome) { + TaskViewTransform getThumbnailTransitionTransform(int runningTaskId, boolean isTopTaskHome, + Task runningTaskOut) { // Get the stack of tasks that we are animating into - TaskStack stack = RecentsTaskLoader.getShallowTaskStack(mSystemServicesProxy, -1); + TaskStack stack = RecentsTaskLoader.getShallowTaskStack(mSystemServicesProxy, -1, + mContext.getResources()); if (stack.getTaskCount() == 0) { - return new Rect(); + return null; } // Get the stack - TaskStackView tsv = new TaskStackView(mContext, stack); - TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm(); - Rect taskStackBounds = new Rect(mTaskStackBounds); - taskStackBounds.bottom -= mSystemInsets.bottom; - tsv.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds, - mTriggeredFromAltTab, isTopTaskHome); - tsv.getScroller().setStackScrollToInitialState(); + mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome); + mDummyStackView.getScroller().setStackScrollToInitialState(); // Find the running task in the TaskStack Task task = null; @@ -415,6 +452,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta Task t = tasks.get(i); if (t.key.id == runningTaskId) { task = t; + runningTaskOut.copyFrom(t); break; } } @@ -425,8 +463,9 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta } // Get the transform for the running task - mTmpTransform = algo.getStackTransform(task, tsv.getScroller().getStackScroll(), mTmpTransform, null); - return new Rect(mTmpTransform.rect); + mTmpTransform = mDummyStackView.getStackAlgorithm().getStackTransform(task, + mDummyStackView.getScroller().getStackScroll(), mTmpTransform, null); + return mTmpTransform; } /** Starts the recents activity */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index 3709c43..6f4cf6b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -27,6 +27,8 @@ public class Constants { public static class App { // Enables the screenshot app->Recents transition public static final boolean EnableScreenshotAppTransition = false; + // Enables debug drawing for the transition thumbnail + public static final boolean EnableTransitionThumbnailDebugMode = false; // Enables the filtering of tasks according to their grouping public static final boolean EnableTaskFiltering = false; // Enables clipping of tasks against each other diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 7fafe7a..a5b845d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -384,7 +384,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Private API calls to make the shadows look better try { - Utilities.setShadowProperty("ambientShadowStrength", String.valueOf(35f)); Utilities.setShadowProperty("ambientRatio", String.valueOf(1.5f)); } catch (IllegalAccessException e) { e.printStackTrace(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 5390daf..b29f378 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -174,6 +174,7 @@ public class SystemServicesProxy { int minNumTasksToQuery = 10; int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks); List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery, + ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS | ActivityManager.RECENT_IGNORE_UNAVAILABLE | ActivityManager.RECENT_INCLUDE_PROFILES | ActivityManager.RECENT_WITH_EXCLUDED, userId); @@ -185,11 +186,6 @@ public class SystemServicesProxy { // NOTE: The order of these checks happens in the expected order of the traversal of the // tasks - // Skip tasks from this Recents package - if (t.baseIntent.getComponent().getPackageName().equals(mRecentsPackage)) { - iter.remove(); - continue; - } // Check the first non-recents task, include this task even if it is marked as excluded // from recents. In other words, only remove excluded tasks if it is not the first task boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) @@ -199,11 +195,6 @@ public class SystemServicesProxy { continue; } isFirstValidTask = false; - // Skip tasks in the home stack - if (isInHomeStack(t.persistentId)) { - iter.remove(); - continue; - } } return tasks.subList(0, Math.min(tasks.size(), numLatestTasks)); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index 0e2f370..b93c126 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -18,6 +18,7 @@ package com.android.systemui.recents.model; import android.app.ActivityManager; import android.content.ComponentCallbacks2; +import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Resources; @@ -31,6 +32,7 @@ import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.misc.SystemServicesProxy; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; @@ -171,9 +173,9 @@ class TaskResourceLoader implements Runnable { // Load the application icon if it is stale or we haven't cached one yet if (cachedIcon == null) { ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(), - t.userId); + t.key.userId); if (info != null) { - cachedIcon = ssp.getActivityIcon(info, t.userId); + cachedIcon = ssp.getActivityIcon(info, t.key.userId); } if (cachedIcon == null) { cachedIcon = mDefaultApplicationIcon; @@ -228,8 +230,10 @@ public class RecentsTaskLoader { static RecentsTaskLoader sInstance; SystemServicesProxy mSystemServicesProxy; + DrawableLruCache mTaskDescriptionIconCache; DrawableLruCache mApplicationIconCache; BitmapLruCache mThumbnailCache; + StringLruCache mActivityLabelCache; TaskResourceLoadQueue mLoadQueue; TaskResourceLoader mLoader; @@ -270,8 +274,10 @@ public class RecentsTaskLoader { mSystemServicesProxy = new SystemServicesProxy(context); mPackageMonitor = new RecentsPackageMonitor(); mLoadQueue = new TaskResourceLoadQueue(); + mTaskDescriptionIconCache = new DrawableLruCache(iconCacheSize); mApplicationIconCache = new DrawableLruCache(iconCacheSize); mThumbnailCache = new BitmapLruCache(thumbnailCacheSize); + mActivityLabelCache = new StringLruCache(100); mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache, mDefaultThumbnail, mDefaultApplicationIcon); } @@ -313,6 +319,7 @@ public class RecentsTaskLoader { RecentsConfiguration config = RecentsConfiguration.getInstance(); Resources res = context.getResources(); LinkedHashSet<Task> tasksToLoad = new LinkedHashSet<Task>(); + ArrayList<Task> tasksToAdd = new ArrayList<Task>(); TaskStack stack = new TaskStack(); SpaceNode root = new SpaceNode(); root.setStack(stack); @@ -325,55 +332,80 @@ public class RecentsTaskLoader { int taskCount = tasks.size(); for (int i = 0; i < taskCount; i++) { ActivityManager.RecentTaskInfo t = tasks.get(i); - ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId); - if (info == null) continue; + Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId, + t.firstActiveTime, t.lastActiveTime); + ComponentName cn = t.baseIntent.getComponent(); + ActivityInfo info = null; ActivityManager.TaskDescription av = t.taskDescription; - String activityLabel = null; + String activityLabel = null; Drawable activityIcon = mDefaultApplicationIcon; int activityColor = config.taskBarViewDefaultBackgroundColor; + boolean loadedActivityIcon = false; if (av != null) { - activityLabel = (av.getLabel() != null ? av.getLabel() : ssp.getActivityLabel(info)); - activityIcon = (av.getIcon() != null) ? + activityLabel = av.getLabel(); + activityIcon = mTaskDescriptionIconCache.getAndInvalidateIfModified(taskKey); + if (activityIcon == null) { + activityIcon = (av.getIcon() != null) ? ssp.getBadgedIcon(new BitmapDrawable(res, av.getIcon()), t.userId) : null; + if (activityIcon != null) { + mTaskDescriptionIconCache.put(taskKey, activityIcon); + } + } if (av.getPrimaryColor() != 0) { activityColor = av.getPrimaryColor(); } - } else { - activityLabel = ssp.getActivityLabel(info); + loadedActivityIcon = (activityIcon != null); + } + // If there is no activity label, then try and read it from the label cache before + // loading it from the system + if (activityLabel == null) { + activityLabel = mActivityLabelCache.getAndInvalidateIfModified(taskKey); + if (activityLabel == null) { + if (info == null) { + info = ssp.getActivityInfo(cn, t.userId); + } + activityLabel = ssp.getActivityLabel(info); + mActivityLabelCache.put(taskKey, activityLabel); + } } // Create a new task - Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, t.affiliatedTaskId, - t.affiliatedTaskColor, activityLabel, activityIcon, activityColor, t.userId, - t.firstActiveTime, t.lastActiveTime, (i == (taskCount - 1)), + Task task = new Task(taskKey, (t.id > -1), t.affiliatedTaskId, t.affiliatedTaskColor, + activityLabel, activityIcon, activityColor, (i == (taskCount - 1)), config.lockToAppEnabled); // Preload the specified number of apps if (i >= (taskCount - preloadCount)) { - // Load the icon from the cache if possible - task.applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(task.key); - if (task.applicationIcon == null) { - // Load the icon from the system - task.applicationIcon = ssp.getActivityIcon(info, task.userId); - if (task.applicationIcon != null) { - mApplicationIconCache.put(task.key, task.applicationIcon); + // Load the icon from the cache if possible (only if we don't have an activity icon) + if (!loadedActivityIcon) { + task.applicationIcon = + mApplicationIconCache.getAndInvalidateIfModified(taskKey); + if (task.applicationIcon == null) { + // Load the icon from the system + if (info == null) { + info = ssp.getActivityInfo(cn, t.userId); + } + task.applicationIcon = ssp.getActivityIcon(info, taskKey.userId); + if (task.applicationIcon != null) { + mApplicationIconCache.put(taskKey, task.applicationIcon); + } + } + if (task.applicationIcon == null) { + // Either the task has changed since the last active time, or it was not + // previously cached, so try and load the task anew. + tasksToLoad.add(task); } - } - if (task.applicationIcon == null) { - // Either the task has changed since the last active time, or it was not - // previously cached, so try and load the task anew. - tasksToLoad.add(task); } // Load the thumbnail from the cache if possible - task.thumbnail = mThumbnailCache.getAndInvalidateIfModified(task.key); + task.thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey); if (task.thumbnail == null) { // Load the thumbnail from the system - task.thumbnail = ssp.getTaskThumbnail(task.key.id); + task.thumbnail = ssp.getTaskThumbnail(taskKey.id); if (task.thumbnail != null) { task.thumbnail.setHasAlpha(false); - mThumbnailCache.put(task.key, task.thumbnail); + mThumbnailCache.put(taskKey, task.thumbnail); } } if (task.thumbnail == null) { @@ -384,10 +416,11 @@ public class RecentsTaskLoader { } // Add the task to the stack - stack.addTask(task); + tasksToAdd.add(task); } // Simulate the groupings that we describe + stack.setTasks(tasksToAdd); stack.createAffiliatedGroupings(config); // Start the task loader and add all the tasks we need to load @@ -401,21 +434,29 @@ public class RecentsTaskLoader { } /** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */ - public static TaskStack getShallowTaskStack(SystemServicesProxy ssp, int numTasks) { + public static TaskStack getShallowTaskStack(SystemServicesProxy ssp, int numTasks, + Resources resources) { RecentsConfiguration config = RecentsConfiguration.getInstance(); List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp, numTasks); + ArrayList<Task> tasksToAdd = new ArrayList<Task>(); TaskStack stack = new TaskStack(); int taskCount = tasks.size(); for (int i = 0; i < taskCount; i++) { ActivityManager.RecentTaskInfo t = tasks.get(i); - ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId); - if (info == null) continue; + ActivityManager.TaskDescription av = t.taskDescription; - stack.addTask(new Task(t.persistentId, true, t.baseIntent, t.affiliatedTaskId, - t.affiliatedTaskColor, null, null, 0, 0, t.firstActiveTime, t.lastActiveTime, - (i == (taskCount - 1)), config.lockToAppEnabled)); + BitmapDrawable icon = null; + if (av.getIcon() != null) { + icon = new BitmapDrawable(resources, av.getIcon()); + } + Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId, + t.firstActiveTime, t.lastActiveTime); + tasksToAdd.add(new Task(taskKey, true, t.affiliatedTaskId, t.affiliatedTaskColor, + av.getLabel(), icon, av.getPrimaryColor(), (i == (taskCount - 1)), + config.lockToAppEnabled)); } + stack.setTasks(tasksToAdd); stack.createAffiliatedGroupings(config); return stack; } @@ -484,18 +525,23 @@ public class RecentsTaskLoader { // We are leaving recents, so trim the data a bit mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2); mApplicationIconCache.trimToSize(mMaxIconCacheSize / 2); + mTaskDescriptionIconCache.trimToSize(mMaxIconCacheSize / 2); break; case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW: case ComponentCallbacks2.TRIM_MEMORY_MODERATE: // We are going to be low on memory mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4); mApplicationIconCache.trimToSize(mMaxIconCacheSize / 4); + mTaskDescriptionIconCache.trimToSize(mMaxIconCacheSize / 4); break; case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL: case ComponentCallbacks2.TRIM_MEMORY_COMPLETE: // We are low on memory, so release everything mThumbnailCache.evictAll(); mApplicationIconCache.evictAll(); + mTaskDescriptionIconCache.evictAll(); + // The cache is small, only clear the label cache when we are critical + mActivityLabelCache.evictAll(); break; default: break; diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/StringLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/StringLruCache.java new file mode 100644 index 0000000..b06c454 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/model/StringLruCache.java @@ -0,0 +1,32 @@ +/* + * 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.recents.model; + +/** + * The String LRU cache. + */ +class StringLruCache extends KeyStoreLruCache<String> { + public StringLruCache(int cacheSize) { + super(cacheSize); + } + + @Override + protected int computeSize(String s) { + // The cache size is measured in number of strings + return 1; + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java index f6c3a7e..977db60 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java @@ -88,7 +88,6 @@ public class Task { public boolean isActive; public boolean lockToThisTask; public boolean lockToTaskEnabled; - public int userId; TaskCallbacks mCb; @@ -96,13 +95,12 @@ public class Task { // Only used by RecentsService for task rect calculations. } - public Task(int id, boolean isActive, Intent intent, int taskAffiliation, int taskAffiliationColor, - String activityTitle, Drawable activityIcon, int colorPrimary, int userId, - long firstActiveTime, long lastActiveTime, boolean lockToThisTask, - boolean lockToTaskEnabled) { - boolean isInAffiliationGroup = (taskAffiliation != id); + public Task(TaskKey key, boolean isActive, int taskAffiliation, int taskAffiliationColor, + String activityTitle, Drawable activityIcon, int colorPrimary, + boolean lockToThisTask, boolean lockToTaskEnabled) { + boolean isInAffiliationGroup = (taskAffiliation != key.id); boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0); - this.key = new TaskKey(id, intent, userId, firstActiveTime, lastActiveTime); + this.key = key; this.taskAffiliation = taskAffiliation; this.taskAffiliationColor = taskAffiliationColor; this.activityLabel = activityTitle; @@ -113,7 +111,20 @@ public class Task { this.isActive = isActive; this.lockToThisTask = lockToTaskEnabled && lockToThisTask; this.lockToTaskEnabled = lockToTaskEnabled; - this.userId = userId; + } + + /** Copies the other task. */ + public void copyFrom(Task o) { + this.key = o.key; + this.taskAffiliation = o.taskAffiliation; + this.taskAffiliationColor = o.taskAffiliationColor; + this.activityLabel = o.activityLabel; + this.activityIcon = o.activityIcon; + this.colorPrimary = o.colorPrimary; + this.useLightOnPrimaryColor = o.useLightOnPrimaryColor; + this.isActive = o.isActive; + this.lockToThisTask = o.lockToThisTask; + this.lockToTaskEnabled = o.lockToTaskEnabled; } /** Set the callbacks */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index 98bf151..1e47b50 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -92,7 +92,10 @@ class FilteredTaskList { /** Returns the index of this task in the list of filtered tasks */ int indexOf(Task t) { - return mTaskIndices.get(t.key); + if (mTaskIndices.containsKey(t.key)) { + return mTaskIndices.get(t.key); + } + return -1; } /** Returns the size of the list of filtered tasks */ 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 07a7e74..1dd484b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -408,14 +408,26 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV int thumbnailHeight = transform.rect.height(); if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 && task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) { - // Resize the thumbnail to the size of the view that we are animating from - Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, - Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(b); - c.drawBitmap(task.thumbnail, - new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()), - new Rect(0, 0, thumbnailWidth, thumbnailHeight), null); - c.setBitmap(null); + Bitmap b; + if (tv != null) { + // Disable any focused state before we draw the header + if (tv.isFocusedTask()) { + tv.unsetFocusedTask(); + } + + b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888); + if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) { + b.eraseColor(0xFFff0000); + } else { + Canvas c = new Canvas(b); + c.scale(tv.getScaleX(), tv.getScaleY()); + tv.mHeaderView.draw(c); + c.setBitmap(null); + } + } else { + // Notify the system to skip the thumbnail layer by using an ALPHA_8 bitmap + b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ALPHA_8); + } ActivityOptions.OnAnimationStartedListener animStartedListener = null; if (lockToTask) { animStartedListener = new ActivityOptions.OnAnimationStartedListener() { @@ -434,7 +446,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } }; } - opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView, + opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView, b, offsetX, offsetY, animStartedListener); } @@ -472,7 +484,15 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV if (tv == null) { post(launchRunnable); } else { - stackView.startLaunchTaskAnimation(tv, launchRunnable); + if (!task.group.isFrontMostTask(task)) { + // For affiliated tasks that are behind other tasks, we must animate the front cards + // out of view before starting the task transition + stackView.startLaunchTaskAnimation(tv, launchRunnable); + } else { + // Otherwise, we can start the task transition immediately + stackView.startLaunchTaskAnimation(tv, null); + postDelayed(launchRunnable, 17); + } } } @@ -485,7 +505,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV intent.setComponent(intent.resolveActivity(getContext().getPackageManager())); TaskStackBuilder.create(getContext()) .addNextIntentWithParentStack(intent).startActivities(null, - new UserHandle(t.userId)); + new UserHandle(t.key.userId)); } @Override 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 aee558f..861011f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -460,7 +460,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } /** - * This is called with the full window width and height to allow stack view children to + * This is ONLY used from AlternateRecentsComponent to update the dummy stack view for purposes + * of getting the task rect to animate to. + */ + public void updateMinMaxScrollForStack(TaskStack stack, boolean launchedWithAltTab, + boolean launchedFromHome) { + mStack = stack; + updateMinMaxScroll(false, launchedWithAltTab, launchedFromHome); + } + + /** + * This is called with the full window width and height to allow stack view children to * perform the full screen transition down. */ @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java index f0bdfa2..31fc701 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java @@ -33,7 +33,7 @@ import java.util.HashMap; public class TaskStackViewLayoutAlgorithm { // These are all going to change - static final float StackPeekMinScale = 0.825f; // The min scale of the last card in the peek area + static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area RecentsConfiguration mConfig; @@ -157,7 +157,7 @@ public class TaskStackViewLayoutAlgorithm { public TaskViewTransform getStackTransform(Task task, float stackScroll, TaskViewTransform transformOut, TaskViewTransform prevTransform) { // Return early if we have an invalid index - if (task == null) { + if (task == null || !mTaskProgressMap.containsKey(task.key)) { transformOut.reset(); return transformOut; } @@ -200,6 +200,15 @@ public class TaskStackViewLayoutAlgorithm { } /** + * Returns the untransformed task view size. + */ + public Rect getUntransformedTaskViewSize() { + Rect tvSize = new Rect(mTaskRect); + tvSize.offsetTo(0, 0); + return tvSize; + } + + /** * Returns the scroll to such task top = 1f; */ float getStackScrollForTask(Task t) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index e514c90..dfbcce1 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -235,7 +235,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, int initialDim = getDim(); if (mConfig.launchedFromAppWithScreenshot) { if (isTaskViewLaunchTargetTask) { - mHeaderView.prepareEnterRecentsAnimation(); // Hide the footer during the transition in, and animate it out afterwards? if (mFooterView != null) { mFooterView.animateFooterVisibility(false, 0); @@ -246,8 +245,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, } else if (mConfig.launchedFromAppWithThumbnail) { if (isTaskViewLaunchTargetTask) { - // Hide the front most task bar view so we can animate it in - mHeaderView.prepareEnterRecentsAnimation(); // Hide the action button if it exists mActionButtonView.setAlpha(0f); // Set the dim to 0 so we can animate it in @@ -306,7 +303,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, mViewBounds.animateClipBottom(getMeasuredHeight() - (windowInsetTop + size), duration); } // Animate the task bar of the first task view - mHeaderView.startEnterRecentsAnimation(0, null); animate() .scaleX(taskScale) .scaleY(taskScale) @@ -352,9 +348,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, } else if (mConfig.launchedFromAppWithThumbnail) { if (mTask.isLaunchTarget) { - // Animate the task bar of the first task view - mHeaderView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, - mThumbnailView.enableTaskBarClipAsRunnable(mHeaderView)); + // Enable the task bar clip + mThumbnailView.enableTaskBarClip(mHeaderView); // Animate the dim/overlay if (Constants.DebugFlags.App.EnableThumbnailAlphaOnFrontmost) { // Animate the thumbnail alpha before the dim animation (to prevent updating the @@ -475,14 +470,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, } /** Animates this task view as it exits recents */ - void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask, + void startLaunchTaskAnimation(final Runnable postAnimRunnable, boolean isLaunchingTask, boolean occludesLaunchTarget) { if (isLaunchingTask) { - // Disable the thumbnail clip and animate the bar out for the window animation out - mHeaderView.startLaunchTaskAnimation(mThumbnailView.disableTaskBarClipAsRunnable(), r, - mIsFocused); + // Disable the thumbnail clip + mThumbnailView.disableTaskBarClip(); // Animate the thumbnail alpha back into full opacity for the window animation out - mThumbnailView.startLaunchTaskAnimation(); + mThumbnailView.startLaunchTaskAnimation(postAnimRunnable); // Animate the dim if (mDim > 0) { @@ -493,7 +487,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, } // Animate the action button away - mActionButtonView.animate().alpha(0f) + float toScale = 0.9f; + mActionButtonView.animate() + .alpha(0f) + .scaleX(toScale) + .scaleY(toScale) .setStartDelay(0) .setDuration(mConfig.taskBarExitAnimDuration) .setInterpolator(mConfig.fastOutLinearInInterpolator) @@ -706,22 +704,30 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, } /** + * Unsets the focused task explicitly. + */ + void unsetFocusedTask() { + mIsFocused = false; + if (mFocusAnimationsEnabled) { + // Un-focus the header bar + mHeaderView.onTaskViewFocusChanged(false); + } + + // Update the thumbnail alpha with the focus + mThumbnailView.onFocusChanged(false); + // Call the callback + mCb.onTaskViewFocusChanged(this, false); + invalidate(); + } + + /** * Updates the explicitly focused state when the view focus changes. */ @Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); if (!gainFocus) { - mIsFocused = false; - if (mFocusAnimationsEnabled) { - // Un-focus the header bar - mHeaderView.onTaskViewFocusChanged(false); - } - // Update the thumbnail alpha with the focus - mThumbnailView.onFocusChanged(false); - // Call the callback - mCb.onTaskViewFocusChanged(this, false); - invalidate(); + unsetFocusedTask(); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index d39f64e..1743433 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -49,7 +49,7 @@ import com.android.systemui.recents.model.Task; /* The task bar view */ -class TaskViewHeader extends FrameLayout { +public class TaskViewHeader extends FrameLayout { RecentsConfiguration mConfig; @@ -156,8 +156,11 @@ class TaskViewHeader extends FrameLayout { // Draw the highlight at the top edge (but put the bottom edge just out of view) float offset = (float) Math.ceil(mConfig.taskViewHighlightPx / 2f); float radius = mConfig.taskViewRoundedCornerRadiusPx; + int count = canvas.save(Canvas.CLIP_SAVE_FLAG); + canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight()); canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset, getMeasuredHeight() + radius, radius, radius, sHighlightPaint); + canvas.restoreToCount(count); } } @@ -178,7 +181,7 @@ class TaskViewHeader extends FrameLayout { } /** Binds the bar view to the task */ - void rebindToTask(Task t) { + public void rebindToTask(Task t) { // If an activity icon is defined, then we use that as the primary icon to show in the bar, // otherwise, we fall back to the application icon if (t.activityIcon != null) { @@ -212,51 +215,6 @@ class TaskViewHeader extends FrameLayout { mApplicationIcon.setImageDrawable(null); } - /** Prepares this task view for the enter-recents animations. This is called earlier in the - * first layout because the actual animation into recents may take a long time. */ - void prepareEnterRecentsAnimation() { - setVisibility(View.INVISIBLE); - } - - /** Animates this task bar as it enters recents */ - void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) { - // Animate the task bar of the first task view - setVisibility(View.VISIBLE); - setAlpha(0f); - animate() - .alpha(1f) - .setStartDelay(delay) - .setInterpolator(mConfig.linearOutSlowInInterpolator) - .setDuration(mConfig.taskBarEnterAnimDuration) - .withEndAction(postAnimRunnable) - .withLayer() - .start(); - } - - /** Animates this task bar as it exits recents */ - void startLaunchTaskAnimation(Runnable preAnimRunnable, final Runnable postAnimRunnable, - boolean isFocused) { - if (isFocused) { - onTaskViewFocusChanged(false); - } - - // Animate the task bar out of the first task view - animate() - .alpha(0f) - .setStartDelay(0) - .setInterpolator(mConfig.linearOutSlowInInterpolator) - .setDuration(mConfig.taskBarExitAnimDuration) - .withStartAction(preAnimRunnable) - .withEndAction(new Runnable() { - @Override - public void run() { - post(postAnimRunnable); - } - }) - .withLayer() - .start(); - } - /** Animates this task bar dismiss button when launching a task. */ void startLaunchTaskDismissAnimation() { if (mDismissButton.getVisibility() == View.VISIBLE) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index f223bf3..fe36987 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -64,25 +64,10 @@ public class TaskViewThumbnail extends FixedSizeImageView { setClipBounds(mClipRect); } - /** Convenience method to enable task bar clipping as a runnable. */ - Runnable enableTaskBarClipAsRunnable(final View taskBar) { - return new Runnable() { - @Override - public void run() { - enableTaskBarClip(taskBar); - } - }; - } - /** Disables the task bar clipping. */ - Runnable disableTaskBarClipAsRunnable() { - return new Runnable() { - @Override - public void run() { - mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); - setClipBounds(mClipRect); - } - }; + void disableTaskBarClip() { + mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + setClipBounds(mClipRect); } /** Binds the thumbnail view to the screenshot. */ @@ -140,8 +125,8 @@ public class TaskViewThumbnail extends FixedSizeImageView { } /** Animates this task thumbnail as it exits recents */ - void startLaunchTaskAnimation() { - startFadeAnimation(1f, 0, mConfig.taskBarExitAnimDuration, null); + void startLaunchTaskAnimation(Runnable postAnimRunnable) { + startFadeAnimation(1f, 0, mConfig.taskBarExitAnimDuration, postAnimRunnable); } /** Animates the thumbnail alpha. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 0ddef48..8b50a36 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -21,7 +21,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.TimeInterpolator; import android.app.ActivityManager; import android.app.ActivityManagerNative; -import android.app.ActivityThread; import android.app.Notification; import android.app.PendingIntent; import android.app.TaskStackBuilder; @@ -32,16 +31,12 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.database.ContentObserver; -import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Handler; @@ -579,7 +574,7 @@ public abstract class BaseStatusBar extends SystemUI implements } - protected void applyLegacyRowBackground(StatusBarNotification sbn, + protected void applyColorsAndBackgrounds(StatusBarNotification sbn, NotificationData.Entry entry) { int version = 0; try { @@ -602,6 +597,14 @@ public abstract class BaseStatusBar extends SystemUI implements entry.row.setTintColor(color); } } + + if (entry.icon != null) { + if (version >= Build.VERSION_CODES.L) { + entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white)); + } else { + entry.icon.setColorFilter(null); + } + } } public boolean isMediaNotification(NotificationData.Entry entry) { @@ -1250,7 +1253,7 @@ public abstract class BaseStatusBar extends SystemUI implements entry.expandedPublic = publicViewLocal; entry.setBigContentView(bigContentViewLocal); - applyLegacyRowBackground(sbn, entry); + applyColorsAndBackgrounds(sbn, entry); // Restore previous flags. if (hasUserChangedExpansion) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java index c5f8303..b633453 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java @@ -37,7 +37,7 @@ public class NavigationBarViewTaskSwitchHelper extends GestureDetector.SimpleOnG public NavigationBarViewTaskSwitchHelper(Context context) { ViewConfiguration configuration = ViewConfiguration.get(context); - mScrollTouchSlop = configuration.getScaledTouchSlop(); + mScrollTouchSlop = 4 * configuration.getScaledTouchSlop(); mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity(); mTaskSwitcherDetector = new GestureDetector(context, this); } 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 e1beb08..387abc3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -58,7 +58,7 @@ public class NotificationPanelView extends PanelView implements private static final int CAP_HEIGHT = 1456; private static final int FONT_HEIGHT = 2163; - private static final float HEADER_RUBBERBAND_FACTOR = 2.15f; + private static final float HEADER_RUBBERBAND_FACTOR = 2.05f; private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f; private KeyguardAffordanceHelper mAfforanceHelper; @@ -1665,7 +1665,7 @@ public class NotificationPanelView extends PanelView implements @Override protected boolean fullyExpandedClearAllVisible() { return mNotificationStackScroller.isDismissViewNotGone() - && mNotificationStackScroller.isScrolledToBottom(); + && mNotificationStackScroller.isScrolledToBottom() && !mTwoFingerQsExpand; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index f427ec4..3338f6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -298,9 +298,7 @@ public class StatusBarKeyguardViewManager { */ public boolean onBackPressed() { if (mBouncer.isShowing()) { - mBouncer.hide(false /* destroyView */); - mPhoneStatusBar.showKeyguard(); - updateStates(); + reset(); return true; } return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 68a02cf..82efd1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -2150,7 +2150,15 @@ public class NotificationStackScrollLayout extends ViewGroup } public int getDismissViewHeight() { - return mDismissView.getHeight() + mPaddingBetweenElementsNormal; + int height = mDismissView.getHeight() + mPaddingBetweenElementsNormal; + + // Hack: Accommodate for additional distance when we only have one notification and the + // dismiss all button. + if (getNotGoneChildCount() == 2 && getLastChildNotGone() == mDismissView + && getFirstChildNotGone() instanceof ActivatableNotificationView) { + height += mCollapseSecondCardPadding; + } + return height; } public float getBottomMostNotificationBottom() { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index c57563b..20206b9 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -2978,7 +2978,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; boolean navAllowedHidden = immersive || immersiveSticky; navTranslucent &= !immersiveSticky; // transient trumps translucent - navTranslucent &= areTranslucentBarsAllowed(); + boolean isKeyguardShowing = isStatusBarKeyguard() && !mHideLockScreen; + if (!isKeyguardShowing) { + navTranslucent &= areTranslucentBarsAllowed(); + } // When the navigation bar isn't visible, we put up a fake // input window to catch all touch events. This way we can @@ -3102,7 +3105,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0; boolean statusBarTranslucent = (sysui & (View.STATUS_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT)) != 0; - statusBarTranslucent &= areTranslucentBarsAllowed(); + if (!isKeyguardShowing) { + statusBarTranslucent &= areTranslucentBarsAllowed(); + } // If the status bar is hidden, we don't want to cause // windows behind it to scroll. @@ -5611,7 +5616,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { vis = (vis & ~flags) | (oldVis & flags); } - if (!areTranslucentBarsAllowed()) { + if (!areTranslucentBarsAllowed() && transWin != mStatusBar) { vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 62a212b..1acbec7 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6615,6 +6615,10 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } + /** + * @param uri This uri must NOT contain an embedded userId. + * @param userId The userId in which the uri is to be resolved. + */ @Override public int checkUriPermission(Uri uri, int pid, int uid, final int modeFlags, int userId) { @@ -6767,6 +6771,10 @@ public final class ActivityManagerService extends ActivityManagerNative return targetUid; } + /** + * @param uri This uri must NOT contain an embedded userId. + * @param userId The userId in which the uri is to be resolved. + */ @Override public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, final int modeFlags, int userId) { @@ -6951,6 +6959,10 @@ public final class ActivityManagerService extends ActivityManagerNative grantUriPermissionUncheckedFromIntentLocked(needed, owner); } + /** + * @param uri This uri must NOT contain an embedded userId. + * @param userId The userId in which the uri is to be resolved. + */ @Override public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri, final int modeFlags, int userId) { @@ -7053,6 +7065,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } + /** + * @param uri This uri must NOT contain an embedded userId. + * @param userId The userId in which the uri is to be resolved. + */ @Override public void revokeUriPermission(IApplicationThread caller, Uri uri, final int modeFlags, int userId) { @@ -7151,9 +7167,16 @@ public final class ActivityManagerService extends ActivityManagerNative } } + /** + * @param uri This uri must NOT contain an embedded userId. + * @param sourceUserId The userId in which the uri is to be resolved. + * @param targetUserId The userId of the app that receives the grant. + */ @Override public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri, final int modeFlags, int sourceUserId, int targetUserId) { + targetUserId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + targetUserId, false, ALLOW_FULL_ONLY, "grantUriPermissionFromOwner", null); synchronized(this) { UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token); if (owner == null) { @@ -7178,6 +7201,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } + /** + * @param uri This uri must NOT contain an embedded userId. + * @param userId The userId in which the uri is to be resolved. + */ @Override public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) { synchronized(this) { @@ -7318,6 +7345,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } + /** + * @param uri This uri must NOT contain an embedded userId. + * @param userId The userId in which the uri is to be resolved. + */ @Override public void takePersistableUriPermission(Uri uri, final int modeFlags, int userId) { enforceNotIsolatedCaller("takePersistableUriPermission"); @@ -7360,6 +7391,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } + /** + * @param uri This uri must NOT contain an embedded userId. + * @param userId The userId in which the uri is to be resolved. + */ @Override public void releasePersistableUriPermission(Uri uri, final int modeFlags, int userId) { enforceNotIsolatedCaller("releasePersistableUriPermission"); @@ -7711,6 +7746,12 @@ public final class ActivityManagerService extends ActivityManagerNative continue; } } + if ((flags & ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS) != 0) { + if (tr.stack != null && tr.stack.isHomeStack()) { + if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, home stack task: " + tr); + continue; + } + } if (tr.autoRemoveRecents && tr.getTopActivity() == null) { // Don't include auto remove tasks that are finished or finishing. if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, auto-remove without activity: " diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index fcbe71e..39b6375 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -723,6 +723,22 @@ final class ActivityRecord { + pendingOptions.getThumbnail().getHeight())); } break; + case ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP: + case ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: + service.mWindowManager.overridePendingAppTransitionAspectScaledThumb( + pendingOptions.getThumbnail(), + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getOnAnimationStartListener(), + (animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP)); + if (intent.getSourceBounds() == null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + + pendingOptions.getThumbnail().getWidth(), + pendingOptions.getStartY() + + pendingOptions.getThumbnail().getHeight())); + } + break; default: Slog.e(TAG, "applyOptionsLocked: Unknown animationType=" + animationType); break; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 8aec392..bd8501c 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2430,7 +2430,6 @@ public final class ActivityStackSupervisor implements DisplayListener { r.userId, r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind); } - mWindowManager.addTask(taskId, stackId, false); } resumeHomeStackTask(HOME_ACTIVITY_TYPE, null); } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 949019e..9e169d9 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -152,7 +152,10 @@ public class SyncManager { */ private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10; - private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000; + /** + * How long to wait before considering an active sync to have timed-out, and cancelling it. + */ + private static final long ACTIVE_SYNC_TIMEOUT_MILLIS = 30L * 60 * 1000; // 30 mins. private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/"; private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; @@ -851,6 +854,31 @@ public class SyncManager { mSyncHandler.sendMessage(msg); } + /** + * Post a delayed message to the handler that will result in the cancellation of the provided + * running sync's context. + */ + private void postSyncExpiryMessage(ActiveSyncContext activeSyncContext) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "posting MESSAGE_SYNC_EXPIRED in " + + (ACTIVE_SYNC_TIMEOUT_MILLIS/1000) + "s"); + } + Message msg = mSyncHandler.obtainMessage(); + msg.what = SyncHandler.MESSAGE_SYNC_EXPIRED; + msg.obj = activeSyncContext; + mSyncHandler.sendMessageDelayed(msg, ACTIVE_SYNC_TIMEOUT_MILLIS); + } + + /** + * Remove any time-outs previously posted for the provided active sync. + */ + private void removeSyncExpiryMessage(ActiveSyncContext activeSyncContext) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "removing all MESSAGE_SYNC_EXPIRED for " + activeSyncContext.toString()); + } + mSyncHandler.removeMessages(SyncHandler.MESSAGE_SYNC_EXPIRED, activeSyncContext); + } + class SyncHandlerMessagePayload { public final ActiveSyncContext activeSyncContext; public final SyncResult syncResult; @@ -1902,6 +1930,8 @@ public class SyncManager { private static final int MESSAGE_SERVICE_CONNECTED = 4; private static final int MESSAGE_SERVICE_DISCONNECTED = 5; private static final int MESSAGE_CANCEL = 6; + /** Posted delayed in order to expire syncs that are long-running. */ + private static final int MESSAGE_SYNC_EXPIRED = 7; public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo(); private Long mAlarmScheduleTime = null; @@ -2000,10 +2030,21 @@ public class SyncManager { // to also take into account the periodic syncs. earliestFuturePollTime = scheduleReadyPeriodicSyncs(); switch (msg.what) { + case SyncHandler.MESSAGE_SYNC_EXPIRED: + ActiveSyncContext expiredContext = (ActiveSyncContext) msg.obj; + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_EXPIRED: expiring " + + expiredContext); + } + cancelActiveSync(expiredContext.mSyncOperation.target, + expiredContext.mSyncOperation.extras); + nextPendingSyncTime = maybeStartNextSyncLocked(); + break; + case SyncHandler.MESSAGE_CANCEL: { SyncStorageEngine.EndPoint payload = (SyncStorageEngine.EndPoint) msg.obj; Bundle extras = msg.peekData(); - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: " + payload + " bundle: " + extras); } @@ -2653,6 +2694,13 @@ public class SyncManager { new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid); activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext); mActiveSyncContexts.add(activeSyncContext); + if (!activeSyncContext.mSyncOperation.isInitialization() && + !activeSyncContext.mSyncOperation.isExpedited() && + !activeSyncContext.mSyncOperation.isManual() && + !activeSyncContext.mSyncOperation.isIgnoreSettings()) { + // Post message to expire this sync if it runs for too long. + postSyncExpiryMessage(activeSyncContext); + } if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext); } @@ -2757,9 +2805,7 @@ public class SyncManager { } else { Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult); // the operation failed so increase the backoff time - if (!syncResult.syncAlreadyInProgress) { - increaseBackoffSetting(syncOperation); - } + increaseBackoffSetting(syncOperation); // reschedule the sync if so indicated by the syncResult maybeRescheduleSync(syncResult, syncOperation); historyMessage = ContentResolver.syncErrorToString( @@ -2768,7 +2814,6 @@ public class SyncManager { downstreamActivity = 0; upstreamActivity = 0; } - setDelayUntilTime(syncOperation, syncResult.delayUntil); } else { if (isLoggable) { @@ -2794,7 +2839,6 @@ public class SyncManager { stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage, upstreamActivity, downstreamActivity, elapsedTime); - // Check for full-resync and schedule it after closing off the last sync. if (info.target_provider) { if (syncResult != null && syncResult.tooManyDeletions) { @@ -2833,6 +2877,7 @@ public class SyncManager { mActiveSyncContexts.remove(activeSyncContext); mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo, activeSyncContext.mSyncOperation.target.userId); + removeSyncExpiryMessage(activeSyncContext); } /** diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java index 9a4abce..35827cc 100644 --- a/services/core/java/com/android/server/content/SyncOperation.java +++ b/services/core/java/com/android/server/content/SyncOperation.java @@ -273,6 +273,14 @@ public class SyncOperation implements Comparable { return extras.getBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, false); } + public boolean isManual() { + return extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); + } + + public boolean isIgnoreSettings() { + return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false); + } + /** Changed in V3. */ public static String toKey(SyncStorageEngine.EndPoint info, Bundle extras) { StringBuilder sb = new StringBuilder(); diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 4dc76de..aabb8f7 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -19,7 +19,6 @@ package com.android.server.wm; import android.content.Context; import android.content.res.Configuration; import android.graphics.Bitmap; -import android.graphics.Point; import android.graphics.Rect; import android.os.Debug; import android.os.Handler; @@ -111,11 +110,14 @@ public class AppTransition implements Dump { * The new window will show briefly and then be gone. */ public static final int TRANSIT_TASK_OPEN_BEHIND = 16; + /** Fraction of animation at which the recents thumbnail stays completely transparent */ + private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.7f; /** Fraction of animation at which the recents thumbnail becomes completely transparent */ - private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.25f; + private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.3f; private static final int DEFAULT_APP_TRANSITION_DURATION = 250; - private static final int THUMBNAIL_APP_TRANSITION_DURATION = 275; + private static final int THUMBNAIL_APP_TRANSITION_DURATION = 300; + private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 325; private final Context mContext; private final Handler mH; @@ -127,6 +129,8 @@ public class AppTransition implements Dump { private static final int NEXT_TRANSIT_TYPE_SCALE_UP = 2; private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP = 3; private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4; + private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP = 5; + private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN = 6; private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE; // These are the possible states for the enter/exit activities during a thumbnail transition @@ -146,6 +150,7 @@ public class AppTransition implements Dump { private int mNextAppTransitionStartY; private int mNextAppTransitionStartWidth; private int mNextAppTransitionStartHeight; + private Rect mNextAppTransitionInsets = new Rect(); private Rect mTmpFromClipRect = new Rect(); private Rect mTmpToClipRect = new Rect(); @@ -158,7 +163,8 @@ public class AppTransition implements Dump { private final int mConfigShortAnimTime; private final Interpolator mDecelerateInterpolator; - private final Interpolator mThumbnailFadeoutInterpolator; + private final Interpolator mThumbnailFadeInInterpolator; + private final Interpolator mThumbnailFadeOutInterpolator; private final Interpolator mThumbnailFastOutSlowInInterpolator; private int mCurrentUserId = 0; @@ -172,14 +178,25 @@ public class AppTransition implements Dump { com.android.internal.R.interpolator.decelerate_cubic); mThumbnailFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.fast_out_slow_in); - mThumbnailFadeoutInterpolator = new Interpolator() { + mThumbnailFadeInInterpolator = new Interpolator() { + @Override + public float getInterpolation(float input) { + // Linear response for first fraction, then complete after that. + if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) { + return 0f; + } + return (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) / + (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION); + } + }; + mThumbnailFadeOutInterpolator = new Interpolator() { @Override public float getInterpolation(float input) { // Linear response for first fraction, then complete after that. if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) { return input / RECENTS_THUMBNAIL_FADEOUT_FRACTION; } - return 1.0f; + return 1f; } }; } @@ -233,9 +250,23 @@ public class AppTransition implements Dump { return mNextAppTransitionThumbnail; } - void getStartingPoint(Point outPoint) { - outPoint.x = mNextAppTransitionStartX; - outPoint.y = mNextAppTransitionStartY; + /** Returns whether the next thumbnail transition is aspect scaled up. */ + boolean isNextThumbnailTransitionAspectScaled() { + return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP || + mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; + } + + /** Returns whether the next thumbnail transition is scaling up. */ + boolean isNextThumbnailTransitionScaleUp() { + return mNextAppTransitionScaleUp; + } + + int getStartingX() { + return mNextAppTransitionStartX; + } + + int getStartingY() { + return mNextAppTransitionStartY; } void prepare() { @@ -372,7 +403,7 @@ public class AppTransition implements Dump { scale.setInterpolator(mDecelerateInterpolator); Animation alpha = new AlphaAnimation(0, 1); - alpha.setInterpolator(mThumbnailFadeoutInterpolator); + alpha.setInterpolator(mThumbnailFadeOutInterpolator); AnimationSet set = new AnimationSet(false); set.addAnimation(scale); @@ -417,7 +448,9 @@ public class AppTransition implements Dump { */ Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight, int duration, Interpolator interpolator) { - a.setDuration(duration); + if (duration > 0) { + a.setDuration(duration); + } a.setFillAfter(true); a.setInterpolator(interpolator); a.initialize(appWidth, appHeight, appWidth, appHeight); @@ -468,50 +501,73 @@ public class AppTransition implements Dump { * This animation runs for the thumbnail that gets cross faded with the enter/exit activity * when a thumbnail is specified with the activity options. */ - Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) { + Animation createThumbnailAspectScaleAnimationLocked(int appWidth, int appHeight, + int deviceWidth, int transit) { Animation a; final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; + float scaleW = deviceWidth / thumbWidth; + float unscaledWidth = deviceWidth; + float unscaledHeight = thumbHeight * scaleW; + float unscaledStartY = mNextAppTransitionStartY - (unscaledHeight - thumbHeight) / 2f; if (mNextAppTransitionScaleUp) { - // Animation for the thumbnail zooming from its initial size to the full screen - float scaleW = appWidth / thumbWidth; - float scaleH = appHeight / thumbHeight; - Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, - computePivot(mNextAppTransitionStartX, 1 / scaleW), - computePivot(mNextAppTransitionStartY, 1 / scaleH)); - scale.setInterpolator(mDecelerateInterpolator); - + // Animation up from the thumbnail to the full screen + Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, + mNextAppTransitionStartX + (thumbWidth / 2f), + mNextAppTransitionStartY + (thumbHeight / 2f)); + scale.setInterpolator(mThumbnailFastOutSlowInInterpolator); + scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); Animation alpha = new AlphaAnimation(1, 0); - alpha.setInterpolator(mThumbnailFadeoutInterpolator); + alpha.setInterpolator(mThumbnailFadeOutInterpolator); + alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION); + Animation translate = new TranslateAnimation(0, 0, 0, -unscaledStartY + + mNextAppTransitionInsets.top); + translate.setInterpolator(mThumbnailFastOutSlowInInterpolator); + translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); // This AnimationSet uses the Interpolators assigned above. AnimationSet set = new AnimationSet(false); set.addAnimation(scale); set.addAnimation(alpha); + set.addAnimation(translate); a = set; } else { - // Animation for the thumbnail zooming down from the full screen to its final size - float scaleW = appWidth / thumbWidth; - float scaleH = appHeight / thumbHeight; - a = new ScaleAnimation(scaleW, 1, scaleH, 1, - computePivot(mNextAppTransitionStartX, 1 / scaleW), - computePivot(mNextAppTransitionStartY, 1 / scaleH)); - } + // Animation down from the full screen to the thumbnail + Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, + mNextAppTransitionStartX + (thumbWidth / 2f), + mNextAppTransitionStartY + (thumbHeight / 2f)); + scale.setInterpolator(mThumbnailFastOutSlowInInterpolator); + scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); + Animation alpha = new AlphaAnimation(0f, 1f); + alpha.setInterpolator(mThumbnailFadeInInterpolator); + alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION); + Animation translate = new TranslateAnimation(0, 0, -unscaledStartY + + mNextAppTransitionInsets.top, 0); + translate.setInterpolator(mThumbnailFastOutSlowInInterpolator); + translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); - return prepareThumbnailAnimation(a, appWidth, appHeight, transit); + // This AnimationSet uses the Interpolators assigned above. + AnimationSet set = new AnimationSet(false); + set.addAnimation(scale); + set.addAnimation(alpha); + set.addAnimation(translate); + a = set; + + } + return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, 0, + mThumbnailFastOutSlowInInterpolator); } /** * This alternate animation is created when we are doing a thumbnail transition, for the * activity that is leaving, and the activity that is entering. */ - Animation createAlternateThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth, - int appHeight, int orientation, int transit, - Rect containingFrame, Rect contentInsets, - boolean isFullScreen) { + Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState, + int appWidth, int appHeight, int orientation, int transit, Rect containingFrame, + Rect contentInsets, boolean isFullScreen) { Animation a; final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; @@ -524,7 +580,7 @@ public class AppTransition implements Dump { switch (thumbTransitState) { case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: { - // Entering app scales up with the thumbnail + // App window scaling up to become full screen if (orientation == Configuration.ORIENTATION_PORTRAIT) { // In portrait, we scale the width and clip to the top/left square scale = thumbWidth / appWidth; @@ -550,16 +606,15 @@ public class AppTransition implements Dump { mTmpFromClipRect.right = (mTmpFromClipRect.left + unscaledThumbWidth); mTmpToClipRect.set(containingFrame); } + mNextAppTransitionInsets.set(contentInsets); Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1, computePivot(mNextAppTransitionStartX, scale), computePivot(mNextAppTransitionStartY, scale)); - Animation alphaAnim = new AlphaAnimation(1, 1); Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0); AnimationSet set = new AnimationSet(true); - set.addAnimation(alphaAnim); set.addAnimation(clipAnim); set.addAnimation(scaleAnim); set.addAnimation(translateAnim); @@ -567,26 +622,29 @@ public class AppTransition implements Dump { break; } case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: { - // Exiting app while the thumbnail is scaling up should fade + // Previous app window during the scale up if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { - // Fade out while bringing up selected activity. This keeps the - // current activity from showing through a launching wallpaper + // Fade out the source activity if we are animating to a wallpaper // activity. a = new AlphaAnimation(1, 0); } else { - // noop animation a = new AlphaAnimation(1, 1); } break; } case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: { - // Entering the other app, it should just be visible while we scale the thumbnail - // down above it - a = new AlphaAnimation(1, 1); + // Target app window during the scale down + if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { + // Fade in the destination activity if we are animating from a wallpaper + // activity. + a = new AlphaAnimation(0, 1); + } else { + a = new AlphaAnimation(1, 1); + } break; } case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { - // Exiting the current app, the app should scale down with the thumbnail + // App window scaling down from full screen if (orientation == Configuration.ORIENTATION_PORTRAIT) { // In portrait, we scale the width and clip to the top/left square scale = thumbWidth / appWidth; @@ -612,16 +670,15 @@ public class AppTransition implements Dump { } mTmpToClipRect.right = (mTmpToClipRect.left + unscaledThumbWidth); } + mNextAppTransitionInsets.set(contentInsets); Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale, computePivot(mNextAppTransitionStartX, scale), computePivot(mNextAppTransitionStartY, scale)); - Animation alphaAnim = new AlphaAnimation(1, 1); Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor); AnimationSet set = new AnimationSet(true); - set.addAnimation(alphaAnim); set.addAnimation(clipAnim); set.addAnimation(scaleAnim); set.addAnimation(translateAnim); @@ -639,6 +696,46 @@ public class AppTransition implements Dump { } /** + * This animation runs for the thumbnail that gets cross faded with the enter/exit activity + * when a thumbnail is specified with the activity options. + */ + Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) { + Animation a; + final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); + final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; + final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); + final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; + + if (mNextAppTransitionScaleUp) { + // Animation for the thumbnail zooming from its initial size to the full screen + float scaleW = appWidth / thumbWidth; + float scaleH = appHeight / thumbHeight; + Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, + computePivot(mNextAppTransitionStartX, 1 / scaleW), + computePivot(mNextAppTransitionStartY, 1 / scaleH)); + scale.setInterpolator(mDecelerateInterpolator); + + Animation alpha = new AlphaAnimation(1, 0); + alpha.setInterpolator(mThumbnailFadeOutInterpolator); + + // This AnimationSet uses the Interpolators assigned above. + AnimationSet set = new AnimationSet(false); + set.addAnimation(scale); + set.addAnimation(alpha); + a = set; + } else { + // Animation for the thumbnail zooming down from the full screen to its final size + float scaleW = appWidth / thumbWidth; + float scaleH = appHeight / thumbHeight; + a = new ScaleAnimation(scaleW, 1, scaleH, 1, + computePivot(mNextAppTransitionStartX, 1 / scaleW), + computePivot(mNextAppTransitionStartY, 1 / scaleH)); + } + + return prepareThumbnailAnimation(a, appWidth, appHeight, transit); + } + + /** * This animation is created when we are doing a thumbnail transition, for the activity that is * leaving, and the activity that is entering. */ @@ -747,12 +844,26 @@ public class AppTransition implements Dump { mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) { mNextAppTransitionScaleUp = (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP); - a = createAlternateThumbnailEnterExitAnimationLocked( + a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter), + appWidth, appHeight, transit); + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { + String animName = mNextAppTransitionScaleUp ? + "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN"; + Slog.v(TAG, "applyAnimation:" + + " anim=" + a + " nextAppTransition=" + animName + + " transit=" + transit + " isEntrance=" + enter + + " Callers=" + Debug.getCallers(3)); + } + } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP || + mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) { + mNextAppTransitionScaleUp = + (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP); + a = createAspectScaledThumbnailEnterExitAnimationLocked( getThumbnailTransitionState(enter), appWidth, appHeight, orientation, transit, containingFrame, contentInsets, isFullScreen); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { String animName = mNextAppTransitionScaleUp ? - "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN"; + "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN"; Slog.v(TAG, "applyAnimation:" + " anim=" + a + " nextAppTransition=" + animName + " transit=" + transit + " isEntrance=" + enter @@ -881,6 +992,23 @@ public class AppTransition implements Dump { } } + void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY, + IRemoteCallback startedCallback, boolean scaleUp) { + if (isTransitionSet()) { + mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP + : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; + mNextAppTransitionPackage = null; + mNextAppTransitionThumbnail = srcThumb; + mNextAppTransitionScaleUp = scaleUp; + mNextAppTransitionStartX = startX; + mNextAppTransitionStartY = startY; + postAnimationCallback(); + mNextAppTransitionCallback = startedCallback; + } else { + postAnimationCallback(); + } + } + @Override public String toString() { return "mNextAppTransition=0x" + Integer.toHexString(mNextAppTransition); @@ -966,6 +1094,10 @@ public class AppTransition implements Dump { return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP"; case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN: return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN"; + case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP: + return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP"; + case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: + return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN"; default: return "unknown type=" + mNextAppTransitionType; } @@ -998,6 +1130,8 @@ public class AppTransition implements Dump { break; case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP: case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN: + case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP: + case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: pw.print(" mNextAppTransitionThumbnail="); pw.print(mNextAppTransitionThumbnail); pw.print(" mNextAppTransitionStartX="); diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java index 5b05d53..ef74205 100644 --- a/services/core/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java @@ -64,8 +64,17 @@ public class AppWindowAnimator { int thumbnailX; int thumbnailY; int thumbnailLayer; + int thumbnailForceAboveLayer; Animation thumbnailAnimation; final Transformation thumbnailTransformation = new Transformation(); + // This flag indicates that the destruction of the thumbnail surface is synchronized with + // another animation, so do not pre-emptively destroy the thumbnail surface when the animation + // completes + boolean deferThumbnailDestruction; + // This is the thumbnail surface that has been bestowed upon this animator, and when the + // surface for this animator's animation is complete, we will destroy the thumbnail surface + // as well. Do not animate or do anything with this surface. + SurfaceControl deferredThumbnail; /** WindowStateAnimator from mAppAnimator.allAppWindows as of last performLayout */ ArrayList<WindowStateAnimator> mAllAppWinAnimators = new ArrayList<WindowStateAnimator>(); @@ -121,7 +130,9 @@ public class AppWindowAnimator { animation = null; animating = true; } - clearThumbnail(); + if (!deferThumbnailDestruction) { + clearThumbnail(); + } if (mAppToken.deferClearAllDrawn) { mAppToken.allDrawn = false; mAppToken.deferClearAllDrawn = false; @@ -135,6 +146,13 @@ public class AppWindowAnimator { } } + public void clearDeferredThumbnail() { + if (deferredThumbnail != null) { + deferredThumbnail.destroy(); + deferredThumbnail = null; + } + } + void updateLayers() { final int N = mAppToken.allAppWindows.size(); final int adj = animLayerAdjustment; @@ -184,10 +202,14 @@ public class AppWindowAnimator { + "][" + tmpFloats[Matrix.MSKEW_X] + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null); thumbnail.setAlpha(thumbnailTransformation.getAlpha()); - // The thumbnail is layered below the window immediately above this - // token's anim layer. - thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER - - WindowManagerService.LAYER_OFFSET_THUMBNAIL); + if (thumbnailForceAboveLayer > 0) { + thumbnail.setLayer(thumbnailForceAboveLayer + 1); + } else { + // The thumbnail is layered below the window immediately above this + // token's anim layer. + thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER + - WindowManagerService.LAYER_OFFSET_THUMBNAIL); + } thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y], tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]); } @@ -202,7 +224,9 @@ public class AppWindowAnimator { TAG, "Stepped animation in " + mAppToken + ": more=" + more + ", xform=" + transformation); if (!more) { animation = null; - clearThumbnail(); + if (!deferThumbnailDestruction) { + clearThumbnail(); + } if (WindowManagerService.DEBUG_ANIM) Slog.v( TAG, "Finished animation in " + mAppToken + " @ " + currentTime); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 32f3707..e144bde 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -95,7 +95,6 @@ import android.os.WorkSource; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.EventLog; -import android.util.FloatMath; import android.util.Log; import android.util.SparseArray; import android.util.Pair; @@ -4038,6 +4037,15 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, + int startY, IRemoteCallback startedCallback, boolean scaleUp) { + synchronized(mWindowMap) { + mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX, + startY, startedCallback, scaleUp); + } + } + + @Override public void executeAppTransition() { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "executeAppTransition()")) { @@ -8893,7 +8901,9 @@ public class WindowManagerService extends IWindowManager.Stub } AppWindowToken topOpeningApp = null; + AppWindowToken topClosingApp = null; int topOpeningLayer = 0; + int topClosingLayer = 0; NN = mOpeningApps.size(); for (i=0; i<NN; i++) { @@ -8901,8 +8911,8 @@ public class WindowManagerService extends IWindowManager.Stub final AppWindowAnimator appAnimator = wtoken.mAppAnimator; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); appAnimator.clearThumbnail(); - wtoken.inPendingTransaction = false; appAnimator.animation = null; + wtoken.inPendingTransaction = false; setTokenVisibilityLocked(wtoken, animLp, true, transit, false, voiceInteraction); wtoken.updateReportedVisibilityLocked(); wtoken.waitingToShow = false; @@ -8931,10 +8941,11 @@ public class WindowManagerService extends IWindowManager.Stub NN = mClosingApps.size(); for (i=0; i<NN; i++) { AppWindowToken wtoken = mClosingApps.valueAt(i); + final AppWindowAnimator appAnimator = wtoken.mAppAnimator; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); - wtoken.mAppAnimator.clearThumbnail(); + appAnimator.clearThumbnail(); + appAnimator.animation = null; wtoken.inPendingTransaction = false; - wtoken.mAppAnimator.animation = null; setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction); wtoken.updateReportedVisibilityLocked(); wtoken.waitingToHide = false; @@ -8943,14 +8954,30 @@ public class WindowManagerService extends IWindowManager.Stub // gotten drawn. wtoken.allDrawn = true; wtoken.deferClearAllDrawn = false; + + if (animLp != null) { + int layer = -1; + for (int j=0; j<wtoken.windows.size(); j++) { + WindowState win = wtoken.windows.get(j); + if (win.mWinAnimator.mAnimLayer > layer) { + layer = win.mWinAnimator.mAnimLayer; + } + } + if (topClosingApp == null || layer > topClosingLayer) { + topClosingApp = wtoken; + topClosingLayer = layer; + } + } } - boolean useAlternateThumbnailAnimation = true; - AppWindowAnimator appAnimator = - topOpeningApp == null ? null : topOpeningApp.mAppAnimator; + AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null : + topOpeningApp.mAppAnimator; + AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null : + topClosingApp.mAppAnimator; Bitmap nextAppTransitionThumbnail = mAppTransition.getNextAppTransitionThumbnail(); - if (!useAlternateThumbnailAnimation && nextAppTransitionThumbnail != null - && appAnimator != null && appAnimator.animation != null) { + if (nextAppTransitionThumbnail != null + && openingAppAnimator != null && openingAppAnimator.animation != null && + nextAppTransitionThumbnail.getConfig() != Config.ALPHA_8) { // This thumbnail animation is very special, we need to have // an extra surface with the thumbnail included with the animation. Rect dirty = new Rect(0, 0, nextAppTransitionThumbnail.getWidth(), @@ -8959,34 +8986,61 @@ public class WindowManagerService extends IWindowManager.Stub // TODO(multi-display): support other displays final DisplayContent displayContent = getDefaultDisplayContentLocked(); final Display display = displayContent.getDisplay(); + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + + // Create a new surface for the thumbnail SurfaceControl surfaceControl = new SurfaceControl(mFxSession, - "thumbnail anim", - dirty.width(), dirty.height(), + "thumbnail anim", dirty.width(), dirty.height(), PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN); surfaceControl.setLayerStack(display.getLayerStack()); - appAnimator.thumbnail = surfaceControl; - if (SHOW_TRANSACTIONS) Slog.i(TAG, " THUMBNAIL " + surfaceControl + ": CREATE"); + if (SHOW_TRANSACTIONS) { + Slog.i(TAG, " THUMBNAIL " + surfaceControl + ": CREATE"); + } + + // Draw the thumbnail onto the surface Surface drawSurface = new Surface(); drawSurface.copyFrom(surfaceControl); Canvas c = drawSurface.lockCanvas(dirty); c.drawBitmap(nextAppTransitionThumbnail, 0, 0, null); drawSurface.unlockCanvasAndPost(c); drawSurface.release(); - appAnimator.thumbnailLayer = topOpeningLayer; - DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); - Animation anim = mAppTransition.createThumbnailScaleAnimationLocked( - displayInfo.appWidth, displayInfo.appHeight, transit); - appAnimator.thumbnailAnimation = anim; + + // Get the thumbnail animation + Animation anim; + if (mAppTransition.isNextThumbnailTransitionAspectScaled()) { + // For the new aspect-scaled transition, we want it to always show + // above the animating opening/closing window, and we want to + // synchronize its thumbnail surface with the surface for the + // open/close animation (only on the way down) + anim = mAppTransition.createThumbnailAspectScaleAnimationLocked( + displayInfo.appWidth, displayInfo.appHeight, + displayInfo.logicalWidth, transit); + openingAppAnimator.thumbnailForceAboveLayer = Math.max(topOpeningLayer, + topClosingLayer); + openingAppAnimator.deferThumbnailDestruction = + !mAppTransition.isNextThumbnailTransitionScaleUp(); + if (openingAppAnimator.deferThumbnailDestruction) { + if (closingAppAnimator != null && + closingAppAnimator.animation != null) { + closingAppAnimator.deferredThumbnail = surfaceControl; + } + } + } else { + anim = mAppTransition.createThumbnailScaleAnimationLocked( + displayInfo.appWidth, displayInfo.appHeight, transit); + } anim.restrictDuration(MAX_ANIMATION_DURATION); anim.scaleCurrentDuration(getTransitionAnimationScaleLocked()); - Point p = new Point(); - mAppTransition.getStartingPoint(p); - appAnimator.thumbnailX = p.x; - appAnimator.thumbnailY = p.y; + + openingAppAnimator.thumbnail = surfaceControl; + openingAppAnimator.thumbnailLayer = topOpeningLayer; + openingAppAnimator.thumbnailAnimation = anim; + openingAppAnimator.thumbnailX = mAppTransition.getStartingX(); + openingAppAnimator.thumbnailY = mAppTransition.getStartingY(); } catch (OutOfResourcesException e) { Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w=" + dirty.width() + " h=" + dirty.height(), e); - appAnimator.clearThumbnail(); + openingAppAnimator.clearThumbnail(); } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 7bf090a..38433ae 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -902,6 +902,11 @@ class WindowStateAnimator { mWin.mHasSurface = false; mDrawState = NO_SURFACE; } + + // Destroy any deferred thumbnail surfaces + if (mAppAnimator != null) { + mAppAnimator.clearDeferredThumbnail(); + } } void destroyDeferredSurfaceLocked() { diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index 5954947..85ceefd 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -266,7 +266,7 @@ public class TelephonyIntents { /** * Activity Action: Start this activity to invoke the carrier setup app. - * To filter the intent, see {@link #CATEGORY_MCCMNC_PREFIX}. + * The carrier app must be signed using a certificate that matches the UICC access rules. * * <p class="note">Callers of this should hold the android.permission.INVOKE_CARRIER_SETUP * permission.</p> @@ -274,13 +274,6 @@ public class TelephonyIntents { public static final String ACTION_CARRIER_SETUP = "android.intent.action.ACTION_CARRIER_SETUP"; /** - * A <em>prefix</em> for the MCC/MNC filtering used with {@link #ACTION_CARRIER_SETUP}. - * The MCC/MNC will be concatenated (zero-padded to 3 digits each) to create a final - * string of the form: {@code android.intent.category.MCCMNC_310260} - */ - public static final String CATEGORY_MCCMNC_PREFIX = "android.intent.category.MCCMNC_"; - - /** * Broadcast Action: A "secret code" has been entered in the dialer. Secret codes are * of the form {@code *#*#<code>#*#*}. The intent will have the data URI: * diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java index 6633787..78e360b 100644 --- a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java +++ b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; +import android.graphics.Color; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -199,6 +200,7 @@ public class FrameworkPerfActivity extends Activity mLocalCheckBox = (CheckBox)findViewById(R.id.local); mLog = (TextView)findViewById(R.id.log); + mLog.setTextColor(Color.RED); PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE); mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Scheduler"); diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp index 69a9c7f..32a0cd3 100644 --- a/tools/aapt/AaptConfig.cpp +++ b/tools/aapt/AaptConfig.cpp @@ -255,6 +255,8 @@ void applyVersionForCompatibility(ConfigDescription* config) { != ResTable_config::SCREENLONG_ANY || config->density != ResTable_config::DENSITY_DEFAULT) { minSdk = SDK_DONUT; + } else if ((config->density == ResTable_config::DENSITY_ANY)) { + minSdk = SDK_L; } if (minSdk > config->sdkVersion) { @@ -477,6 +479,11 @@ bool parseDensity(const char* name, ResTable_config* out) { return true; } + if (strcmp(name, "anydpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_ANY; + return true; + } + if (strcmp(name, "nodpi") == 0) { if (out) out->density = ResTable_config::DENSITY_NONE; return true; diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index 1439f14..af49461 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -24,6 +24,7 @@ enum { SDK_HONEYCOMB_MR2 = 13, SDK_ICE_CREAM_SANDWICH = 14, SDK_ICE_CREAM_SANDWICH_MR1 = 15, + SDK_L = 21, }; /* diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index b394671..a0f0a08 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -2488,10 +2488,12 @@ int doPackage(Bundle* bundle) if (bundle->getCustomPackage() == NULL) { // Write the R.java file into the appropriate class directory // e.g. gen/com/foo/app/R.java - err = writeResourceSymbols(bundle, assets, assets->getPackage(), true); + err = writeResourceSymbols(bundle, assets, assets->getPackage(), true, + bundle->getBuildSharedLibrary()); } else { const String8 customPkg(bundle->getCustomPackage()); - err = writeResourceSymbols(bundle, assets, customPkg, true); + err = writeResourceSymbols(bundle, assets, customPkg, true, + bundle->getBuildSharedLibrary()); } if (err < 0) { goto bail; @@ -2505,7 +2507,7 @@ int doPackage(Bundle* bundle) char* packageString = strtok(libs.lockBuffer(libs.length()), ":"); while (packageString != NULL) { // Write the R.java file out with the correct package name - err = writeResourceSymbols(bundle, assets, String8(packageString), true); + err = writeResourceSymbols(bundle, assets, String8(packageString), true, false); if (err < 0) { goto bail; } @@ -2514,11 +2516,11 @@ int doPackage(Bundle* bundle) libs.unlockBuffer(); } } else { - err = writeResourceSymbols(bundle, assets, assets->getPackage(), false); + err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false); if (err < 0) { goto bail; } - err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true); + err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false); if (err < 0) { goto bail; } diff --git a/tools/aapt/IndentPrinter.h b/tools/aapt/IndentPrinter.h new file mode 100644 index 0000000..6fc94bc --- /dev/null +++ b/tools/aapt/IndentPrinter.h @@ -0,0 +1,63 @@ +#ifndef __INDENT_PRINTER_H +#define __INDENT_PRINTER_H + +class IndentPrinter { +public: + IndentPrinter(FILE* stream, int indentSize=2) + : mStream(stream) + , mIndentSize(indentSize) + , mIndent(0) + , mNeedsIndent(true) { + } + + void indent(int amount = 1) { + mIndent += amount; + if (mIndent < 0) { + mIndent = 0; + } + } + + void print(const char* fmt, ...) { + doIndent(); + va_list args; + va_start(args, fmt); + vfprintf(mStream, fmt, args); + va_end(args); + } + + void println(const char* fmt, ...) { + doIndent(); + va_list args; + va_start(args, fmt); + vfprintf(mStream, fmt, args); + va_end(args); + fputs("\n", mStream); + mNeedsIndent = true; + } + + void println() { + doIndent(); + fputs("\n", mStream); + mNeedsIndent = true; + } + +private: + void doIndent() { + if (mNeedsIndent) { + int numSpaces = mIndent * mIndentSize; + while (numSpaces > 0) { + fputs(" ", mStream); + numSpaces--; + } + mNeedsIndent = false; + } + } + + FILE* mStream; + const int mIndentSize; + int mIndent; + bool mNeedsIndent; +}; + +#endif // __INDENT_PRINTER_H + diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h index 34c4496..dd40b20 100644 --- a/tools/aapt/Main.h +++ b/tools/aapt/Main.h @@ -49,7 +49,8 @@ extern android::status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder); extern android::status_t writeResourceSymbols(Bundle* bundle, - const sp<AaptAssets>& assets, const String8& pkgName, bool includePrivate); + const sp<AaptAssets>& assets, const String8& pkgName, + bool includePrivate, bool emitCallback); extern android::status_t writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets); diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 1d93b89..7979a1d 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -3,18 +3,17 @@ // // Build resource files from raw assets. // -#include "Main.h" #include "AaptAssets.h" -#include "StringPool.h" -#include "XMLNode.h" -#include "ResourceTable.h" -#include "Images.h" - +#include "CacheUpdater.h" #include "CrunchCache.h" #include "FileFinder.h" -#include "CacheUpdater.h" - +#include "Images.h" +#include "IndentPrinter.h" +#include "Main.h" +#include "ResourceTable.h" +#include "StringPool.h" #include "WorkQueue.h" +#include "XMLNode.h" #if HAVE_PRINTF_ZD # define ZD "%zd" @@ -1801,6 +1800,112 @@ static String16 getAttributeComment(const sp<AaptAssets>& assets, return String16(); } +static void writeResourceLoadedCallback(FILE* fp, int indent) { + IndentPrinter p(fp, 4); + p.indent(indent); + p.println("private static void rewriteIntArrayField(java.lang.reflect.Field field, int packageId) throws IllegalAccessException {"); + { + p.indent(); + p.println("int requiredModifiers = java.lang.reflect.Modifier.STATIC | java.lang.reflect.Modifier.PUBLIC;"); + p.println("if ((field.getModifiers() & requiredModifiers) != requiredModifiers) {"); + { + p.indent(); + p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not rewritable\");"); + p.indent(-1); + } + p.println("}"); + p.println("if (field.getType() != int[].class) {"); + { + p.indent(); + p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not an int array\");"); + p.indent(-1); + } + p.println("}"); + p.println("int[] array = (int[]) field.get(null);"); + p.println("for (int i = 0; i < array.length; i++) {"); + { + p.indent(); + p.println("array[i] = (array[i] & 0x00ffffff) | (packageId << 24);"); + p.indent(-1); + } + p.println("}"); + p.indent(-1); + } + p.println("}"); + p.println(); + p.println("private static void rewriteIntField(java.lang.reflect.Field field, int packageId) throws IllegalAccessException {"); + { + p.indent(); + p.println("int requiredModifiers = java.lang.reflect.Modifier.STATIC | java.lang.reflect.Modifier.PUBLIC;"); + p.println("int bannedModifiers = java.lang.reflect.Modifier.FINAL;"); + p.println("int mod = field.getModifiers();"); + p.println("if ((mod & requiredModifiers) != requiredModifiers || (mod & bannedModifiers) != 0) {"); + { + p.indent(); + p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not rewritable\");"); + p.indent(-1); + } + p.println("}"); + p.println("if (field.getType() != int.class && field.getType() != Integer.class) {"); + { + p.indent(); + p.println("throw new IllegalArgumentException(\"Field \" + field.getName() + \" is not an int\");"); + p.indent(-1); + } + p.println("}"); + p.println("int resId = field.getInt(null);"); + p.println("field.setInt(null, (resId & 0x00ffffff) | (packageId << 24));"); + p.indent(-1); + } + p.println("}"); + p.println(); + p.println("public static void onResourcesLoaded(int assignedPackageId) throws Exception {"); + { + p.indent(); + p.println("Class<?>[] declaredClasses = R.class.getDeclaredClasses();"); + p.println("for (Class<?> clazz : declaredClasses) {"); + { + p.indent(); + p.println("if (clazz.getSimpleName().equals(\"styleable\")) {"); + { + p.indent(); + p.println("for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {"); + { + p.indent(); + p.println("if (field.getType() == int[].class) {"); + { + p.indent(); + p.println("rewriteIntArrayField(field, assignedPackageId);"); + p.indent(-1); + } + p.println("}"); + p.indent(-1); + } + p.println("}"); + p.indent(-1); + } + p.println("} else {"); + { + p.indent(); + p.println("for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {"); + { + p.indent(); + p.println("rewriteIntField(field, assignedPackageId);"); + p.indent(-1); + } + p.println("}"); + p.indent(-1); + } + p.println("}"); + p.indent(-1); + } + p.println("}"); + p.indent(-1); + } + p.println("}"); + p.println(); +} + static status_t writeLayoutClasses( FILE* fp, const sp<AaptAssets>& assets, const sp<AaptSymbols>& symbols, int indent, bool includePrivate, bool nonConstantId) @@ -2138,7 +2243,7 @@ static status_t writeTextLayoutClasses( static status_t writeSymbolClass( FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, const sp<AaptSymbols>& symbols, const String8& className, int indent, - bool nonConstantId) + bool nonConstantId, bool emitCallback) { fprintf(fp, "%spublic %sfinal class %s {\n", getIndentSpace(indent), @@ -2238,7 +2343,8 @@ static status_t writeSymbolClass( if (nclassName == "styleable") { styleableSymbols = nsymbols; } else { - err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId); + err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, + indent, nonConstantId, false); } if (err != NO_ERROR) { return err; @@ -2252,6 +2358,10 @@ static status_t writeSymbolClass( } } + if (emitCallback) { + writeResourceLoadedCallback(fp, indent); + } + indent--; fprintf(fp, "%s}\n", getIndentSpace(indent)); return NO_ERROR; @@ -2299,7 +2409,7 @@ static status_t writeTextSymbolClass( } status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, - const String8& package, bool includePrivate) + const String8& package, bool includePrivate, bool emitCallback) { if (!bundle->getRClassDir()) { return NO_ERROR; @@ -2355,7 +2465,7 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, "package %s;\n\n", package.string()); status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, - className, 0, bundle->getNonConstantId()); + className, 0, bundle->getNonConstantId(), emitCallback); fclose(fp); if (err != NO_ERROR) { return err; diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index f8bc2ae..2604e97 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -221,6 +221,12 @@ public class IWindowManagerImpl implements IWindowManager { } @Override + public void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, + int startY, IRemoteCallback startedCallback, boolean scaleUp) { + // TODO Auto-generated method stub + } + + @Override public void pauseKeyDispatching(IBinder arg0) throws RemoteException { // TODO Auto-generated method stub |