Merge "Moved window manager wallpaper control into separate class"
diff --git a/api/current.txt b/api/current.txt
index 3e1f5a5..acc9c4b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -34141,6 +34141,15 @@
field public static final int RTL = 1; // 0x1
}
+ public final class LocaleList {
+ ctor public LocaleList();
+ ctor public LocaleList(java.util.Locale[]);
+ method public java.util.Locale get(int);
+ method public java.util.Locale getPrimary();
+ method public boolean isEmpty();
+ method public int size();
+ }
+
public final class Log {
method public static int d(java.lang.String, java.lang.String);
method public static int d(java.lang.String, java.lang.String, java.lang.Throwable);
diff --git a/api/system-current.txt b/api/system-current.txt
index 984d395..955fb07 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -36474,6 +36474,15 @@
field public static final int RTL = 1; // 0x1
}
+ public final class LocaleList {
+ ctor public LocaleList();
+ ctor public LocaleList(java.util.Locale[]);
+ method public java.util.Locale get(int);
+ method public java.util.Locale getPrimary();
+ method public boolean isEmpty();
+ method public int size();
+ }
+
public final class Log {
method public static int d(java.lang.String, java.lang.String);
method public static int d(java.lang.String, java.lang.String, java.lang.Throwable);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ed7a2a3..924df1b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4702,7 +4702,7 @@
private static boolean copyNeeded(int flags, Package p,
PackageUserState state, Bundle metaData, int userId) {
- if (userId != UserHandle.USER_OWNER) {
+ if (userId != UserHandle.USER_SYSTEM) {
// We always need to copy for other users, since we need
// to fix up the uid.
return true;
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index b293e2a..b3f03c3 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -213,7 +213,7 @@
@Override
public void onReceive(Context context, Intent intent) {
// External apps can't coexist with multi-user, so scan owner
- handlePackageEvent(intent, UserHandle.USER_OWNER);
+ handlePackageEvent(intent, UserHandle.USER_SYSTEM);
}
};
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 7aa9787..a04cdce 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -19,7 +19,9 @@
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.utils.HashCodeHelpers;
+import android.hardware.camera2.utils.SurfaceUtils;
import android.util.Log;
+import android.util.Size;
import android.view.Surface;
import android.os.Parcel;
import android.os.Parcelable;
@@ -66,9 +68,7 @@
*
*/
public OutputConfiguration(Surface surface) {
- checkNotNull(surface, "Surface must not be null");
- mSurface = surface;
- mRotation = ROTATION_0;
+ this(surface, ROTATION_0);
}
/**
@@ -94,6 +94,9 @@
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
mSurface = surface;
mRotation = rotation;
+ mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
+ mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
+ mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
}
/**
@@ -106,6 +109,9 @@
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
mSurface = surface;
mRotation = rotation;
+ mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
+ mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
+ mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
}
/**
@@ -163,8 +169,9 @@
/**
* Check if this {@link OutputConfiguration} is equal to another {@link OutputConfiguration}.
*
- * <p>Two output configurations are only equal if and only if the underlying surface and
- * all other configuration parameters are equal. </p>
+ * <p>Two output configurations are only equal if and only if the underlying surfaces, surface
+ * properties (width, height, format, dataspace) when the output configurations are created,
+ * and all other configuration parameters are equal. </p>
*
* @return {@code true} if the objects were equal, {@code false} otherwise
*/
@@ -176,7 +183,11 @@
return true;
} else if (obj instanceof OutputConfiguration) {
final OutputConfiguration other = (OutputConfiguration) obj;
- return (mSurface == other.mSurface && mRotation == other.mRotation);
+ return mSurface == other.mSurface &&
+ mRotation == other.mRotation &&
+ mConfiguredSize.equals(other.mConfiguredSize) &&
+ mConfiguredFormat == other.mConfiguredFormat &&
+ mConfiguredDataspace == other.mConfiguredDataspace;
}
return false;
}
@@ -192,4 +203,9 @@
private static final String TAG = "OutputConfiguration";
private final Surface mSurface;
private final int mRotation;
+
+ // The size, format, and dataspace of the surface when OutputConfiguration is created.
+ private final Size mConfiguredSize;
+ private final int mConfiguredFormat;
+ private final int mConfiguredDataspace;
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index a6d477f..3bd12c0 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -351,8 +351,12 @@
public void maybeMarkCapabilitiesRestricted() {
// If all the capabilities are typically provided by restricted networks, conclude that this
// network is restricted.
- if ((mNetworkCapabilities & ~(DEFAULT_CAPABILITIES | RESTRICTED_CAPABILITIES)) == 0)
+ if ((mNetworkCapabilities & ~(DEFAULT_CAPABILITIES | RESTRICTED_CAPABILITIES)) == 0 &&
+ // Must have at least some restricted capabilities, otherwise a request for an
+ // internet-less network will get marked restricted.
+ (mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0) {
removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ }
}
/**
diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java
new file mode 100644
index 0000000..017735a
--- /dev/null
+++ b/core/java/android/util/LocaleList.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.Nullable;
+
+import java.util.HashSet;
+import java.util.Locale;
+
+// TODO: We don't except too many LocaleLists to exist at the same time, and
+// we need access to the data at native level, so we should pass the data
+// down to the native level, create a mapt of every list seen there, take a
+// pointer back, and just keep that pointed in the Java-level object, so
+// things could be copied very quickly.
+
+/**
+ * LocaleList is an immutable list of Locales, typically used to keep an
+ * ordered user preferences for locales.
+ */
+public final class LocaleList {
+ private final Locale[] mList;
+ private static final Locale[] sEmptyList = new Locale[0];
+
+ public Locale get(int location) {
+ return location < mList.length ? mList[location] : null;
+ }
+
+ public Locale getPrimary() {
+ return mList.length == 0 ? null : get(0);
+ }
+
+ public boolean isEmpty() {
+ return mList.length == 0;
+ }
+
+ public int size() {
+ return mList.length;
+ }
+
+ public LocaleList() {
+ mList = sEmptyList;
+ }
+
+ /**
+ * @throws NullPointerException if any of the input locales is <code>null</code>.
+ * @throws IllegalArgumentException if any of the input locales repeat.
+ */
+ public LocaleList(@Nullable Locale[] list) {
+ if (list == null || list.length == 0) {
+ mList = sEmptyList;
+ } else {
+ final Locale[] localeList = new Locale[list.length];
+ final HashSet<Locale> seenLocales = new HashSet<Locale>();
+ for (int i = 0; i < list.length; ++i) {
+ final Locale l = list[i];
+ if (l == null) {
+ throw new NullPointerException();
+ } else if (seenLocales.contains(l)) {
+ throw new IllegalArgumentException();
+ } else {
+ seenLocales.add(l);
+ localeList[i] = (Locale) l.clone();
+ }
+ }
+ mList = localeList;
+ }
+ }
+}
diff --git a/core/java/android/view/AppTransitionAnimationSpec.aidl b/core/java/android/view/AppTransitionAnimationSpec.aidl
new file mode 100644
index 0000000..8388347
--- /dev/null
+++ b/core/java/android/view/AppTransitionAnimationSpec.aidl
@@ -0,0 +1,20 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.view;
+
+/** @hide */
+parcelable AppTransitionAnimationSpec;
diff --git a/core/java/android/view/AppTransitionAnimationSpec.java b/core/java/android/view/AppTransitionAnimationSpec.java
new file mode 100644
index 0000000..c6e1989
--- /dev/null
+++ b/core/java/android/view/AppTransitionAnimationSpec.java
@@ -0,0 +1,61 @@
+package android.view;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Holds information about how the next app transition animation should be executed.
+ *
+ * This class is intended to be used with IWindowManager.overridePendingAppTransition* methods when
+ * simple arguments are not enough to describe the animation.
+ *
+ * @hide
+ */
+public class AppTransitionAnimationSpec implements Parcelable {
+ public final int taskId;
+ public final Bitmap bitmap;
+ public final Rect rect;
+
+ public AppTransitionAnimationSpec(int taskId, Bitmap bitmap, Rect rect) {
+ this.taskId = taskId;
+ this.bitmap = bitmap;
+ this.rect = rect;
+ }
+
+ public AppTransitionAnimationSpec(Parcel in) {
+ taskId = in.readInt();
+ bitmap = in.readParcelable(null);
+ rect = in.readParcelable(null);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(taskId);
+ dest.writeParcelable(bitmap, 0 /* flags */);
+ dest.writeParcelable(rect, 0 /* flags */);
+
+ }
+
+ public static final Parcelable.Creator<AppTransitionAnimationSpec> CREATOR
+ = new Parcelable.Creator<AppTransitionAnimationSpec>() {
+ public AppTransitionAnimationSpec createFromParcel(Parcel in) {
+ return new AppTransitionAnimationSpec(in);
+ }
+
+ public AppTransitionAnimationSpec[] newArray(int size) {
+ return new AppTransitionAnimationSpec[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "{taskId: " + taskId + ", bitmap: " + bitmap + ", rect: " + rect + "}";
+ }
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 0a4b982..f86adfe 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -39,6 +39,7 @@
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.IInputFilter;
+import android.view.AppTransitionAnimationSpec;
import android.view.WindowContentFrameStats;
/**
@@ -127,6 +128,16 @@
void overridePendingAppTransitionAspectScaledThumb(in Bitmap srcThumb, int startX,
int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
boolean scaleUp);
+ /**
+ * Overrides animation for app transition that exits from an application to a multi-window
+ * environment and allows specifying transition animation parameters for each window.
+ *
+ * @param specs Array of transition animation descriptions for entering windows.
+ *
+ * @hide
+ */
+ void overridePendingAppTransitionMultiThumb(in AppTransitionAnimationSpec[] specs,
+ IRemoteCallback startedCallback, boolean scaleUp);
void overridePendingAppTransitionInPlace(String packageName, int anim);
void executeAppTransition();
void setAppStartingWindow(IBinder token, String pkg, int theme,
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index cdc196e..197ea09 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -23,6 +23,7 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
@@ -294,6 +295,22 @@
public void buildLayer() {
}
+ @Override
+ public void setForeground(Drawable foreground) {
+ if (foreground != null) {
+ throw new UnsupportedOperationException(
+ "TextureView doesn't support displaying a foreground drawable");
+ }
+ }
+
+ @Override
+ public void setBackgroundDrawable(Drawable background) {
+ if (background != null) {
+ throw new UnsupportedOperationException(
+ "TextureView doesn't support displaying a background drawable");
+ }
+ }
+
/**
* Subclasses of TextureView cannot do their own rendering
* with the {@link Canvas} object.
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index abcd614..df01fc1 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -1226,6 +1226,12 @@
private int mLeft, mTop, mRight, mBottom;
+ /**
+ * Whether this view had any relative rules modified following the most
+ * recent resolution of layout direction.
+ */
+ private boolean mNeedsLayoutResolution;
+
private boolean mRulesChanged = false;
private boolean mIsRtlCompatibilityMode = false;
@@ -1374,47 +1380,69 @@
}
/**
- * Adds a layout rule to be interpreted by the RelativeLayout. This
- * method should only be used for constraints that don't refer to another sibling
- * (e.g., CENTER_IN_PARENT) or take a boolean value ({@link RelativeLayout#TRUE}
- * for true or 0 for false). To specify a verb that takes a subject, use
- * {@link #addRule(int, int)} instead.
+ * Adds a layout rule to be interpreted by the RelativeLayout.
+ * <p>
+ * This method should only be used for verbs that don't refer to a
+ * sibling (ex. {@link #ALIGN_RIGHT}) or take a boolean
+ * value ({@link #TRUE} for true or 0 for false). To
+ * specify a verb that takes a subject, use {@link #addRule(int, int)}.
+ * <p>
+ * If the rule is relative to the layout direction (ex.
+ * {@link #ALIGN_PARENT_START}), then the layout direction must be
+ * resolved using {@link #resolveLayoutDirection(int)} before calling
+ * {@link #getRule(int)} an absolute rule (ex.
+ * {@link #ALIGN_PARENT_LEFT}.
*
- * @param verb One of the verbs defined by
- * {@link android.widget.RelativeLayout RelativeLayout}, such as
- * ALIGN_WITH_PARENT_LEFT.
+ * @param verb a layout verb, such as {@link #ALIGN_PARENT_LEFT}
* @see #addRule(int, int)
+ * @see #removeRule(int)
* @see #getRule(int)
*/
public void addRule(int verb) {
- mRules[verb] = TRUE;
- mInitialRules[verb] = TRUE;
- mRulesChanged = true;
+ addRule(verb, TRUE);
}
/**
- * Adds a layout rule to be interpreted by the RelativeLayout. Use this for
- * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean
- * value (VISIBLE).
+ * Adds a layout rule to be interpreted by the RelativeLayout.
+ * <p>
+ * Use this for verbs that refer to a sibling (ex.
+ * {@link #ALIGN_RIGHT}) or take a boolean value (ex.
+ * {@link #CENTER_IN_PARENT}).
+ * <p>
+ * If the rule is relative to the layout direction (ex.
+ * {@link #START_OF}), then the layout direction must be resolved using
+ * {@link #resolveLayoutDirection(int)} before calling
+ * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
*
- * @param verb One of the verbs defined by
- * {@link android.widget.RelativeLayout RelativeLayout}, such as
- * ALIGN_WITH_PARENT_LEFT.
- * @param anchor The id of another view to use as an anchor,
- * or a boolean value (represented as {@link RelativeLayout#TRUE}
- * for true or 0 for false). For verbs that don't refer to another sibling
- * (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1.
+ * @param verb a layout verb, such as {@link #ALIGN_RIGHT}
+ * @param subject the ID of another view to use as an anchor, or a
+ * boolean value (represented as {@link #TRUE} for true
+ * or 0 for false)
* @see #addRule(int)
+ * @see #removeRule(int)
* @see #getRule(int)
*/
- public void addRule(int verb, int anchor) {
- mRules[verb] = anchor;
- mInitialRules[verb] = anchor;
+ public void addRule(int verb, int subject) {
+ // If we're removing a relative rule, we'll need to force layout
+ // resolution the next time it's requested.
+ if (!mNeedsLayoutResolution && isRelativeRule(verb)
+ && mInitialRules[verb] != 0 && subject == 0) {
+ mNeedsLayoutResolution = true;
+ }
+
+ mRules[verb] = subject;
+ mInitialRules[verb] = subject;
mRulesChanged = true;
}
/**
* Removes a layout rule to be interpreted by the RelativeLayout.
+ * <p>
+ * If the rule is relative to the layout direction (ex.
+ * {@link #START_OF}, {@link #ALIGN_PARENT_START}, etc.) then the
+ * layout direction must be resolved using
+ * {@link #resolveLayoutDirection(int)} before before calling
+ * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
*
* @param verb One of the verbs defined by
* {@link android.widget.RelativeLayout RelativeLayout}, such as
@@ -1424,9 +1452,7 @@
* @see #getRule(int)
*/
public void removeRule(int verb) {
- mRules[verb] = 0;
- mInitialRules[verb] = 0;
- mRulesChanged = true;
+ addRule(verb, 0);
}
/**
@@ -1451,6 +1477,12 @@
mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
}
+ private boolean isRelativeRule(int rule) {
+ return rule == START_OF || rule == END_OF
+ || rule == ALIGN_START || rule == ALIGN_END
+ || rule == ALIGN_PARENT_START || rule == ALIGN_PARENT_END;
+ }
+
// The way we are resolving rules depends on the layout direction and if we are pre JB MR1
// or not.
//
@@ -1578,7 +1610,9 @@
mRules[ALIGN_PARENT_END] = 0;
}
}
+
mRulesChanged = false;
+ mNeedsLayoutResolution = false;
}
/**
@@ -1596,13 +1630,7 @@
* @hide
*/
public int[] getRules(int layoutDirection) {
- if (hasRelativeRules() &&
- (mRulesChanged || layoutDirection != getLayoutDirection())) {
- resolveRules(layoutDirection);
- if (layoutDirection != getLayoutDirection()) {
- setLayoutDirection(layoutDirection);
- }
- }
+ resolveLayoutDirection(layoutDirection);
return mRules;
}
@@ -1618,16 +1646,30 @@
return mRules;
}
+ /**
+ * This will be called by {@link android.view.View#requestLayout()} to
+ * resolve layout parameters that are relative to the layout direction.
+ * <p>
+ * After this method is called, any rules using layout-relative verbs
+ * (ex. {@link #START_OF}) previously added via {@link #addRule(int)}
+ * may only be accessed via their resolved absolute verbs (ex.
+ * {@link #LEFT_OF}).
+ */
@Override
public void resolveLayoutDirection(int layoutDirection) {
- final boolean isLayoutRtl = isLayoutRtl();
- if (hasRelativeRules() && layoutDirection != getLayoutDirection()) {
+ if (shouldResolveLayoutDirection(layoutDirection)) {
resolveRules(layoutDirection);
}
- // This will set the layout direction
+
+ // This will set the layout direction.
super.resolveLayoutDirection(layoutDirection);
}
+ private boolean shouldResolveLayoutDirection(int layoutDirection) {
+ return (mNeedsLayoutResolution || hasRelativeRules())
+ && (mRulesChanged || layoutDirection != getLayoutDirection());
+ }
+
/** @hide */
@Override
protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 6c7e298..4ff7869 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -25,7 +25,6 @@
import android.net.NetworkStats;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiManager;
-import android.os.BadParcelableException;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Build;
diff --git a/core/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
index f5637dd..50ad547 100644
--- a/core/java/com/android/internal/widget/ExploreByTouchHelper.java
+++ b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
@@ -307,6 +307,10 @@
private AccessibilityEvent createEventForHost(int eventType) {
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
mView.onInitializeAccessibilityEvent(event);
+
+ // Allow the client to populate the event.
+ onPopulateEventForHost(event);
+
return event;
}
@@ -369,6 +373,10 @@
private AccessibilityNodeInfo createNodeForHost() {
final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(mView);
mView.onInitializeAccessibilityNodeInfo(node);
+ final int realNodeCount = node.getChildCount();
+
+ // Allow the client to populate the host node.
+ onPopulateNodeForHost(node);
// Add the virtual descendants.
if (mTempArray == null) {
@@ -378,6 +386,9 @@
}
final IntArray virtualViewIds = mTempArray;
getVisibleVirtualViews(virtualViewIds);
+ if (realNodeCount > 0 && virtualViewIds.size() > 0) {
+ throw new RuntimeException("Views cannot have both real and virtual children");
+ }
final int N = virtualViewIds.size();
for (int i = 0; i < N; i++) {
@@ -692,6 +703,18 @@
int virtualViewId, AccessibilityEvent event);
/**
+ * Populates an {@link AccessibilityEvent} with information about the host
+ * view.
+ * <p>
+ * The default implementation is a no-op.
+ *
+ * @param event the event to populate with information about the host view
+ */
+ protected void onPopulateEventForHost(AccessibilityEvent event) {
+ // Default implementation is no-op.
+ }
+
+ /**
* Populates an {@link AccessibilityNodeInfo} with information
* about the specified item.
* <p>
@@ -750,6 +773,18 @@
int virtualViewId, AccessibilityNodeInfo node);
/**
+ * Populates an {@link AccessibilityNodeInfo} with information about the
+ * host view.
+ * <p>
+ * The default implementation is a no-op.
+ *
+ * @param node the node to populate with information about the host view
+ */
+ protected void onPopulateNodeForHost(AccessibilityNodeInfo node) {
+ // Default implementation is no-op.
+ }
+
+ /**
* Performs the specified accessibility action on the item associated
* with the virtual view identifier. See
* {@link AccessibilityNodeInfo#performAction(int, Bundle)} for
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 05711b5..92812f8 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -20,6 +20,7 @@
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.view.ViewGroup;
@@ -63,18 +64,31 @@
private final int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
// The height of a window which has not in DIP.
private final int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
-
private PhoneWindow mOwner = null;
- boolean mWindowHasShadow = false;
- boolean mShowDecor = false;
+ private boolean mWindowHasShadow = false;
+ private boolean mShowDecor = false;
+
+ // True if the window is being dragged.
+ private boolean mDragging = false;
+
+ // The bounds of the window and the absolute mouse pointer coordinates from before we started to
+ // drag the window. They will be used to determine the next window position.
+ private final Rect mWindowOriginalBounds = new Rect();
+ private float mStartDragX;
+ private float mStartDragY;
+ // True when the left mouse button got released while dragging.
+ private boolean mLeftMouseButtonReleased;
+
+ // Avoiding re-creation of Rect's by keeping a temporary window drag bound.
+ private final Rect mWindowDragBounds = new Rect();
// The current focus state of the window for updating the window elevation.
- boolean mWindowHasFocus = true;
+ private boolean mWindowHasFocus = true;
// Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
// size calculation takes the shadow size into account. We set the elevation currently
// to max until the first layout command has been executed.
- boolean mAllowUpdateElevation = false;
+ private boolean mAllowUpdateElevation = false;
public NonClientDecorView(Context context) {
super(context);
@@ -103,6 +117,62 @@
findViewById(R.id.close_window).setOnClickListener(this);
}
+ @Override
+ public boolean onTouchEvent(MotionEvent e) {
+ // Note: There are no mixed events. When a new device gets used (e.g. 1. Mouse, 2. touch)
+ // the old input device events get cancelled first. So no need to remember the kind of
+ // input device we are listening to.
+ switch (e.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ // A drag action is started if we aren't dragging already and the starting event is
+ // either a left mouse button or any other input device.
+ if (!mDragging &&
+ (e.getToolType(e.getActionIndex()) != MotionEvent.TOOL_TYPE_MOUSE ||
+ (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0)) {
+ mDragging = true;
+ mWindowOriginalBounds.set(getActivityBounds());
+ mLeftMouseButtonReleased = false;
+ mStartDragX = e.getRawX();
+ mStartDragY = e.getRawY();
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (mDragging && !mLeftMouseButtonReleased) {
+ if (e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE &&
+ (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) == 0) {
+ // There is no separate mouse button up call and if the user mixes mouse
+ // button drag actions, we stop dragging once he releases the button.
+ mLeftMouseButtonReleased = true;
+ break;
+ }
+ mWindowDragBounds.set(mWindowOriginalBounds);
+ mWindowDragBounds.offset(Math.round(e.getRawX() - mStartDragX),
+ Math.round(e.getRawY() - mStartDragY));
+ setActivityBounds(mWindowDragBounds);
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ if (mDragging) {
+ // Since the window is already where it should be we don't have to do anything
+ // special at this time.
+ mDragging = false;
+ return true;
+ }
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ if (mDragging) {
+ mDragging = false;
+ setActivityBounds(mWindowOriginalBounds);
+ return true;
+ }
+ break;
+ }
+ return mDragging;
+ }
+
/**
* The phone window configuration has changed and the decor needs to be updated.
* @param showDecor True if the decor should be shown.
diff --git a/core/jni/android/graphics/AvoidXfermode.h b/core/jni/android/graphics/AvoidXfermode.h
index 318d7be..8b7fb71 100644
--- a/core/jni/android/graphics/AvoidXfermode.h
+++ b/core/jni/android/graphics/AvoidXfermode.h
@@ -40,7 +40,7 @@
Tolerance near 255: draw on any colors even remotely similar to the op-color
*/
static AvoidXfermode* Create(SkColor opColor, U8CPU tolerance, Mode mode) {
- return SkNEW_ARGS(AvoidXfermode, (opColor, tolerance, mode));
+ return new AvoidXfermode(opColor, tolerance, mode);
}
// overrides from SkXfermode
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index 751531e..20ecda2 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -52,14 +52,14 @@
// If this is set to negative value, then all the edge will be tessellated.
#define ALPHA_THRESHOLD (0.1f / 255.0f)
-#include <math.h>
-#include <utils/Log.h>
-
#include "AmbientShadow.h"
+
#include "ShadowTessellator.h"
#include "Vertex.h"
#include "VertexBuffer.h"
-#include "utils/MathUtils.h"
+
+#include <algorithm>
+#include <utils/Log.h>
namespace android {
namespace uirenderer {
@@ -78,7 +78,7 @@
// The input z value will be converted to be non-negative inside.
// The output must be ranged from 0 to 1.
inline float getAlphaFromFactoredZ(float factoredZ) {
- return 1.0 / (1 + MathUtils::max(factoredZ, 0.0f));
+ return 1.0 / (1 + std::max(factoredZ, 0.0f));
}
// The shader is using gaussian function e^-(1-x)*(1-x)*4, therefore, we transform
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
deleted file mode 100644
index 5c48709..0000000
--- a/libs/hwui/Android.common.mk
+++ /dev/null
@@ -1,118 +0,0 @@
-# getConfig in external/skia/include/core/SkBitmap.h is deprecated.
-# Allow Gnu extension: in-class initializer of static 'const float' member.
-# DeferredLayerUpdater.h: private field 'mRenderThread' is not used.
-
-LOCAL_SRC_FILES := \
- font/CacheTexture.cpp \
- font/Font.cpp \
- renderstate/Blend.cpp \
- renderstate/MeshState.cpp \
- renderstate/PixelBufferState.cpp \
- renderstate/RenderState.cpp \
- renderstate/Scissor.cpp \
- renderstate/Stencil.cpp \
- renderstate/TextureState.cpp \
- renderthread/CanvasContext.cpp \
- renderthread/DrawFrameTask.cpp \
- renderthread/EglManager.cpp \
- renderthread/RenderProxy.cpp \
- renderthread/RenderTask.cpp \
- renderthread/RenderThread.cpp \
- renderthread/TimeLord.cpp \
- thread/TaskManager.cpp \
- utils/Blur.cpp \
- utils/GLUtils.cpp \
- utils/LinearAllocator.cpp \
- utils/NinePatchImpl.cpp \
- AmbientShadow.cpp \
- AnimationContext.cpp \
- Animator.cpp \
- AnimatorManager.cpp \
- AssetAtlas.cpp \
- Caches.cpp \
- CanvasState.cpp \
- ClipArea.cpp \
- DamageAccumulator.cpp \
- DeferredDisplayList.cpp \
- DeferredLayerUpdater.cpp \
- DisplayList.cpp \
- DisplayListCanvas.cpp \
- Dither.cpp \
- Extensions.cpp \
- FboCache.cpp \
- FontRenderer.cpp \
- FrameInfo.cpp \
- FrameInfoVisualizer.cpp \
- GammaFontRenderer.cpp \
- GlopBuilder.cpp \
- GradientCache.cpp \
- Image.cpp \
- Interpolator.cpp \
- JankTracker.cpp \
- Layer.cpp \
- LayerCache.cpp \
- LayerRenderer.cpp \
- Matrix.cpp \
- OpenGLRenderer.cpp \
- Patch.cpp \
- PatchCache.cpp \
- PathCache.cpp \
- PathTessellator.cpp \
- PixelBuffer.cpp \
- Program.cpp \
- ProgramCache.cpp \
- Properties.cpp \
- RenderBufferCache.cpp \
- RenderNode.cpp \
- RenderProperties.cpp \
- ResourceCache.cpp \
- ShadowTessellator.cpp \
- SkiaCanvas.cpp \
- SkiaCanvasProxy.cpp \
- SkiaShader.cpp \
- Snapshot.cpp \
- SpotShadow.cpp \
- TessellationCache.cpp \
- TextDropShadowCache.cpp \
- Texture.cpp \
- TextureCache.cpp
-
-intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
-
-LOCAL_C_INCLUDES += \
- external/skia/src/core
-
-LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
-LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui libgui
-
-ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
- LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
- LOCAL_SHARED_LIBRARIES += libRS libRScpp
- LOCAL_C_INCLUDES += \
- $(intermediates) \
- frameworks/rs/cpp \
- frameworks/rs \
-
-endif
-
-ifndef HWUI_COMPILE_SYMBOLS
- LOCAL_CFLAGS += -fvisibility=hidden
-endif
-
-ifdef HWUI_COMPILE_FOR_PERF
- # TODO: Non-arm?
- LOCAL_CFLAGS += -fno-omit-frame-pointer -marm -mapcs
-endif
-
-ifeq (true, $(HWUI_NULL_GPU))
- LOCAL_SRC_FILES += \
- tests/nullegl.cpp \
- tests/nullgles.cpp
-
- LOCAL_CFLAGS += -DHWUI_NULL_GPU
-endif
-
-# Defaults for ATRACE_TAG and LOG_TAG for libhwui
-LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\"
-LOCAL_CFLAGS += -Wall -Wno-unused-parameter -Wunreachable-code
-LOCAL_CFLAGS += -ffast-math -O3 -Werror
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index 3c77b3d..4bd4ac8 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -85,10 +85,6 @@
virtual void getMatrix(SkMatrix* outMatrix) const = 0;
virtual void setMatrix(const SkMatrix& matrix) = 0;
- /// Like setMatrix(), but to be translated into local / view-relative coordinates
- /// rather than executed in global / device coordinates at rendering time.
- virtual void setLocalMatrix(const SkMatrix& matrix) = 0;
-
virtual void concat(const SkMatrix& matrix) = 0;
virtual void rotate(float degrees) = 0;
virtual void scale(float sx, float sy) = 0;
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index e22b0d3..54fb5f2 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -138,7 +138,7 @@
}
void CanvasState::setMatrix(const Matrix4& matrix) {
- mSnapshot->transform->load(matrix);
+ *(mSnapshot->transform) = matrix;
}
void CanvasState::concatMatrix(const SkMatrix& matrix) {
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 61c5883..bb149fe 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -168,11 +168,6 @@
mState.setMatrix(matrix);
}
-void DisplayListCanvas::setLocalMatrix(const SkMatrix& matrix) {
- addStateOp(new (alloc()) SetLocalMatrixOp(matrix));
- mState.setMatrix(matrix);
-}
-
void DisplayListCanvas::concat(const SkMatrix& matrix) {
addStateOp(new (alloc()) ConcatMatrixOp(matrix));
mState.concatMatrix(matrix);
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index 3b61904..f29e835 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -143,7 +143,6 @@
// Matrix
virtual void getMatrix(SkMatrix* outMatrix) const override { mState.getMatrix(outMatrix); }
virtual void setMatrix(const SkMatrix& matrix) override;
- virtual void setLocalMatrix(const SkMatrix& matrix) override;
virtual void concat(const SkMatrix& matrix) override;
virtual void rotate(float degrees) override;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 8ff58d4..8bb892f 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -472,7 +472,9 @@
: mMatrix(matrix) {}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.setMatrix(mMatrix);
+ // Setting a matrix on a Canvas isn't equivalent to setting a total matrix on the scene.
+ // Set a canvas-relative matrix on the renderer instead.
+ renderer.setLocalMatrix(mMatrix);
}
virtual void output(int level, uint32_t logFlags) const override {
@@ -489,25 +491,6 @@
const SkMatrix mMatrix;
};
-class SetLocalMatrixOp : public StateOp {
-public:
- SetLocalMatrixOp(const SkMatrix& matrix)
- : mMatrix(matrix) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.setLocalMatrix(mMatrix);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("SetLocalMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
- }
-
- virtual const char* name() override { return "SetLocalMatrix"; }
-
-private:
- const SkMatrix mMatrix;
-};
-
class ConcatMatrixOp : public StateOp {
public:
ConcatMatrixOp(const SkMatrix& matrix)
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 057c231..75c3ead 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -26,14 +26,12 @@
#include "Rect.h"
#include "renderstate/RenderState.h"
#include "utils/Blur.h"
-#include "utils/MathUtils.h"
#include "utils/Timing.h"
+#include <algorithm>
+#include <cutils/properties.h>
#include <SkGlyph.h>
#include <SkUtils.h>
-
-#include <cutils/properties.h>
-
#include <utils/Log.h>
#ifdef ANDROID_ENABLE_RENDERSCRIPT
@@ -118,10 +116,10 @@
uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
- mSmallCacheWidth = MathUtils::min(mSmallCacheWidth, maxTextureSize);
- mSmallCacheHeight = MathUtils::min(mSmallCacheHeight, maxTextureSize);
- mLargeCacheWidth = MathUtils::min(mLargeCacheWidth, maxTextureSize);
- mLargeCacheHeight = MathUtils::min(mLargeCacheHeight, maxTextureSize);
+ mSmallCacheWidth = std::min(mSmallCacheWidth, maxTextureSize);
+ mSmallCacheHeight = std::min(mSmallCacheHeight, maxTextureSize);
+ mLargeCacheWidth = std::min(mLargeCacheWidth, maxTextureSize);
+ mLargeCacheHeight = std::min(mLargeCacheHeight, maxTextureSize);
if (sLogFontRendererCreate) {
INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 288fed3..c9e3880 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -467,8 +467,8 @@
const int transformFlags) {
TRIGGER_STAGE(kTransformStage);
- mOutGlop->transform.ortho.load(ortho);
- mOutGlop->transform.canvas.load(canvas);
+ mOutGlop->transform.ortho = ortho;
+ mOutGlop->transform.canvas = canvas;
mOutGlop->transform.transformFlags = transformFlags;
}
@@ -615,7 +615,7 @@
shaderMatrix.loadInverse(mOutGlop->transform.canvas);
shaderMatrix.multiply(mOutGlop->transform.modelView);
} else {
- shaderMatrix.load(mOutGlop->transform.modelView);
+ shaderMatrix = mOutGlop->transform.modelView;
}
SkiaShader::store(mCaches, *mShader, shaderMatrix,
&textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData));
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index e1b0fc3..cc47f00 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -16,11 +16,11 @@
#include "Interpolator.h"
-#include <cmath>
-#include <cutils/log.h>
-
#include "utils/MathUtils.h"
+#include <algorithm>
+#include <cutils/log.h>
+
namespace android {
namespace uirenderer {
@@ -106,7 +106,7 @@
weight = modff(lutpos, &ipart);
int i1 = (int) ipart;
- int i2 = MathUtils::min(i1 + 1, (int) mSize - 1);
+ int i2 = std::min(i1 + 1, (int) mSize - 1);
LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!"
" i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f",
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 115e23c..73ebd1304 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -152,10 +152,6 @@
mType = kTypeUnknown;
}
-void Matrix4::load(const Matrix4& v) {
- *this = v;
-}
-
void Matrix4::load(const SkMatrix& v) {
memset(data, 0, sizeof(data));
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index ed54a25..ed517ac 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -114,7 +114,6 @@
void loadIdentity();
void load(const float* v);
- void load(const Matrix4& v);
void load(const SkMatrix& v);
void loadInverse(const Matrix4& v);
@@ -139,7 +138,7 @@
void multiply(const Matrix4& v) {
Matrix4 u;
u.loadMultiply(*this, v);
- load(u);
+ *this = u;
}
void multiply(float v);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7a56d42..b35c017 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1141,7 +1141,7 @@
// Transform and alpha always deferred, since they are used by state operations
// (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything)
- state.mMatrix.load(*currentMatrix);
+ state.mMatrix = *currentMatrix;
state.mAlpha = currentSnapshot()->alpha;
// always store/restore, since these are just pointers
@@ -1151,7 +1151,7 @@
}
void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
- setMatrix(state.mMatrix);
+ setGlobalMatrix(state.mMatrix);
writableSnapshot()->alpha = state.mAlpha;
writableSnapshot()->roundRectClipState = state.mRoundRectClipState;
writableSnapshot()->projectionPathMask = state.mProjectionPathMask;
@@ -2098,8 +2098,9 @@
mState.skew(sx, sy);
}
-void OpenGLRenderer::setMatrix(const Matrix4& matrix) {
- mState.setMatrix(matrix);
+void OpenGLRenderer::setLocalMatrix(const Matrix4& matrix) {
+ mState.setMatrix(mBaseTransform);
+ mState.concatMatrix(matrix);
}
void OpenGLRenderer::setLocalMatrix(const SkMatrix& matrix) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 4f75482..45662a7 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -367,8 +367,10 @@
void restore();
void restoreToCount(int saveCount);
- void getMatrix(SkMatrix* outMatrix) const { mState.getMatrix(outMatrix); }
- void setMatrix(const SkMatrix& matrix) { mState.setMatrix(matrix); }
+ void setGlobalMatrix(const Matrix4& matrix) {
+ mState.setMatrix(matrix);
+ }
+ void setLocalMatrix(const Matrix4& matrix);
void setLocalMatrix(const SkMatrix& matrix);
void concatMatrix(const SkMatrix& matrix) { mState.concatMatrix(matrix); }
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 500f9e9..b471e78 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-#include <cmath>
-
-#include <utils/Log.h>
+#include "Patch.h"
#include "Caches.h"
-#include "Patch.h"
#include "Properties.h"
#include "UvMapper.h"
#include "utils/MathUtils.h"
+#include <algorithm>
+#include <utils/Log.h>
+
namespace android {
namespace uirenderer {
@@ -189,10 +189,10 @@
const uint32_t oldQuadCount = quadCount;
quadCount++;
- x1 = MathUtils::max(x1, 0.0f);
- x2 = MathUtils::max(x2, 0.0f);
- y1 = MathUtils::max(y1, 0.0f);
- y2 = MathUtils::max(y2, 0.0f);
+ x1 = std::max(x1, 0.0f);
+ x2 = std::max(x2, 0.0f);
+ y1 = std::max(y1, 0.0f);
+ y2 = std::max(y2, 0.0f);
// Skip degenerate and transparent (empty) quads
if ((mColors[oldQuadCount] == 0) || x1 >= x2 || y1 >= y2) {
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 8fa187c..b57b8f0 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -32,6 +32,15 @@
#define DEBUG_DUMP_BUFFER()
#endif
+#include "PathTessellator.h"
+
+#include "Matrix.h"
+#include "Vector.h"
+#include "Vertex.h"
+#include "utils/MathUtils.h"
+
+#include <algorithm>
+
#include <SkPath.h>
#include <SkPaint.h>
#include <SkPoint.h>
@@ -44,12 +53,6 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-#include "PathTessellator.h"
-#include "Matrix.h"
-#include "Vector.h"
-#include "Vertex.h"
-#include "utils/MathUtils.h"
-
namespace android {
namespace uirenderer {
@@ -152,7 +155,7 @@
// always use 2 points for hairline
if (halfStrokeWidth == 0.0f) return 2;
- float threshold = MathUtils::min(inverseScaleX, inverseScaleY) * ROUND_CAP_THRESH;
+ float threshold = std::min(inverseScaleX, inverseScaleY) * ROUND_CAP_THRESH;
return MathUtils::divisionsNeededToApproximateArc(halfStrokeWidth, PI, threshold);
}
return 0;
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index b66e832..cddfb04 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -22,8 +22,12 @@
#include "Vertex.h"
#include "VertexBuffer.h"
+#include <algorithm>
#include <vector>
+class SkPath;
+class SkPaint;
+
namespace android {
namespace uirenderer {
@@ -38,7 +42,7 @@
: thresholdSquared(pixelThreshold * pixelThreshold)
, sqrInvScaleX(invScaleX * invScaleX)
, sqrInvScaleY(invScaleY * invScaleY)
- , thresholdForConicQuads(pixelThreshold * MathUtils::min(invScaleX, invScaleY) / 2.0f) {
+ , thresholdForConicQuads(pixelThreshold * std::min(invScaleX, invScaleY) / 2.0f) {
};
const float thresholdSquared;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 48da3e8..73c0107 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -532,7 +532,7 @@
if (properties().getProjectBackwards()) {
// composited projectee, flag for out of order draw, save matrix, and store in proj surface
opState->mSkipInOrderDraw = true;
- opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
+ opState->mTransformFromCompositingAncestor = localTransformFromProjectionSurface;
compositedChildrenOfProjectionSurface->push_back(opState);
} else {
// standard in order draw
@@ -719,7 +719,7 @@
// Apply the base transform of the parent of the 3d children. This isolates
// 3d children of the current chunk from transformations made in previous chunks.
int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
- renderer.setMatrix(initialTransform);
+ renderer.setGlobalMatrix(initialTransform);
/**
* Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index ceca607..0c5b4b7 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -74,7 +74,6 @@
virtual void getMatrix(SkMatrix* outMatrix) const override;
virtual void setMatrix(const SkMatrix& matrix) override;
- virtual void setLocalMatrix(const SkMatrix& matrix) override { this->setMatrix(matrix); }
virtual void concat(const SkMatrix& matrix) override;
virtual void rotate(float degrees) override;
virtual void scale(float sx, float sy) override;
@@ -321,7 +320,7 @@
}
if (NULL == mSaveStack.get()) {
- mSaveStack.reset(SkNEW_ARGS(SkDeque, (sizeof(struct SaveRec), 8)));
+ mSaveStack.reset(new SkDeque(sizeof(struct SaveRec), 8));
}
SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 5e6d774..41152dc 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -142,7 +142,7 @@
const SkPaint* paint) {
// TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
- mCanvas->setLocalMatrix(SkMatrix::I());
+ mCanvas->setMatrix(SkMatrix::I());
mCanvas->drawBitmap(bitmap, left, top, paint);
mCanvas->restore();
}
@@ -192,9 +192,7 @@
}
void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
- // SkCanvas setMatrix() is relative to the Canvas origin, but OpenGLRenderer's
- // setMatrix() is relative to device origin; call setLocalMatrix() instead.
- mCanvas->setLocalMatrix(matrix);
+ mCanvas->setMatrix(matrix);
}
void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index fd077d9..ca19a42 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -58,7 +58,7 @@
, mViewportData(s->mViewportData)
, mRelativeLightCenter(s->mRelativeLightCenter) {
if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
- mTransformRoot.load(*s->transform);
+ mTransformRoot = *s->transform;
transform = &mTransformRoot;
} else {
transform = s->transform;
@@ -190,8 +190,7 @@
state->highPriority = highPriority;
// store the inverse drawing matrix
- Matrix4 roundRectDrawingMatrix;
- roundRectDrawingMatrix.load(getOrthoMatrix());
+ Matrix4 roundRectDrawingMatrix = getOrthoMatrix();
roundRectDrawingMatrix.multiply(*transform);
state->matrix.loadInverse(roundRectDrawingMatrix);
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index eaf0303..9b0a1aa 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -46,17 +46,18 @@
#define TRANSFORMED_PENUMBRA_ALPHA 1.0f
#define TRANSFORMED_UMBRA_ALPHA 0.0f
+#include "SpotShadow.h"
+
+#include "ShadowTessellator.h"
+#include "Vertex.h"
+#include "VertexBuffer.h"
+#include "utils/MathUtils.h"
+
#include <algorithm>
#include <math.h>
#include <stdlib.h>
#include <utils/Log.h>
-#include "ShadowTessellator.h"
-#include "SpotShadow.h"
-#include "Vertex.h"
-#include "VertexBuffer.h"
-#include "utils/MathUtils.h"
-
// TODO: After we settle down the new algorithm, we can remove the old one and
// its utility functions.
// Right now, we still need to keep it for comparison purpose and future expansion.
@@ -543,7 +544,7 @@
}
float ratioVI = outlineData[i].radius / distOutline;
- minRaitoVI = MathUtils::min(minRaitoVI, ratioVI);
+ minRaitoVI = std::min(minRaitoVI, ratioVI);
if (ratioVI >= (1 - FAKE_UMBRA_SIZE_RATIO)) {
ratioVI = (1 - FAKE_UMBRA_SIZE_RATIO);
}
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 9be4d84..c0373ac 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_HWUI_VERTEX_BUFFER_H
#define ANDROID_HWUI_VERTEX_BUFFER_H
-#include "utils/MathUtils.h"
+#include <algorithm>
namespace android {
namespace uirenderer {
@@ -129,10 +129,10 @@
unsigned int getSize() const { return mByteCount; }
unsigned int getIndexCount() const { return mIndexCount; }
void updateIndexCount(unsigned int newCount) {
- mIndexCount = MathUtils::min(newCount, mAllocatedIndexCount);
+ mIndexCount = std::min(newCount, mAllocatedIndexCount);
}
void updateVertexCount(unsigned int newCount) {
- mVertexCount = MathUtils::min(newCount, mAllocatedVertexCount);
+ mVertexCount = std::min(newCount, mAllocatedVertexCount);
}
MeshFeatureFlags getMeshFeatureFlags() const { return mMeshFeatureFlags; }
void setMeshFeatureFlags(int flags) {
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 1e39bfa..b5ed9e6 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -19,6 +19,8 @@
#include "renderthread/EglManager.h"
#include "utils/GLUtils.h"
+#include <algorithm>
+
namespace android {
namespace uirenderer {
@@ -320,7 +322,7 @@
GLsizei elementsCount = mesh.elementCount;
const GLbyte* vertexData = static_cast<const GLbyte*>(vertices.position);
while (elementsCount > 0) {
- GLsizei drawCount = MathUtils::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
+ GLsizei drawCount = std::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
// rebind pointers without forcing, since initial bind handled above
meshState().bindPositionVertexPointer(false, vertexData, vertices.stride);
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 9c3787c..8d20f21 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -16,6 +16,7 @@
#ifndef MATHUTILS_H
#define MATHUTILS_H
+#include <algorithm>
#include <math.h>
namespace android {
@@ -82,18 +83,8 @@
}
template<typename T>
- static inline T max(T a, T b) {
- return a > b ? a : b;
- }
-
- template<typename T>
- static inline T min(T a, T b) {
- return a < b ? a : b;
- }
-
- template<typename T>
static inline T clamp(T a, T minValue, T maxValue) {
- return min(max(a, minValue), maxValue);
+ return std::min(std::max(a, minValue), maxValue);
}
inline static float lerp(float v1, float v2, float t) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 328ee35..36efead 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -256,6 +256,8 @@
private void continuePulsing(int reason) {
if (mHost.isPulsingBlocked()) {
+ mPulsing = false;
+ mWakeLock.release();
return;
}
mHost.pulseWhileDozing(new DozeHost.PulseCallback() {
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 947c19c..00ac5f9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents.views;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.TaskStackBuilder;
import android.content.Context;
@@ -31,6 +32,8 @@
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.SparseArray;
+import android.view.AppTransitionAnimationSpec;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowInsets;
@@ -60,6 +63,8 @@
private static final String TAG = "RecentsView";
+ private static final boolean ADD_HEADER_BITMAP = true;
+
/** The RecentsView callbacks */
public interface RecentsViewCallbacks {
public void onTaskViewClicked();
@@ -443,62 +448,158 @@
}
}
- private void postDrawHeaderThumbnailTransitionRunnable(final TaskView tv, final int offsetX,
- final int offsetY, final TaskViewTransform transform,
+ private void postDrawHeaderThumbnailTransitionRunnable(final TaskStackView view,
+ final TaskView clickedView, final int offsetX, final int offsetY,
+ final float stackScroll,
final ActivityOptions.OnAnimationStartedListener animStartedListener) {
Runnable r = new Runnable() {
@Override
public void run() {
- // Disable any focused state before we draw the header
- if (tv.isFocusedTask()) {
- tv.unsetFocusedTask();
- }
+ overrideDrawHeaderThumbnailTransition(view, clickedView, offsetX, offsetY,
+ stackScroll, animStartedListener);
- float scale = tv.getScaleX();
- int fromHeaderWidth = (int) (tv.mHeaderView.getMeasuredWidth() * scale);
- int fromHeaderHeight = (int) (tv.mHeaderView.getMeasuredHeight() * scale);
-
- Bitmap b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
- 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);
- }
- b = b.createAshmemBitmap();
- int[] pts = new int[2];
- tv.getLocationOnScreen(pts);
- try {
- WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionAspectScaledThumb(b,
- pts[0] + offsetX,
- pts[1] + offsetY,
- transform.rect.width(),
- transform.rect.height(),
- new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data)
- throws RemoteException {
- post(new Runnable() {
- @Override
- public void run() {
- if (animStartedListener != null) {
- animStartedListener.onAnimationStarted();
- }
- }
- });
- }
- }, true);
- } catch (RemoteException e) {
- Log.w(TAG, "Error overriding app transition", e);
- }
}
};
+
mCb.runAfterPause(r);
}
+
+ private void overrideDrawHeaderThumbnailTransition(TaskStackView stackView,
+ TaskView clickedTask, int offsetX, int offsetY, float stackScroll,
+ final ActivityOptions.OnAnimationStartedListener animStartedListener) {
+ List<AppTransitionAnimationSpec> specs = getAppTransitionAnimationSpecs(stackView,
+ clickedTask, offsetX, offsetY, stackScroll);
+ if (specs == null) {
+ return;
+ }
+
+ IRemoteCallback.Stub callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ if (animStartedListener != null) {
+ animStartedListener.onAnimationStarted();
+ }
+ }
+ });
+ }
+ };
+
+ AppTransitionAnimationSpec[] specsArray =
+ new AppTransitionAnimationSpec[specs.size()];
+ try {
+ WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionMultiThumb(
+ specs.toArray(specsArray), callback, true /* scaleUp */);
+
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error overriding app transition", e);
+ }
+ }
+
+ private List<AppTransitionAnimationSpec> getAppTransitionAnimationSpecs(TaskStackView stackView,
+ TaskView clickedTask, int offsetX, int offsetY, float stackScroll) {
+ final int targetStackId = clickedTask.getTask().key.stackId;
+ if (targetStackId != ActivityManager.FREEFORM_WORKSPACE_STACK_ID
+ && targetStackId != ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID) {
+ return null;
+ }
+ // If this is a full screen stack, the transition will be towards the single, full screen
+ // task. We only need the transition spec for this task.
+ List<AppTransitionAnimationSpec> specs = new ArrayList<>();
+ if (targetStackId == ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID) {
+ specs.add(createThumbnailHeaderAnimationSpec(
+ stackView, offsetX, offsetY, stackScroll, clickedTask,
+ clickedTask.getTask().key.id, ADD_HEADER_BITMAP));
+ return specs;
+ }
+ // This is a free form stack or full screen stack, so there will be multiple windows
+ // animating from thumbnails. We need transition animation specs for all of them.
+
+ // We will use top and bottom task views as a base for tasks, that aren't visible on the
+ // screen. This is necessary for cascade recents list, where some of the tasks might be
+ // hidden.
+ List<TaskView> taskViews = stackView.getTaskViews();
+ int childCount = taskViews.size();
+ TaskView topChild = taskViews.get(0);
+ TaskView bottomChild = taskViews.get(childCount - 1);
+ SparseArray<TaskView> taskViewsByTaskId = new SparseArray<>();
+ for (int i = 0; i < childCount; i++) {
+ TaskView taskView = taskViews.get(i);
+ taskViewsByTaskId.put(taskView.getTask().key.id, taskView);
+ }
+
+ TaskStack stack = stackView.getStack();
+ // We go through all tasks now and for each generate transition animation spec. If there is
+ // a view associated with a task, we use that view as a base for the animation. If there
+ // isn't, we use bottom or top view, depending on which one would be closer to the task
+ // view if it existed.
+ ArrayList<Task> tasks = stack.getTasks();
+ boolean passedClickedTask = false;
+ for (int i = 0, n = tasks.size(); i < n; i++) {
+ Task task = tasks.get(i);
+ TaskView taskView = taskViewsByTaskId.get(task.key.id);
+ if (taskView != null) {
+ specs.add(createThumbnailHeaderAnimationSpec(stackView, offsetX, offsetY,
+ stackScroll, taskView, taskView.getTask().key.id, ADD_HEADER_BITMAP));
+ if (taskView == clickedTask) {
+ passedClickedTask = true;
+ }
+ } else {
+ taskView = passedClickedTask ? bottomChild : topChild;
+ specs.add(createThumbnailHeaderAnimationSpec(stackView, offsetX, offsetY,
+ stackScroll, taskView, task.key.id, !ADD_HEADER_BITMAP));
+ }
+ }
+
+ return specs;
+ }
+
+ private AppTransitionAnimationSpec createThumbnailHeaderAnimationSpec(TaskStackView stackView,
+ int offsetX, int offsetY, float stackScroll, TaskView tv, int taskId,
+ boolean addHeaderBitmap) {
+ // Disable any focused state before we draw the header
+ // Upfront the processing of the thumbnail
+ if (tv.isFocusedTask()) {
+ tv.unsetFocusedTask();
+ }
+ TaskViewTransform transform = new TaskViewTransform();
+ transform = stackView.getStackAlgorithm().getStackTransform(tv.mTask, stackScroll,
+ transform, null);
+
+ float scale = tv.getScaleX();
+ int fromHeaderWidth = (int) (tv.mHeaderView.getMeasuredWidth() * scale);
+ int fromHeaderHeight = (int) (tv.mHeaderView.getMeasuredHeight() * scale);
+
+ Bitmap b = null;
+ if (addHeaderBitmap) {
+ b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
+ 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);
+
+ }
+ b = b.createAshmemBitmap();
+ }
+
+ int[] pts = new int[2];
+ tv.getLocationOnScreen(pts);
+
+ final int left = pts[0] + offsetX;
+ final int top = pts[1] + offsetY;
+ final Rect rect = new Rect(left, top, left + transform.rect.width(),
+ top + transform.rect.height());
+
+ return new AppTransitionAnimationSpec(taskId, b, rect);
+ }
+
/**** TaskStackView.TaskStackCallbacks Implementation ****/
@Override
@@ -521,12 +622,10 @@
// and then offset to the expected transform rect, but bound this to just
// outside the display rect (to ensure we don't animate from too far away)
sourceView = stackView;
- transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
offsetX = transform.rect.left;
offsetY = mConfig.displayRect.height();
} else {
sourceView = tv.mThumbnailView;
- transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
}
// Compute the thumbnail to scale up from
@@ -553,10 +652,8 @@
}
};
}
- if (tv != null) {
- postDrawHeaderThumbnailTransitionRunnable(tv, offsetX, offsetY, transform,
- animStartedListener);
- }
+ postDrawHeaderThumbnailTransitionRunnable(stackView, tv, offsetX, offsetY, stackScroll,
+ animStartedListener);
if (mConfig.multiStackEnabled) {
opts = ActivityOptions.makeCustomAnimation(sourceView.getContext(),
R.anim.recents_from_unknown_enter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 4d3e57e..3ff69c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -104,13 +104,11 @@
* Aborts pulsing immediately.
*/
public void abortPulsing() {
- mHandler.removeCallbacks(mPulseIn);
- abortAnimations();
+ cancelPulsing();
if (mDozing) {
mScrimController.setDozeBehindAlpha(1f);
mScrimController.setDozeInFrontAlpha(1f);
}
- mPulseCallback = null;
}
public void onScreenTurnedOn() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index d74c5b0..cb5c125 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -20,7 +20,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.AppGlobals;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -29,13 +28,10 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AttributeSet;
@@ -49,6 +45,7 @@
import android.widget.LinearLayout;
import android.widget.Toast;
+import com.android.internal.content.PackageMonitor;
import com.android.systemui.R;
import java.util.List;
@@ -75,6 +72,8 @@
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final LayoutInflater mLayoutInflater;
+ private final AppPackageMonitor mAppPackageMonitor;
+
// This view has two roles:
// 1) If the drag started outside the pinned apps list, it is a placeholder icon with a null
@@ -106,6 +105,7 @@
mPackageManager = context.getPackageManager();
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
mLayoutInflater = LayoutInflater.from(context);
+ mAppPackageMonitor = new AppPackageMonitor();
// Dragging an icon removes and adds back the dragged icon. Use the layout transitions to
// trigger animation. By default all transitions animate, so turn off the unneeded ones.
@@ -121,6 +121,76 @@
setLayoutTransition(transition);
}
+ // Monitor that catches events like "app uninstalled".
+ private class AppPackageMonitor extends PackageMonitor {
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ postRemoveIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
+ super.onPackageRemoved(packageName, uid);
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ postRemoveIfUnlauncheable(packageName, new UserHandle(getChangingUserId()));
+ super.onPackageModified(packageName);
+ }
+
+ @Override
+ public void onPackagesAvailable(String[] packages) {
+ if (isReplacing()) {
+ UserHandle user = new UserHandle(getChangingUserId());
+
+ for (String packageName : packages) {
+ postRemoveIfUnlauncheable(packageName, user);
+ }
+ }
+ super.onPackagesAvailable(packages);
+ }
+
+ @Override
+ public void onPackagesUnavailable(String[] packages) {
+ if (!isReplacing()) {
+ UserHandle user = new UserHandle(getChangingUserId());
+
+ for (String packageName : packages) {
+ postRemoveIfUnlauncheable(packageName, user);
+ }
+ }
+ super.onPackagesUnavailable(packages);
+ }
+ }
+
+ private void postRemoveIfUnlauncheable(final String packageName, final UserHandle user) {
+ // This method doesn't necessarily get called in the main thread. Redirect the call into
+ // the main thread.
+ post(new Runnable() {
+ @Override
+ public void run() {
+ if (!isAttachedToWindow()) return;
+ removeIfUnlauncheable(packageName, user);
+ }
+ });
+ }
+
+ private void removeIfUnlauncheable(String packageName, UserHandle user) {
+ long appUserSerialNumber = mUserManager.getSerialNumberForUser(user);
+
+ // Remove icons for all apps that match a package that perhaps became unlauncheable.
+ for(int i = sAppsModel.getAppCount() - 1; i >= 0; --i) {
+ AppInfo appInfo = sAppsModel.getApp(i);
+ if (appInfo.getUserSerialNumber() != appUserSerialNumber) continue;
+
+ ComponentName appComponentName = appInfo.getComponentName();
+ if (!appComponentName.getPackageName().equals(packageName)) continue;
+
+ if (sAppsModel.buildAppLaunchIntent(appComponentName, user) != null) continue;
+
+ removeViewAt(i);
+ sAppsModel.removeApp(i);
+ sAppsModel.savePrefs();
+ }
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -145,12 +215,15 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(mBroadcastReceiver, filter);
+
+ mAppPackageMonitor.register(mContext, null, UserHandle.ALL, true);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mContext.unregisterReceiver(mBroadcastReceiver);
+ mAppPackageMonitor.unregister();
}
/**
@@ -470,7 +543,6 @@
ComponentName component = appInfo.getComponentName();
long appUserSerialNumber = appInfo.getUserSerialNumber();
-
UserHandle appUser = mUserManager.getUserForSerialNumber(appUserSerialNumber);
if (appUser == null) {
Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
@@ -478,7 +550,12 @@
" because its user doesn't exist.");
return;
}
- int appUserId = appUser.getIdentifier();
+
+ Intent launchIntent = sAppsModel.buildAppLaunchIntent(component, appUser);
+ if (launchIntent == null) {
+ Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ return;
+ }
// Play a scale-up animation while launching the activity.
// TODO: Consider playing a different animation, or no animation, if the activity is
@@ -489,54 +566,9 @@
ActivityOptions opts =
ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getWidth(), v.getHeight());
Bundle optsBundle = opts.toBundle();
-
- // Launch the activity. This code is based on LauncherAppsService.startActivityAsUser code.
- Intent launchIntent = new Intent(Intent.ACTION_MAIN);
- launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
launchIntent.setSourceBounds(sourceBounds);
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- launchIntent.setPackage(component.getPackageName());
- IPackageManager pm = AppGlobals.getPackageManager();
- try {
- ActivityInfo info = pm.getActivityInfo(component, 0, appUserId);
- if (info == null) {
- Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Can't start activity " + component + " because it's not installed.");
- return;
- }
-
- if (!info.exported) {
- Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Can't start activity " + component + " because it doesn't have 'exported' attribute.");
- return;
- }
- } catch (RemoteException e) {
- Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Failed to get activity info for " + component, e);
- return;
- }
-
- // Check that the component actually has Intent.CATEGORY_LAUCNCHER
- // as calling startActivityAsUser ignores the category and just
- // resolves based on the component if present.
- List<ResolveInfo> apps = getContext().getPackageManager().queryIntentActivitiesAsUser(launchIntent,
- 0 /* flags */, appUserId);
- final int size = apps.size();
- for (int i = 0; i < size; ++i) {
- ActivityInfo activityInfo = apps.get(i).activityInfo;
- if (activityInfo.packageName.equals(component.getPackageName()) &&
- activityInfo.name.equals(component.getClassName())) {
- // Found an activity with category launcher that matches
- // this component so ok to launch.
- launchIntent.setComponent(component);
- mContext.startActivityAsUser(launchIntent, optsBundle, appUser);
- return;
- }
- }
-
- Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Attempt to launch activity without category Intent.CATEGORY_LAUNCHER " + component);
+ mContext.startActivityAsUser(launchIntent, optsBundle, appUser);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
index b8764cf..c4c31fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
@@ -16,16 +16,23 @@
package com.android.systemui.statusbar.phone;
+import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -94,6 +101,58 @@
}
}
+ @VisibleForTesting
+ protected IPackageManager getPackageManager() {
+ return AppGlobals.getPackageManager();
+ }
+
+ // Returns a launch intent for a given component, or null if the component is unlauncheable.
+ public Intent buildAppLaunchIntent(ComponentName component, UserHandle appUser) {
+ int appUserId = appUser.getIdentifier();
+
+ // This code is based on LauncherAppsService.startActivityAsUser code.
+ Intent launchIntent = new Intent(Intent.ACTION_MAIN);
+ launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ launchIntent.setPackage(component.getPackageName());
+
+ try {
+ ActivityInfo info = getPackageManager().getActivityInfo(component, 0, appUserId);
+ if (info == null) {
+ Log.e(TAG, "Activity " + component + " is not installed.");
+ return null;
+ }
+
+ if (!info.exported) {
+ Log.e(TAG, "Activity " + component + " doesn't have 'exported' attribute.");
+ return null;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get activity info for " + component, e);
+ return null;
+ }
+
+ // Check that the component actually has Intent.CATEGORY_LAUNCHER
+ // as calling startActivityAsUser ignores the category and just
+ // resolves based on the component if present.
+ List<ResolveInfo> apps = mContext.getPackageManager().queryIntentActivitiesAsUser(launchIntent,
+ 0 /* flags */, appUserId);
+ final int size = apps.size();
+ for (int i = 0; i < size; ++i) {
+ ActivityInfo activityInfo = apps.get(i).activityInfo;
+ if (activityInfo.packageName.equals(component.getPackageName()) &&
+ activityInfo.name.equals(component.getClassName())) {
+ // Found an activity with category launcher that matches
+ // this component so ok to launch.
+ launchIntent.setComponent(component);
+ return launchIntent;
+ }
+ }
+
+ Log.e(TAG, "Activity doesn't have category Intent.CATEGORY_LAUNCHER " + component);
+ return null;
+ }
+
/**
* Reinitializes the model for a new user.
*/
@@ -199,6 +258,10 @@
/** Loads the list of apps from SharedPreferences. */
private void loadAppsFromPrefs() {
+ UserManager mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+
+ boolean hadUnlauncheableApps = false;
+
int appCount = mPrefs.getInt(userPrefixed(APP_COUNT_PREF), -1);
for (int i = 0; i < appCount; i++) {
String prefValue = mPrefs.getString(prefNameForApp(i), null);
@@ -214,8 +277,15 @@
// Couldn't find the saved state. Just skip this item.
continue;
}
- mApps.add(new AppInfo(componentName, userSerialNumber));
+ UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber);
+ if (appUser != null && buildAppLaunchIntent(componentName, appUser) != null) {
+ mApps.add(new AppInfo(componentName, userSerialNumber));
+ } else {
+ hadUnlauncheableApps = true;
+ }
}
+
+ if (hadUnlauncheableApps) savePrefs();
}
/** Adds the first few apps from the owner profile. Used for demo purposes. */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
index 62213ab..4d0e28b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
@@ -16,21 +16,26 @@
package com.android.systemui.statusbar.phone;
+import org.mockito.InOrder;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.test.AndroidTestCase;
@@ -45,6 +50,7 @@
/** Tests for the data model for the navigation bar app icons. */
public class NavigationBarAppsModelTest extends AndroidTestCase {
private PackageManager mMockPackageManager;
+ private IPackageManager mMockIPackageManager;
private SharedPreferences mMockPrefs;
private SharedPreferences.Editor mMockEdit;
private UserManager mMockUserManager;
@@ -61,6 +67,7 @@
final Context context = mock(Context.class);
mMockPackageManager = mock(PackageManager.class);
+ mMockIPackageManager = mock(IPackageManager.class);
mMockPrefs = mock(SharedPreferences.class);
mMockEdit = mock(SharedPreferences.Editor.class);
mMockUserManager = mock(UserManager.class);
@@ -78,8 +85,71 @@
when(mMockPrefs.edit()).thenReturn(mMockEdit);
when(mMockUserManager.getSerialNumberForUser(new UserHandle(2))).thenReturn(22L);
+ when(mMockUserManager.getUserForSerialNumber(45L)).thenReturn(new UserHandle(4));
+ when(mMockUserManager.getUserForSerialNumber(239L)).thenReturn(new UserHandle(5));
- mModel = new NavigationBarAppsModel(context);
+ mModel = new NavigationBarAppsModel(context) {
+ @Override
+ protected IPackageManager getPackageManager() {
+ return mMockIPackageManager;
+ }
+ };
+ }
+
+ /** Tests buildAppLaunchIntent(). */
+ public void testBuildAppLaunchIntent() {
+ ActivityInfo mockNonExportedActivityInfo = new ActivityInfo();
+ mockNonExportedActivityInfo.exported = false;
+ ActivityInfo mockExportedActivityInfo = new ActivityInfo();
+ mockExportedActivityInfo.exported = true;
+ try {
+ when(mMockIPackageManager.getActivityInfo(
+ new ComponentName("package1", "class1"), 0, 4)).
+ thenReturn(mockNonExportedActivityInfo);
+ when(mMockIPackageManager.getActivityInfo(
+ new ComponentName("package2", "class2"), 0, 5)).
+ thenThrow(new RemoteException());
+ when(mMockIPackageManager.getActivityInfo(
+ new ComponentName("package3", "class3"), 0, 6)).
+ thenReturn(mockExportedActivityInfo);
+ when(mMockIPackageManager.getActivityInfo(
+ new ComponentName("package4", "class4"), 0, 7)).
+ thenReturn(mockExportedActivityInfo);
+ } catch (RemoteException e) {
+ fail("RemoteException can't happen in the test, but it happened.");
+ }
+
+ // Assume some installed activities.
+ ActivityInfo ai0 = new ActivityInfo();
+ ai0.packageName = "package0";
+ ai0.name = "class0";
+ ActivityInfo ai1 = new ActivityInfo();
+ ai1.packageName = "package4";
+ ai1.name = "class4";
+ ResolveInfo ri0 = new ResolveInfo();
+ ri0.activityInfo = ai0;
+ ResolveInfo ri1 = new ResolveInfo();
+ ri1.activityInfo = ai1;
+ when(mMockPackageManager
+ .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
+ .thenReturn(Arrays.asList(ri0, ri1));
+
+ // Unlauncheable (for various reasons) apps.
+ assertEquals(null, mModel.buildAppLaunchIntent(
+ new ComponentName("package0", "class0"), new UserHandle(3)));
+ assertEquals(null, mModel.buildAppLaunchIntent(
+ new ComponentName("package1", "class1"), new UserHandle(4)));
+ assertEquals(null, mModel.buildAppLaunchIntent(
+ new ComponentName("package2", "class2"), new UserHandle(5)));
+ assertEquals(null, mModel.buildAppLaunchIntent(
+ new ComponentName("package3", "class3"), new UserHandle(6)));
+
+ // A launcheable app.
+ Intent intent = mModel.buildAppLaunchIntent(
+ new ComponentName("package4", "class4"), new UserHandle(7));
+ assertNotNull(intent);
+ assertEquals(new ComponentName("package4", "class4"), intent.getComponent());
+ assertEquals("package4", intent.getPackage());
}
/** Initializes the model from SharedPreferences for a few app activites. */
@@ -93,6 +163,39 @@
when(mMockPrefs.getString("22|app_2", null)).thenReturn("package2/class2");
when(mMockPrefs.getLong("22|app_user_2", -1)).thenReturn(239L);
+ ActivityInfo mockActivityInfo = new ActivityInfo();
+ mockActivityInfo.exported = true;
+ try {
+ when(mMockIPackageManager.getActivityInfo(
+ new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
+ when(mMockIPackageManager.getActivityInfo(
+ new ComponentName("package1", "class1"), 0, 4)).thenReturn(mockActivityInfo);
+ when(mMockIPackageManager.getActivityInfo(
+ new ComponentName("package2", "class2"), 0, 5)).thenReturn(mockActivityInfo);
+ } catch (RemoteException e) {
+ fail("RemoteException can't happen in the test, but it happened.");
+ }
+
+ // Assume some installed activities.
+ ActivityInfo ai0 = new ActivityInfo();
+ ai0.packageName = "package0";
+ ai0.name = "class0";
+ ActivityInfo ai1 = new ActivityInfo();
+ ai1.packageName = "package1";
+ ai1.name = "class1";
+ ActivityInfo ai2 = new ActivityInfo();
+ ai2.packageName = "package2";
+ ai2.name = "class2";
+ ResolveInfo ri0 = new ResolveInfo();
+ ri0.activityInfo = ai0;
+ ResolveInfo ri1 = new ResolveInfo();
+ ri1.activityInfo = ai1;
+ ResolveInfo ri2 = new ResolveInfo();
+ ri2.activityInfo = ai2;
+ when(mMockPackageManager
+ .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
+ .thenReturn(Arrays.asList(ri0, ri1, ri2));
+
mModel.setCurrentUser(2);
}
@@ -133,6 +236,15 @@
assertEquals(22L, mModel.getApp(0).getUserSerialNumber());
assertEquals("package2/class2", mModel.getApp(1).getComponentName().flattenToString());
assertEquals(22L, mModel.getApp(1).getUserSerialNumber());
+ InOrder order = inOrder(mMockEdit);
+ order.verify(mMockEdit).apply();
+ order.verify(mMockEdit).putInt("22|app_count", 2);
+ order.verify(mMockEdit).putString("22|app_0", "package1/class1");
+ order.verify(mMockEdit).putLong("22|app_user_0", 22L);
+ order.verify(mMockEdit).putString("22|app_1", "package2/class2");
+ order.verify(mMockEdit).putLong("22|app_user_1", 22L);
+ order.verify(mMockEdit).apply();
+ verifyNoMoreInteractions(mMockEdit);
}
/** Tests initializing the model if one of the prefs is missing. */
@@ -145,11 +257,72 @@
// But assume one pref is missing.
when(mMockPrefs.getString("22|app_1", null)).thenReturn(null);
+ ActivityInfo mockActivityInfo = new ActivityInfo();
+ mockActivityInfo.exported = true;
+ try {
+ when(mMockIPackageManager.getActivityInfo(
+ new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
+ } catch (RemoteException e) {
+ fail("RemoteException can't happen in the test, but it happened.");
+ }
+
+ ActivityInfo ai0 = new ActivityInfo();
+ ai0.packageName = "package0";
+ ai0.name = "class0";
+ ResolveInfo ri0 = new ResolveInfo();
+ ri0.activityInfo = ai0;
+ when(mMockPackageManager
+ .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
+ .thenReturn(Arrays.asList(ri0));
+
// Initializing the model should load from prefs and skip the missing one.
mModel.setCurrentUser(2);
assertEquals(1, mModel.getAppCount());
assertEquals("package0/class0", mModel.getApp(0).getComponentName().flattenToString());
assertEquals(239L, mModel.getApp(0).getUserSerialNumber());
+ verifyNoMoreInteractions(mMockEdit);
+ }
+
+ /** Tests initializing the model if one of the apps is unlauncheable. */
+ public void testInitializeWithUnlauncheableApp() {
+ // Assume two apps are nominally stored.
+ when(mMockPrefs.getInt("22|app_count", -1)).thenReturn(2);
+ when(mMockPrefs.getString("22|app_0", null)).thenReturn("package0/class0");
+ when(mMockPrefs.getLong("22|app_user_0", -1)).thenReturn(239L);
+ when(mMockPrefs.getString("22|app_1", null)).thenReturn("package1/class1");
+ when(mMockPrefs.getLong("22|app_user_1", -1)).thenReturn(45L);
+
+ ActivityInfo mockActivityInfo = new ActivityInfo();
+ mockActivityInfo.exported = true;
+ try {
+ when(mMockIPackageManager.getActivityInfo(
+ new ComponentName("package0", "class0"), 0, 5)).thenReturn(mockActivityInfo);
+ } catch (RemoteException e) {
+ fail("RemoteException can't happen in the test, but it happened.");
+ }
+
+ ActivityInfo ai0 = new ActivityInfo();
+ ai0.packageName = "package0";
+ ai0.name = "class0";
+ ResolveInfo ri0 = new ResolveInfo();
+ ri0.activityInfo = ai0;
+ when(mMockPackageManager
+ .queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
+ .thenReturn(Arrays.asList(ri0));
+
+ // Initializing the model should load from prefs and skip the unlauncheable one.
+ mModel.setCurrentUser(2);
+ assertEquals(1, mModel.getAppCount());
+ assertEquals("package0/class0", mModel.getApp(0).getComponentName().flattenToString());
+ assertEquals(239L, mModel.getApp(0).getUserSerialNumber());
+
+ // Once an unlauncheable app is detected, the model should save all apps excluding the
+ // unlauncheable one.
+ verify(mMockEdit).putInt("22|app_count", 1);
+ verify(mMockEdit).putString("22|app_0", "package0/class0");
+ verify(mMockEdit).putLong("22|app_user_0", 239L);
+ verify(mMockEdit).apply();
+ verifyNoMoreInteractions(mMockEdit);
}
/** Tests saving the model to SharedPreferences. */
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f3ea1c4..b6cd477 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16022,7 +16022,7 @@
"Call does not support special user #" + targetUserId);
}
// Check shell permission
- if (callingUid == Process.SHELL_UID && targetUserId >= UserHandle.USER_OWNER) {
+ if (callingUid == Process.SHELL_UID && targetUserId >= UserHandle.USER_SYSTEM) {
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES,
targetUserId)) {
throw new SecurityException("Shell does not have permission to access user "
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index d54c16f..24b90d8 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -513,7 +513,7 @@
configDestroy = false;
keysPaused = false;
inHistory = false;
- visible = true;
+ visible = false;
nowVisible = false;
idle = false;
hasBeenLaunched = false;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d0f68f0..9bf4f5f 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -975,7 +975,9 @@
pw.println("Battery stats (batterystats) dump options:");
pw.println(" [--checkin] [--history] [--history-start] [--charged] [-c]");
pw.println(" [--daily] [--reset] [--write] [--new-daily] [--read-daily] [-h] [<package.name>]");
- pw.println(" --checkin: format output for a checkin report.");
+ pw.println(" --checkin: generate output for a checkin report; will write (and clear) the");
+ pw.println(" last old completed stats when they had been reset.");
+ pw.println(" --c: write the current stats in checkin format.");
pw.println(" --history: show only history data.");
pw.println(" --history-start <num>: show only history data starting at given time offset.");
pw.println(" --charged: only output data since last charged.");
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index c998c2c..292aff9 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -790,7 +790,7 @@
for (AccountAndUser account : accounts) {
// If userId is specified, do not sync accounts of other users
- if (userId >= UserHandle.USER_OWNER && account.userId >= UserHandle.USER_OWNER
+ if (userId >= UserHandle.USER_SYSTEM && account.userId >= UserHandle.USER_SYSTEM
&& userId != account.userId) {
continue;
}
diff --git a/services/core/java/com/android/server/location/LocationBlacklist.java b/services/core/java/com/android/server/location/LocationBlacklist.java
index 6f22689..3f3f828 100644
--- a/services/core/java/com/android/server/location/LocationBlacklist.java
+++ b/services/core/java/com/android/server/location/LocationBlacklist.java
@@ -50,7 +50,7 @@
private String[] mWhitelist = new String[0];
private String[] mBlacklist = new String[0];
- private int mCurrentUserId = UserHandle.USER_OWNER;
+ private int mCurrentUserId = UserHandle.USER_SYSTEM;
public LocationBlacklist(Context context, Handler handler) {
super(handler);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 47729f9..153bd3b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1925,8 +1925,7 @@
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
- mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
- mSdkVersion, mOnlyCore);
+ mRestoredSettings = mSettings.readLPw(sUserManager.getUsers(false));
String customResolverActivity = Resources.getSystem().getString(
R.string.config_customResolverActivity);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 943e649..715dd2c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -29,6 +29,7 @@
import static android.os.Process.PACKAGE_INFO_GID;
import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION;
+import android.annotation.NonNull;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.IntentFilterVerificationInfo;
@@ -2486,8 +2487,7 @@
}
}
- boolean readLPw(PackageManagerService service, List<UserInfo> users, int sdkVersion,
- boolean onlyCore) {
+ boolean readLPw(@NonNull List<UserInfo> users) {
FileInputStream str = null;
if (mBackupSettingsFilename.exists()) {
try {
@@ -2583,7 +2583,7 @@
String userStr = parser.getAttributeValue(null, ATTR_USER);
String codeStr = parser.getAttributeValue(null, ATTR_CODE);
if (name != null) {
- int userId = 0;
+ int userId = UserHandle.USER_SYSTEM;
boolean andCode = true;
try {
if (userStr != null) {
@@ -2672,14 +2672,8 @@
if (PackageManagerService.CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE) {
final VersionInfo internal = getInternalVersion();
if (!Build.FINGERPRINT.equals(internal.fingerprint)) {
- if (users == null) {
- mRuntimePermissionsPersistence.deleteUserRuntimePermissionsFile(
- UserHandle.USER_OWNER);
- } else {
- for (UserInfo user : users) {
- mRuntimePermissionsPersistence.deleteUserRuntimePermissionsFile(
- user.id);
- }
+ for (UserInfo user : users) {
+ mRuntimePermissionsPersistence.deleteUserRuntimePermissionsFile(user.id);
}
}
}
@@ -2722,23 +2716,15 @@
mBackupStoppedPackagesFilename.delete();
mStoppedPackagesFilename.delete();
// Migrate to new file format
- writePackageRestrictionsLPr(0);
+ writePackageRestrictionsLPr(UserHandle.USER_SYSTEM);
} else {
- if (users == null) {
- readPackageRestrictionsLPr(0);
- } else {
- for (UserInfo user : users) {
- readPackageRestrictionsLPr(user.id);
- }
+ for (UserInfo user : users) {
+ readPackageRestrictionsLPr(user.id);
}
}
- if (users == null) {
- mRuntimePermissionsPersistence.readStateForUserSyncLPr(UserHandle.USER_OWNER);
- } else {
- for (UserInfo user : users) {
- mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
- }
+ for (UserInfo user : users) {
+ mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
}
/*
@@ -4321,7 +4307,7 @@
pw.print(" sourcePackage="); pw.println(p.sourcePackage);
pw.print(" uid="); pw.print(p.uid);
pw.print(" gids="); pw.print(Arrays.toString(
- p.computeGids(UserHandle.USER_OWNER)));
+ p.computeGids(UserHandle.USER_SYSTEM)));
pw.print(" type="); pw.print(p.type);
pw.print(" prot=");
pw.println(PermissionInfo.protectionToString(p.protectionLevel));
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index fcfb5e8..fe205e5 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
@@ -280,7 +281,7 @@
}
@Override
- public List<UserInfo> getUsers(boolean excludeDying) {
+ public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
checkManageUsersPermission("query users");
synchronized (mPackagesLock) {
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
@@ -418,9 +419,10 @@
return ui;
}
+ /** Called by PackageManagerService */
public boolean exists(int userId) {
synchronized (mPackagesLock) {
- return ArrayUtils.contains(mUserIds, userId);
+ return mUsers.get(userId) != null;
}
}
diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java
index d1b50da..da23f45 100644
--- a/services/core/java/com/android/server/policy/StatusBarController.java
+++ b/services/core/java/com/android/server/policy/StatusBarController.java
@@ -122,7 +122,7 @@
*
* @return the desired start time of the status bar transition, in uptime millis
*/
- private long calculateStatusBarTransitionStartTime(Animation openAnimation,
+ private static long calculateStatusBarTransitionStartTime(Animation openAnimation,
Animation closeAnimation) {
if (openAnimation != null && closeAnimation != null) {
TranslateAnimation openTranslateAnimation = findTranslateAnimation(openAnimation);
@@ -151,7 +151,7 @@
*
* @return the found animation, {@code null} otherwise
*/
- private TranslateAnimation findTranslateAnimation(Animation animation) {
+ private static TranslateAnimation findTranslateAnimation(Animation animation) {
if (animation instanceof TranslateAnimation) {
return (TranslateAnimation) animation;
} else if (animation instanceof AnimationSet) {
@@ -170,7 +170,7 @@
* Binary searches for a {@code t} such that there exists a {@code -0.01 < eps < 0.01} for which
* {@code interpolator(t + eps) > 0.99}.
*/
- private float findAlmostThereFraction(Interpolator interpolator) {
+ private static float findAlmostThereFraction(Interpolator interpolator) {
float val = 0.5f;
float adj = 0.25f;
while (adj >= 0.01f) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index dfdb29c..d1145d0 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -26,6 +26,8 @@
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.util.Slog;
+import android.util.SparseArray;
+import android.view.AppTransitionAnimationSpec;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
@@ -153,22 +155,25 @@
private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
private String mNextAppTransitionPackage;
- private Bitmap mNextAppTransitionThumbnail;
// Used for thumbnail transitions. True if we're scaling up, false if scaling down
private boolean mNextAppTransitionScaleUp;
private IRemoteCallback mNextAppTransitionCallback;
private int mNextAppTransitionEnter;
private int mNextAppTransitionExit;
private int mNextAppTransitionInPlace;
- private int mNextAppTransitionStartX;
- private int mNextAppTransitionStartY;
- private int mNextAppTransitionStartWidth;
- private int mNextAppTransitionStartHeight;
+
+ // Keyed by task id.
+ private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs
+ = new SparseArray<>();
+ private AppTransitionAnimationSpec mDefaultNextAppTransitionAnimationSpec;
+
private Rect mNextAppTransitionInsets = new Rect();
private Rect mTmpFromClipRect = new Rect();
private Rect mTmpToClipRect = new Rect();
+ private final Rect mTmpStartRect = new Rect();
+
private final static int APP_STATE_IDLE = 0;
private final static int APP_STATE_READY = 1;
private final static int APP_STATE_RUNNING = 2;
@@ -276,8 +281,9 @@
mAppTransitionState = APP_STATE_TIMEOUT;
}
- Bitmap getNextAppTransitionThumbnail() {
- return mNextAppTransitionThumbnail;
+ Bitmap getAppTransitionThumbnailHeader(int taskId) {
+ AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
+ return spec != null ? spec.bitmap : null;
}
/** Returns whether the next thumbnail transition is aspect scaled up. */
@@ -291,14 +297,6 @@
return mNextAppTransitionScaleUp;
}
- int getStartingX() {
- return mNextAppTransitionStartX;
- }
-
- int getStartingY() {
- return mNextAppTransitionStartY;
- }
-
boolean prepare() {
if (!isRunning()) {
mAppTransitionState = APP_STATE_IDLE;
@@ -321,7 +319,7 @@
void clear() {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
mNextAppTransitionPackage = null;
- mNextAppTransitionThumbnail = null;
+ mNextAppTransitionAnimationsSpecs.clear();
}
void freeze() {
@@ -459,16 +457,17 @@
return -startPos / denom;
}
- private Animation createScaleUpAnimationLocked(int transit, boolean enter,
- int appWidth, int appHeight) {
+ private Animation createScaleUpAnimationLocked(
+ int transit, boolean enter, int appWidth, int appHeight) {
Animation a = null;
+ getDefaultNextAppTransitionStartRect(mTmpStartRect);
if (enter) {
// Entering app zooms out from the center of the initial rect.
- float scaleW = mNextAppTransitionStartWidth / (float) appWidth;
- float scaleH = mNextAppTransitionStartHeight / (float) appHeight;
+ float scaleW = mTmpStartRect.width() / (float) appWidth;
+ float scaleH = mTmpStartRect.height() / (float) appHeight;
Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mNextAppTransitionStartX, scaleW),
- computePivot(mNextAppTransitionStartY, scaleH));
+ computePivot(mTmpStartRect.left, scaleW),
+ computePivot(mTmpStartRect.right, scaleH));
scale.setInterpolator(mDecelerateInterpolator);
Animation alpha = new AlphaAnimation(0, 1);
@@ -512,6 +511,32 @@
return a;
}
+ private void getDefaultNextAppTransitionStartRect(Rect rect) {
+ if (mDefaultNextAppTransitionAnimationSpec == null ||
+ mDefaultNextAppTransitionAnimationSpec.rect == null) {
+ Slog.wtf(TAG, "Starting rect for app requested, but none available", new Throwable());
+ rect.setEmpty();
+ } else {
+ rect.set(mDefaultNextAppTransitionAnimationSpec.rect);
+ }
+ }
+
+ void getNextAppTransitionStartRect(int taskId, Rect rect) {
+ AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
+ if (spec == null || spec.rect == null) {
+ Slog.wtf(TAG, "Starting rect for task: " + taskId + " requested, but not available",
+ new Throwable());
+ rect.setEmpty();
+ } else {
+ rect.set(spec.rect);
+ }
+ }
+
+ private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height) {
+ mDefaultNextAppTransitionAnimationSpec = new AppTransitionAnimationSpec(-1 /* taskId */,
+ null /* bitmap */, new Rect(left, top, left + width, top + height));
+ }
+
private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame) {
final Animation anim;
if (enter) {
@@ -519,28 +544,27 @@
final int appWidth = appFrame.width();
final int appHeight = appFrame.height();
+ getDefaultNextAppTransitionStartRect(mTmpStartRect);
float t = 0f;
if (appHeight > 0) {
- t = (float) mNextAppTransitionStartY / appHeight;
+ t = (float) mTmpStartRect.left / appHeight;
}
int translationY = mClipRevealTranslationY
+ (int)(appHeight / 7f * t);
- int centerX = mNextAppTransitionStartX + mNextAppTransitionStartWidth / 2;
- int centerY = mNextAppTransitionStartY + mNextAppTransitionStartHeight / 2;
+ int centerX = mTmpStartRect.centerX();
+ int centerY = mTmpStartRect.centerY();
+ int halfWidth = mTmpStartRect.width() / 2;
+ int halfHeight = mTmpStartRect.height() / 2;
// Clip third of the from size of launch icon, expand to full width/height
Animation clipAnimLR = new ClipRectLRAnimation(
- centerX - mNextAppTransitionStartWidth / 2,
- centerX + mNextAppTransitionStartWidth / 2,
- 0, appWidth);
+ centerX - halfWidth, centerX + halfWidth, 0, appWidth);
clipAnimLR.setInterpolator(mClipHorizontalInterpolator);
clipAnimLR.setDuration((long) (DEFAULT_APP_TRANSITION_DURATION / 2.5f));
- Animation clipAnimTB = new ClipRectTBAnimation(
- centerY - mNextAppTransitionStartHeight / 2 - translationY,
- centerY + mNextAppTransitionStartHeight / 2 - translationY,
- 0, appHeight);
+ Animation clipAnimTB = new ClipRectTBAnimation(centerY - halfHeight - translationY,
+ centerY + halfHeight/ 2 - translationY, 0, appHeight);
clipAnimTB.setInterpolator(mTouchResponseInterpolator);
clipAnimTB.setDuration(DEFAULT_APP_TRANSITION_DURATION);
@@ -649,31 +673,32 @@
/**
* This animation runs for the thumbnail that gets cross faded with the enter/exit activity
- * when a thumbnail is specified with the activity options.
+ * when a thumbnail is specified with the pending animation override.
*/
- Animation createThumbnailAspectScaleAnimationLocked(Rect appRect) {
+ Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, Bitmap thumbnailHeader,
+ final int taskId) {
Animation a;
- final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+ final int thumbWidthI = thumbnailHeader.getWidth();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+ final int thumbHeightI = thumbnailHeader.getHeight();
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
final int appWidth = appRect.width();
float scaleW = appWidth / thumbWidth;
float unscaledHeight = thumbHeight * scaleW;
- float unscaledStartY = mNextAppTransitionStartY - (unscaledHeight - thumbHeight) / 2f;
+ getNextAppTransitionStartRect(taskId, mTmpStartRect);
+ float unscaledStartY = mTmpStartRect.top - (unscaledHeight - thumbHeight) / 2f;
if (mNextAppTransitionScaleUp) {
// Animation up from the thumbnail to the full screen
Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW,
- mNextAppTransitionStartX + (thumbWidth / 2f),
- mNextAppTransitionStartY + (thumbHeight / 2f));
+ mTmpStartRect.left + (thumbWidth / 2f), mTmpStartRect.top + (thumbHeight / 2f));
scale.setInterpolator(mTouchResponseInterpolator);
scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
Animation alpha = new AlphaAnimation(1, 0);
alpha.setInterpolator(mThumbnailFadeOutInterpolator);
alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
final float toX = appRect.left + appRect.width() / 2 -
- (mNextAppTransitionStartX + thumbWidth / 2);
+ (mTmpStartRect.left + thumbWidth / 2);
final float toY = appRect.top + mNextAppTransitionInsets.top + -unscaledStartY;
Animation translate = new TranslateAnimation(0, toX, 0, toY);
translate.setInterpolator(mTouchResponseInterpolator);
@@ -688,8 +713,7 @@
} else {
// Animation down from the full screen to the thumbnail
Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f,
- mNextAppTransitionStartX + (thumbWidth / 2f),
- mNextAppTransitionStartY + (thumbHeight / 2f));
+ mTmpStartRect.left + (thumbWidth / 2f), mTmpStartRect.top + (thumbHeight / 2f));
scale.setInterpolator(mTouchResponseInterpolator);
scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
Animation alpha = new AlphaAnimation(0f, 1f);
@@ -718,11 +742,13 @@
*/
Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
int appWidth, int appHeight, int orientation, int transit, Rect containingFrame,
- Rect contentInsets, @Nullable Rect surfaceInsets, boolean resizedWindow) {
+ Rect contentInsets, @Nullable Rect surfaceInsets, boolean resizedWindow,
+ int taskId) {
Animation a;
- final int thumbWidthI = mNextAppTransitionStartWidth;
+ getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ final int thumbWidthI = mTmpStartRect.width();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = mNextAppTransitionStartHeight;
+ final int thumbHeightI = mTmpStartRect.height();
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
// Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
@@ -733,7 +759,7 @@
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
if (resizedWindow) {
a = createAspectScaledThumbnailEnterNonFullscreenAnimationLocked(
- containingFrame, surfaceInsets);
+ containingFrame, surfaceInsets, taskId);
} else {
// App window scaling up to become full screen
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -759,8 +785,8 @@
mNextAppTransitionInsets.set(contentInsets);
Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
- computePivot(mNextAppTransitionStartX, scale),
- computePivot(mNextAppTransitionStartY, scale));
+ computePivot(mTmpStartRect.left, scale),
+ computePivot(mTmpStartRect.top, scale));
Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
@@ -819,8 +845,8 @@
mNextAppTransitionInsets.set(contentInsets);
Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
- computePivot(mNextAppTransitionStartX, scale),
- computePivot(mNextAppTransitionStartY, scale));
+ computePivot(mTmpStartRect.left, scale),
+ computePivot(mTmpStartRect.top, scale));
Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
@@ -844,11 +870,12 @@
}
private Animation createAspectScaledThumbnailEnterNonFullscreenAnimationLocked(
- Rect containingFrame, @Nullable Rect surfaceInsets) {
+ Rect containingFrame, @Nullable Rect surfaceInsets, int taskId) {
+ getNextAppTransitionStartRect(taskId, mTmpStartRect);
float width = containingFrame.width();
float height = containingFrame.height();
- float scaleWidth = mNextAppTransitionStartWidth / width;
- float scaleHeight = mNextAppTransitionStartHeight / height;
+ float scaleWidth = mTmpStartRect.width() / width;
+ float scaleHeight = mTmpStartRect.height() / height;
AnimationSet set = new AnimationSet(true);
int surfaceInsetsHorizontal = surfaceInsets == null
? 0 : surfaceInsets.left + surfaceInsets.right;
@@ -858,9 +885,9 @@
// we need to account for surface insets that will be used to enlarge the surface.
ScaleAnimation scale = new ScaleAnimation(scaleWidth, 1, scaleHeight, 1,
(width + surfaceInsetsHorizontal) / 2, (height + surfaceInsetsVertical) / 2);
- int fromX = mNextAppTransitionStartX + mNextAppTransitionStartWidth / 2
+ int fromX = mTmpStartRect.left + mTmpStartRect.width() / 2
- (containingFrame.left + containingFrame.width() / 2);
- int fromY = mNextAppTransitionStartY + mNextAppTransitionStartHeight / 2
+ int fromY = mTmpStartRect.top + mTmpStartRect.height() / 2
- (containingFrame.top + containingFrame.height() / 2);
TranslateAnimation translation = new TranslateAnimation(fromX, 0, fromY, 0);
set.addAnimation(scale);
@@ -870,13 +897,15 @@
/**
* This animation runs for the thumbnail that gets cross faded with the enter/exit activity
- * when a thumbnail is specified with the activity options.
+ * when a thumbnail is specified with the pending animation override.
*/
- Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) {
+ Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit,
+ Bitmap thumbnailHeader) {
Animation a;
- final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+ getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ final int thumbWidthI = thumbnailHeader.getWidth();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+ final int thumbHeightI = thumbnailHeader.getHeight();
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
if (mNextAppTransitionScaleUp) {
@@ -884,8 +913,8 @@
float scaleW = appWidth / thumbWidth;
float scaleH = appHeight / thumbHeight;
Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mNextAppTransitionStartX, 1 / scaleW),
- computePivot(mNextAppTransitionStartY, 1 / scaleH));
+ computePivot(mTmpStartRect.left, 1 / scaleW),
+ computePivot(mTmpStartRect.top, 1 / scaleH));
scale.setInterpolator(mDecelerateInterpolator);
Animation alpha = new AlphaAnimation(1, 0);
@@ -901,8 +930,8 @@
float scaleW = appWidth / thumbWidth;
float scaleH = appHeight / thumbHeight;
a = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mNextAppTransitionStartX, 1 / scaleW),
- computePivot(mNextAppTransitionStartY, 1 / scaleH));
+ computePivot(mTmpStartRect.left, 1 / scaleW),
+ computePivot(mTmpStartRect.top, 1 / scaleH));
}
return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
@@ -913,11 +942,13 @@
* leaving, and the activity that is entering.
*/
Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
- int appHeight, int transit) {
+ int appHeight, int transit, int taskId) {
+ Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
Animation a;
- final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+ getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+ final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
switch (thumbTransitState) {
@@ -926,8 +957,8 @@
float scaleW = thumbWidth / appWidth;
float scaleH = thumbHeight / appHeight;
a = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mNextAppTransitionStartX, scaleW),
- computePivot(mNextAppTransitionStartY, scaleH));
+ computePivot(mTmpStartRect.left, scaleW),
+ computePivot(mTmpStartRect.top, scaleH));
break;
}
case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
@@ -954,8 +985,8 @@
float scaleW = thumbWidth / appWidth;
float scaleH = thumbHeight / appHeight;
Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mNextAppTransitionStartX, scaleW),
- computePivot(mNextAppTransitionStartY, scaleH));
+ computePivot(mTmpStartRect.left, scaleW),
+ computePivot(mTmpStartRect.top, scaleH));
Animation alpha = new AlphaAnimation(1, 0);
@@ -987,7 +1018,7 @@
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
@Nullable Rect surfaceInsets, Rect appFrame, boolean isVoiceInteraction,
- boolean resizedWindow) {
+ boolean resizedWindow, int taskId) {
Animation a;
if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
|| transit == TRANSIT_TASK_OPEN
@@ -1042,7 +1073,7 @@
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
- appWidth, appHeight, transit);
+ appWidth, appHeight, transit, taskId);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
"ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
@@ -1057,7 +1088,7 @@
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
a = createAspectScaledThumbnailEnterExitAnimationLocked(
getThumbnailTransitionState(enter), appWidth, appHeight, orientation, transit,
- containingFrame, contentInsets, surfaceInsets, resizedWindow);
+ containingFrame, contentInsets, surfaceInsets, resizedWindow, taskId);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
"ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
@@ -1147,7 +1178,7 @@
if (isTransitionSet()) {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
mNextAppTransitionPackage = packageName;
- mNextAppTransitionThumbnail = null;
+ mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionEnter = enterAnim;
mNextAppTransitionExit = exitAnim;
postAnimationCallback();
@@ -1158,15 +1189,13 @@
}
void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
- int startHeight) {
+ int startHeight) {
if (isTransitionSet()) {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
mNextAppTransitionPackage = null;
- mNextAppTransitionThumbnail = null;
- mNextAppTransitionStartX = startX;
- mNextAppTransitionStartY = startY;
- mNextAppTransitionStartWidth = startWidth;
- mNextAppTransitionStartHeight = startHeight;
+ mNextAppTransitionAnimationsSpecs.clear();
+ putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth,
+ startY + startHeight);
postAnimationCallback();
mNextAppTransitionCallback = null;
}
@@ -1176,10 +1205,7 @@
int startWidth, int startHeight) {
if (isTransitionSet()) {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
- mNextAppTransitionStartX = startX;
- mNextAppTransitionStartY = startY;
- mNextAppTransitionStartWidth = startWidth;
- mNextAppTransitionStartHeight = startHeight;
+ putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight);
postAnimationCallback();
mNextAppTransitionCallback = null;
}
@@ -1191,10 +1217,9 @@
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
mNextAppTransitionPackage = null;
- mNextAppTransitionThumbnail = srcThumb;
+ mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionScaleUp = scaleUp;
- mNextAppTransitionStartX = startX;
- mNextAppTransitionStartY = startY;
+ putDefaultNextAppTransitionCoordinates(startX, startY, 0 ,0);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
} else {
@@ -1208,12 +1233,9 @@
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
mNextAppTransitionPackage = null;
- mNextAppTransitionThumbnail = srcThumb;
+ mNextAppTransitionAnimationsSpecs.clear();
mNextAppTransitionScaleUp = scaleUp;
- mNextAppTransitionStartX = startX;
- mNextAppTransitionStartY = startY;
- mNextAppTransitionStartWidth = targetWidth;
- mNextAppTransitionStartHeight = targetHeight;
+ putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight);
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
} else {
@@ -1221,6 +1243,27 @@
}
}
+ public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
+ IRemoteCallback callback, boolean scaleUp) {
+ if (isTransitionSet()) {
+ mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
+ : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
+ mNextAppTransitionPackage = null;
+ mNextAppTransitionAnimationsSpecs.clear();
+ mNextAppTransitionScaleUp = scaleUp;
+ for (int i = 0; i < specs.length; i++) {
+ AppTransitionAnimationSpec spec = specs[i];
+ if (spec != null) {
+ mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
+ }
+ }
+ postAnimationCallback();
+ mNextAppTransitionCallback = callback;
+ } else {
+ postAnimationCallback();
+ }
+ }
+
void overrideInPlaceAppTransition(String packageName, int anim) {
if (isTransitionSet()) {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
@@ -1350,33 +1393,30 @@
pw.print(prefix); pw.print("mNextAppTransitionInPlace=0x");
pw.print(Integer.toHexString(mNextAppTransitionInPlace));
break;
- case NEXT_TRANSIT_TYPE_SCALE_UP:
+ case NEXT_TRANSIT_TYPE_SCALE_UP: {
+ getDefaultNextAppTransitionStartRect(mTmpStartRect);
pw.print(prefix); pw.print("mNextAppTransitionStartX=");
- pw.print(mNextAppTransitionStartX);
+ pw.print(mTmpStartRect.left);
pw.print(" mNextAppTransitionStartY=");
- pw.println(mNextAppTransitionStartY);
+ pw.println(mTmpStartRect.top);
pw.print(prefix); pw.print("mNextAppTransitionStartWidth=");
- pw.print(mNextAppTransitionStartWidth);
+ pw.print(mTmpStartRect.width());
pw.print(" mNextAppTransitionStartHeight=");
- pw.println(mNextAppTransitionStartHeight);
+ pw.println(mTmpStartRect.height());
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(prefix); pw.print("mNextAppTransitionThumbnail=");
- pw.print(mNextAppTransitionThumbnail);
- pw.print(" mNextAppTransitionStartX=");
- pw.print(mNextAppTransitionStartX);
- pw.print(" mNextAppTransitionStartY=");
- pw.println(mNextAppTransitionStartY);
- pw.print(prefix); pw.print("mNextAppTransitionStartWidth=");
- pw.print(mNextAppTransitionStartWidth);
- pw.print(" mNextAppTransitionStartHeight=");
- pw.println(mNextAppTransitionStartHeight);
+ case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: {
+ pw.print(prefix); pw.print("mDefaultNextAppTransitionAnimationSpec=");
+ pw.println(mDefaultNextAppTransitionAnimationSpec);
+ pw.print(prefix); pw.print("mNextAppTransitionAnimationsSpecs=");
+ pw.println(mNextAppTransitionAnimationsSpecs);
pw.print(prefix); pw.print("mNextAppTransitionScaleUp=");
pw.println(mNextAppTransitionScaleUp);
break;
+ }
}
if (mNextAppTransitionCallback != null) {
pw.print(prefix); pw.print("mNextAppTransitionCallback=");
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d6396a4..dc5effd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -78,6 +78,7 @@
import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.TypedValue;
+import android.view.AppTransitionAnimationSpec;
import android.view.Choreographer;
import android.view.Display;
import android.view.DisplayInfo;
@@ -442,6 +443,7 @@
final float[] mTmpFloats = new float[9];
final Rect mTmpContentRect = new Rect();
+ private final Rect mTmpStartRect = new Rect();
boolean mDisplayReady;
boolean mSafeMode;
@@ -2829,7 +2831,8 @@
final boolean resizedWindow = !fullscreen && !dialogWindow;
Animation a = mAppTransition.loadAnimation(lp, transit, enter, containingWidth,
containingHeight, mCurConfiguration.orientation, containingFrame, contentInsets,
- surfaceInsets, appFrame, isVoiceInteraction, resizedWindow);
+ surfaceInsets, appFrame, isVoiceInteraction, resizedWindow,
+ atoken.mTask.mTaskId);
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = null;
@@ -3553,6 +3556,14 @@
}
@Override
+ public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
+ IRemoteCallback callback, boolean scaleUp) {
+ synchronized (mWindowMap) {
+ mAppTransition.overridePendingAppTransitionMultiThumb(specs, callback, scaleUp);
+ }
+ }
+
+ @Override
public void overridePendingAppTransitionInPlace(String packageName, int anim) {
synchronized(mWindowMap) {
mAppTransition.overrideInPlaceAppTransition(packageName, anim);
@@ -8707,11 +8718,6 @@
animLp = null;
}
- AppWindowToken topOpeningApp = null;
- AppWindowToken topClosingApp = null;
- int topOpeningLayer = 0;
- int topClosingLayer = 0;
-
// Process all applications animating in place
if (transit == AppTransition.TRANSIT_TASK_IN_PLACE) {
// Find the focused window
@@ -8736,49 +8742,8 @@
}
}
- appsCount = mOpeningApps.size();
- for (i = 0; i < appsCount; i++) {
- AppWindowToken wtoken = mOpeningApps.valueAt(i);
- final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
-
- if (!appAnimator.usingTransferredAnimation) {
- appAnimator.clearThumbnail();
- appAnimator.animation = null;
- }
- wtoken.inPendingTransaction = false;
- if (!setTokenVisibilityLocked(
- wtoken, animLp, true, transit, false, voiceInteraction)){
- // This token isn't going to be animating. Add it to the list of tokens to
- // be notified of app transition complete since the notification will not be
- // sent be the app window animator.
- mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
- }
- wtoken.updateReportedVisibilityLocked();
- wtoken.waitingToShow = false;
-
- appAnimator.mAllAppWinAnimators.clear();
- final int windowsCount = wtoken.allAppWindows.size();
- for (int j = 0; j < windowsCount; j++) {
- appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
- }
- mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();
- mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
-
- 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 (topOpeningApp == null || layer > topOpeningLayer) {
- topOpeningApp = wtoken;
- topOpeningLayer = layer;
- }
- }
- }
+ AppWindowToken topClosingApp = null;
+ int topClosingLayer = 0;
appsCount = mClosingApps.size();
for (i = 0; i < appsCount; i++) {
AppWindowToken wtoken = mClosingApps.valueAt(i);
@@ -8816,77 +8781,57 @@
}
}
- AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null :
+ AppWindowToken topOpeningApp = null;
+ appsCount = mOpeningApps.size();
+ for (i = 0; i < appsCount; i++) {
+ AppWindowToken wtoken = mOpeningApps.valueAt(i);
+ final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
+
+ if (!appAnimator.usingTransferredAnimation) {
+ appAnimator.clearThumbnail();
+ appAnimator.animation = null;
+ }
+ wtoken.inPendingTransaction = false;
+ if (!setTokenVisibilityLocked(
+ wtoken, animLp, true, transit, false, voiceInteraction)){
+ // This token isn't going to be animating. Add it to the list of tokens to
+ // be notified of app transition complete since the notification will not be
+ // sent be the app window animator.
+ mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
+ }
+ wtoken.updateReportedVisibilityLocked();
+ wtoken.waitingToShow = false;
+
+ appAnimator.mAllAppWinAnimators.clear();
+ final int windowsCount = wtoken.allAppWindows.size();
+ for (int j = 0; j < windowsCount; j++) {
+ appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
+ }
+ mAnimator.mAnimating |= appAnimator.showAllWindowsLocked();
+ mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
+
+ int topOpeningLayer = 0;
+ 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 (topOpeningApp == null || layer > topOpeningLayer) {
+ topOpeningApp = wtoken;
+ topOpeningLayer = layer;
+ }
+ }
+ createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
+ }
+
+ AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null :
topOpeningApp.mAppAnimator;
AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null :
topClosingApp.mAppAnimator;
- Bitmap nextAppTransitionThumbnail = mAppTransition.getNextAppTransitionThumbnail();
- 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(),
- nextAppTransitionThumbnail.getHeight());
- try {
- // 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(),
- PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
- surfaceControl.setLayerStack(display.getLayerStack());
- 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();
-
- // Get the thumbnail animation
- Animation anim;
- if (mAppTransition.isNextThumbnailTransitionAspectScaled()) {
- // If this is a multi-window scenario, we use the windows frame as
- // destination of the thumbnail header animation. If this is a full screen
- // window scenario, we use the whole display as the target.
- WindowState win = topOpeningApp.findMainWindow();
- Rect appRect = win != null ? win.getContentFrameLw() :
- new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
- // 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(appRect);
- openingAppAnimator.thumbnailForceAboveLayer = Math.max(topOpeningLayer,
- topClosingLayer);
- openingAppAnimator.deferThumbnailDestruction =
- !mAppTransition.isNextThumbnailTransitionScaleUp();
- } else {
- anim = mAppTransition.createThumbnailScaleAnimationLocked(
- displayInfo.appWidth, displayInfo.appHeight, transit);
- }
- anim.restrictDuration(MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(getTransitionAnimationScaleLocked());
-
- 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);
- openingAppAnimator.clearThumbnail();
- }
- }
mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator);
mAppTransition.postAnimationCallback();
@@ -8914,6 +8859,83 @@
return changes;
}
+ private void createThumbnailAppAnimator(int transit, AppWindowToken appToken,
+ int openingLayer, int closingLayer) {
+ AppWindowAnimator openingAppAnimator = (appToken == null) ? null : appToken.mAppAnimator;
+ if (openingAppAnimator == null || openingAppAnimator.animation == null) {
+ return;
+ }
+ final int taskId = appToken.mTask.mTaskId;
+ Bitmap thumbnailHeader = mAppTransition.getAppTransitionThumbnailHeader(taskId);
+ if (thumbnailHeader == null || thumbnailHeader.getConfig() == Config.ALPHA_8) {
+ return;
+ }
+ // 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, thumbnailHeader.getWidth(), thumbnailHeader.getHeight());
+ try {
+ // 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(),
+ PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
+ surfaceControl.setLayerStack(display.getLayerStack());
+ 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(thumbnailHeader, 0, 0, null);
+ drawSurface.unlockCanvasAndPost(c);
+ drawSurface.release();
+
+ // Get the thumbnail animation
+ Animation anim;
+ if (mAppTransition.isNextThumbnailTransitionAspectScaled()) {
+ // If this is a multi-window scenario, we use the windows frame as
+ // destination of the thumbnail header animation. If this is a full screen
+ // window scenario, we use the whole display as the target.
+ WindowState win = appToken.findMainWindow();
+ Rect appRect = win != null ? win.getContentFrameLw() :
+ new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
+ // 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(appRect,
+ thumbnailHeader, taskId);
+ Log.d(TAG, "assigning thumbnail force above layer: " + openingLayer + " " +
+ closingLayer);
+ openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
+ openingAppAnimator.deferThumbnailDestruction =
+ !mAppTransition.isNextThumbnailTransitionScaleUp();
+ } else {
+ anim = mAppTransition.createThumbnailScaleAnimationLocked(
+ displayInfo.appWidth, displayInfo.appHeight, transit, thumbnailHeader);
+ }
+ anim.restrictDuration(MAX_ANIMATION_DURATION);
+ anim.scaleCurrentDuration(getTransitionAnimationScaleLocked());
+
+ openingAppAnimator.thumbnail = surfaceControl;
+ openingAppAnimator.thumbnailLayer = openingLayer;
+ openingAppAnimator.thumbnailAnimation = anim;
+ mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
+ openingAppAnimator.thumbnailX = mTmpStartRect.left;
+ openingAppAnimator.thumbnailY = mTmpStartRect.top;
+ } catch (OutOfResourcesException e) {
+ Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w=" + dirty.width()
+ + " h=" + dirty.height(), e);
+ openingAppAnimator.clearThumbnail();
+ }
+ }
+
/**
* Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
* @return bitmap indicating if another pass through layout must be made.
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b702f5a..b56b1f9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
@@ -37,6 +38,7 @@
import android.os.RemoteCallbackList;
import android.os.SystemClock;
import android.os.WorkSource;
+import android.util.DisplayMetrics;
import android.util.TimeUtils;
import android.view.Display;
import android.view.IWindowFocusObserver;
@@ -77,6 +79,10 @@
final class WindowState implements WindowManagerPolicy.WindowState {
static final String TAG = "WindowState";
+ // The minimal size of a window within the usable area of the freeform stack.
+ static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48;
+ static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32;
+
final WindowManagerService mService;
final WindowManagerPolicy mPolicy;
final Context mContext;
@@ -535,6 +541,8 @@
mHaveFrame = true;
final Task task = mAppToken != null ? getTask() : null;
+ final boolean isFreeFormWorkspace = task != null && task.mStack != null &&
+ task.mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
final boolean nonFullscreenTask = task != null && !task.isFullscreen();
if (nonFullscreenTask) {
task.getBounds(mContainingFrame);
@@ -544,10 +552,20 @@
// IME is up and obscuring this window. Adjust the window position so it is visible.
mContainingFrame.top -= mContainingFrame.bottom - cf.bottom;
}
- // Make sure the containing frame is within the content frame so we don't layout
- // resized window under screen decorations.
- if (!mContainingFrame.intersect(cf)) {
- mContainingFrame.set(cf);
+
+ if (isFreeFormWorkspace) {
+ // In free form mode we have only to set the rectangle if it wasn't set already. No
+ // need to intersect it with the (visible) "content frame" since it is allowed to
+ // be outside the visible desktop.
+ if (mContainingFrame.isEmpty()) {
+ mContainingFrame.set(cf);
+ }
+ } else {
+ // Make sure the containing frame is within the content frame so we don't layout
+ // resized window under screen decorations.
+ if (!mContainingFrame.intersect(cf)) {
+ mContainingFrame.set(cf);
+ }
}
mDisplayFrame.set(mContainingFrame);
} else {
@@ -651,20 +669,38 @@
// Make sure the content and visible frames are inside of the
// final window frame.
- mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
- Math.max(mContentFrame.top, mFrame.top),
- Math.min(mContentFrame.right, mFrame.right),
- Math.min(mContentFrame.bottom, mFrame.bottom));
+ if (isFreeFormWorkspace && !mFrame.isEmpty()) {
+ // Keep the frame out of the blocked system area, limit it in size to the content area
+ // and make sure that there is always a minimum visible so that the user can drag it
+ // into a usable area..
+ final int height = Math.min(mFrame.height(), mContentFrame.height());
+ final int width = Math.min(mContentFrame.width(), mFrame.width());
+ final int minVisibleHeight = calculatePixelFromDp(MINIMUM_VISIBLE_HEIGHT_IN_DP);
+ final int minVisibleWidth = calculatePixelFromDp(MINIMUM_VISIBLE_WIDTH_IN_DP);
+ final int top = Math.max(mContentFrame.top,
+ Math.min(mFrame.top, mContentFrame.bottom - minVisibleHeight));
+ final int left = Math.max(mContentFrame.left + minVisibleWidth - width,
+ Math.min(mFrame.left, mContentFrame.right - minVisibleWidth));
+ mFrame.set(left, top, left + width, top + height);
+ mContentFrame.set(mFrame);
+ mVisibleFrame.set(mContentFrame);
+ mStableFrame.set(mContentFrame);
+ } else {
+ mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
+ Math.max(mContentFrame.top, mFrame.top),
+ Math.min(mContentFrame.right, mFrame.right),
+ Math.min(mContentFrame.bottom, mFrame.bottom));
- mVisibleFrame.set(Math.max(mVisibleFrame.left, mFrame.left),
- Math.max(mVisibleFrame.top, mFrame.top),
- Math.min(mVisibleFrame.right, mFrame.right),
- Math.min(mVisibleFrame.bottom, mFrame.bottom));
+ mVisibleFrame.set(Math.max(mVisibleFrame.left, mFrame.left),
+ Math.max(mVisibleFrame.top, mFrame.top),
+ Math.min(mVisibleFrame.right, mFrame.right),
+ Math.min(mVisibleFrame.bottom, mFrame.bottom));
- mStableFrame.set(Math.max(mStableFrame.left, mFrame.left),
- Math.max(mStableFrame.top, mFrame.top),
- Math.min(mStableFrame.right, mFrame.right),
- Math.min(mStableFrame.bottom, mFrame.bottom));
+ mStableFrame.set(Math.max(mStableFrame.left, mFrame.left),
+ Math.max(mStableFrame.top, mFrame.top),
+ Math.min(mStableFrame.right, mFrame.right),
+ Math.min(mStableFrame.bottom, mFrame.bottom));
+ }
mOverscanInsets.set(Math.max(mOverscanFrame.left - mFrame.left, 0),
Math.max(mOverscanFrame.top - mFrame.top, 0),
@@ -1576,6 +1612,13 @@
}
}
+ private int calculatePixelFromDp(int dp) {
+ final Configuration serviceConfig = mService.mCurConfiguration;
+ // TODO(multidisplay): Update Dp to that of display stack is on.
+ final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ return (int)(dp * density);
+ }
+
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
final TaskStack stack = getStack();
pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 33979b1..3ad26d3 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -13,6 +13,7 @@
services.net \
easymocklib \
guava \
+ android-support-test \
mockito-target
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 919293a..386a9cb 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -69,7 +69,7 @@
</application>
<instrumentation
- android:name="android.test.InstrumentationTestRunner"
+ android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.servicestests"
android:label="Frameworks Services Tests" />
</manifest>
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 696f106..b4c76b7 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -943,8 +943,7 @@
if (capability == NET_CAPABILITY_CBS || capability == NET_CAPABILITY_DUN ||
capability == NET_CAPABILITY_EIMS || capability == NET_CAPABILITY_FOTA ||
capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS ||
- capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP ||
- capability == NET_CAPABILITY_TRUSTED || capability == NET_CAPABILITY_NOT_VPN) {
+ capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP) {
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
} else {
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index ed1db6f..bf6343f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -21,8 +21,11 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageParser;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
import android.test.AndroidTestCase;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -36,6 +39,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.List;
public class PackageManagerSettingsTests extends AndroidTestCase {
private static final String PACKAGE_NAME_2 = "com.google.app2";
@@ -45,6 +50,12 @@
public static final String TAG = "PackageManagerSettingsTests";
protected final String PREFIX = "android.content.pm";
+ private @NonNull List<UserInfo> createFakeUsers() {
+ ArrayList<UserInfo> users = new ArrayList<>();
+ users.add(new UserInfo(UserHandle.USER_SYSTEM, "test user", UserInfo.FLAG_INITIALIZED));
+ return users;
+ }
+
private void writeFile(File file, byte[] data) {
file.mkdirs();
try {
@@ -245,7 +256,7 @@
writeOldFiles();
createUserManagerServiceRef();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(createFakeUsers()));
verifyKeySetMetaData(settings);
}
@@ -257,11 +268,11 @@
writeOldFiles();
createUserManagerServiceRef();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(createFakeUsers()));
/* write out, read back in and verify the same */
settings.writeLPr();
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(createFakeUsers()));
verifyKeySetMetaData(settings);
}
@@ -269,7 +280,7 @@
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(createFakeUsers()));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_3));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_1));
@@ -289,12 +300,12 @@
writeOldFiles();
createUserManagerServiceRef();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(createFakeUsers()));
settings.writeLPr();
// Create Settings again to make it read from the new files
settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(createFakeUsers()));
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_2);
assertEquals(COMPONENT_ENABLED_STATE_DISABLED_USER, ps.getEnabled(0));
@@ -305,7 +316,7 @@
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(createFakeUsers()));
// Enable/Disable a package
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_1);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d91fa90..2d31a78 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -512,8 +512,8 @@
Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
Objects.equals(mVideoState, d.mVideoState) &&
Objects.equals(mStatusHints, d.mStatusHints) &&
- Objects.equals(mExtras, d.mExtras) &&
- Objects.equals(mIntentExtras, d.mIntentExtras);
+ areBundlesEqual(mExtras, d.mExtras) &&
+ areBundlesEqual(mIntentExtras, d.mIntentExtras);
}
return false;
}
@@ -1253,4 +1253,32 @@
});
}
}
+
+ /**
+ * Determines if two bundles are equal.
+ *
+ * @param bundle The original bundle.
+ * @param newBundle The bundle to compare with.
+ * @retrun {@code true} if the bundles are equal, {@code false} otherwise.
+ */
+ private static boolean areBundlesEqual(Bundle bundle, Bundle newBundle) {
+ if (bundle == null || newBundle == null) {
+ return bundle == newBundle;
+ }
+
+ if (bundle.size() != newBundle.size()) {
+ return false;
+ }
+
+ for(String key : bundle.keySet()) {
+ if (key != null) {
+ final Object value = bundle.get(key);
+ final Object newValue = newBundle.get(key);
+ if (!Objects.equals(value, newValue)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
}
diff --git a/tools/aapt2/Files.cpp b/tools/aapt2/Files.cpp
index 8484148..b24ff6b 100644
--- a/tools/aapt2/Files.cpp
+++ b/tools/aapt2/Files.cpp
@@ -22,7 +22,7 @@
#include <string>
#include <sys/stat.h>
-#ifdef HAVE_MS_C_RUNTIME
+#ifdef _WIN32
// Windows includes.
#include <direct.h>
#endif
@@ -83,7 +83,7 @@
}
inline static int mkdirImpl(const StringPiece& path) {
-#ifdef HAVE_MS_C_RUNTIME
+#ifdef _WIN32
return _mkdir(path.toString().c_str());
#else
return mkdir(path.toString().c_str(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 62859ec..a8fd91c 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -30,6 +30,7 @@
import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.util.DisplayMetrics;
+import android.view.AppTransitionAnimationSpec;
import java.lang.Override;
@@ -241,6 +242,12 @@
}
@Override
+ public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
+ IRemoteCallback callback, boolean scaleUp) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void pauseKeyDispatching(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub