Merge "Implement shell commands for battery and activity services."
diff --git a/api/current.txt b/api/current.txt
index 666bf83..eb888af 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36477,6 +36477,7 @@
method public boolean performHapticFeedback(int);
method public boolean performHapticFeedback(int, int);
method public boolean performLongClick();
+ method public boolean performLongClick(float, float);
method public void playSoundEffect(int);
method public boolean post(java.lang.Runnable);
method public boolean postDelayed(java.lang.Runnable, long);
@@ -39212,6 +39213,7 @@
method public abstract android.net.Uri getUrl();
method public abstract boolean hasGesture();
method public abstract boolean isForMainFrame();
+ method public abstract boolean isRedirect();
}
public class WebResourceResponse {
@@ -39574,7 +39576,8 @@
method public deprecated android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, java.lang.String);
method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, android.webkit.WebResourceRequest);
method public boolean shouldOverrideKeyEvent(android.webkit.WebView, android.view.KeyEvent);
- method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
+ method public deprecated boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
+ method public boolean shouldOverrideUrlLoading(android.webkit.WebView, android.webkit.WebResourceRequest);
field public static final int ERROR_AUTHENTICATION = -4; // 0xfffffffc
field public static final int ERROR_BAD_URL = -12; // 0xfffffff4
field public static final int ERROR_CONNECT = -6; // 0xfffffffa
diff --git a/api/system-current.txt b/api/system-current.txt
index 3554e0f..d288ad1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -38774,6 +38774,7 @@
method public boolean performHapticFeedback(int);
method public boolean performHapticFeedback(int, int);
method public boolean performLongClick();
+ method public boolean performLongClick(float, float);
method public void playSoundEffect(int);
method public boolean post(java.lang.Runnable);
method public boolean postDelayed(java.lang.Runnable, long);
@@ -41572,6 +41573,7 @@
method public abstract android.net.Uri getUrl();
method public abstract boolean hasGesture();
method public abstract boolean isForMainFrame();
+ method public abstract boolean isRedirect();
}
public class WebResourceResponse {
@@ -41981,7 +41983,8 @@
method public deprecated android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, java.lang.String);
method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, android.webkit.WebResourceRequest);
method public boolean shouldOverrideKeyEvent(android.webkit.WebView, android.view.KeyEvent);
- method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
+ method public deprecated boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
+ method public boolean shouldOverrideUrlLoading(android.webkit.WebView, android.webkit.WebResourceRequest);
field public static final int ERROR_AUTHENTICATION = -4; // 0xfffffffc
field public static final int ERROR_BAD_URL = -12; // 0xfffffff4
field public static final int ERROR_CONNECT = -6; // 0xfffffffa
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 821621e..0266a37 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -25,10 +25,12 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.util.Pair;
import android.util.Slog;
+import android.view.AppTransitionAnimationSpec;
import android.view.View;
import android.view.Window;
@@ -129,6 +131,11 @@
public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener";
/**
+ * Descriptions of app transition animations to be played during the activity launch.
+ */
+ private static final String KEY_ANIM_SPECS = "android:activity.animSpecs";
+
+ /**
* Where the docked stack should be positioned.
* @hide
*/
@@ -199,6 +206,7 @@
private int mExitCoordinatorIndex;
private PendingIntent mUsageTimeReport;
private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ private AppTransitionAnimationSpec mAnimSpecs[];
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -512,6 +520,18 @@
return opts;
}
+ /** @hide */
+ public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source,
+ AppTransitionAnimationSpec[] specs, Handler handler,
+ OnAnimationStartedListener listener) {
+ ActivityOptions opts = new ActivityOptions();
+ opts.mPackageName = source.getContext().getPackageName();
+ opts.mAnimationType = ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
+ opts.mAnimSpecs = specs;
+ opts.setOnAnimationStartedListener(handler, listener);
+ return opts;
+ }
+
/**
* Create an ActivityOptions to transition between Activities using cross-Activity scene
* animations. This method carries the position of one shared element to the started Activity.
@@ -698,6 +718,13 @@
break;
}
mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
+ if (opts.containsKey(KEY_ANIM_SPECS)) {
+ Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
+ mAnimSpecs = new AppTransitionAnimationSpec[specs.length];
+ for (int i = specs.length - 1; i >= 0; i--) {
+ mAnimSpecs[i] = (AppTransitionAnimationSpec) specs[i];
+ }
+ }
}
/** @hide */
@@ -810,6 +837,9 @@
}
/** @hide */
+ public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
+
+ /** @hide */
public static void abort(Bundle options) {
if (options != null) {
(new ActivityOptions(options)).abort();
@@ -898,6 +928,7 @@
mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
break;
}
+ mAnimSpecs = otherOptions.mAnimSpecs;
}
/**
@@ -964,6 +995,9 @@
break;
}
b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
+ if (mAnimSpecs != null) {
+ b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
+ }
return b;
}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index ff4ebee..874026f 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -1059,6 +1059,41 @@
}
/**
+ * Sets whether audio routing is allowed.
+ *
+ * Note: This is an internal function and shouldn't be exposed
+ */
+ public void setAudioRouteAllowed(boolean allowed) {
+ if (VDBG) log("setAudioRouteAllowed");
+ if (mService != null && isEnabled()) {
+ try {
+ mService.setAudioRouteAllowed(allowed);
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+
+ /**
+ * Returns whether audio routing is allowed.
+ *
+ * Note: This is an internal function and shouldn't be exposed
+ */
+ public boolean getAudioRouteAllowed() {
+ if (VDBG) log("getAudioRouteAllowed");
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.getAudioRouteAllowed();
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
* Initiates a connection of audio channel.
*
* It setup SCO channel with remote connected Handsfree AG device.
diff --git a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
index e518b7d..79ae4e4 100644
--- a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
@@ -62,6 +62,8 @@
int getAudioState(in BluetoothDevice device);
boolean connectAudio();
boolean disconnectAudio();
+ void setAudioRouteAllowed(boolean allowed);
+ boolean getAudioRouteAllowed();
Bundle getCurrentAgFeatures(in BluetoothDevice device);
}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 1fcfaca..51796eb 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -513,7 +513,7 @@
* {@link CameraManager#registerAvailabilityCallback} to be notified of such availability
* changes.</p>
*
- * @see registerAvailabilityCallback
+ * @see #registerAvailabilityCallback
*/
public static abstract class AvailabilityCallback {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d305f4d..8b804e8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2415,6 +2415,7 @@
* 1 PFLAG3_SCROLL_INDICATOR_START
* 1 PFLAG3_SCROLL_INDICATOR_END
* 1 PFLAG3_ASSIST_BLOCKED
+ * 1111111 PFLAG3_POINTER_ICON_MASK
* |-------|-------|-------|-------|
*/
@@ -2611,6 +2612,36 @@
static final int PFLAG3_ASSIST_BLOCKED = 0x4000;
/**
+ * The mask for use with private flags indicating bits used for pointer icon shapes.
+ */
+ static final int PFLAG3_POINTER_ICON_MASK = 0x7f8000;
+
+ /**
+ * Left-shift used for pointer icon shape values in private flags.
+ */
+ static final int PFLAG3_POINTER_ICON_LSHIFT = 15;
+
+ /**
+ * Value indicating {@link PointerIcon.STYLE_NOT_SPECIFIED}.
+ */
+ private static final int PFLAG3_POINTER_ICON_NOT_SPECIFIED = 0 << PFLAG3_POINTER_ICON_LSHIFT;
+
+ /**
+ * Value indicating {@link PointerIcon.STYLE_NULL}.
+ */
+ private static final int PFLAG3_POINTER_ICON_NULL = 1 << PFLAG3_POINTER_ICON_LSHIFT;
+
+ /**
+ * Value incicating {@link PointerIcon.STYLE_CUSTOM}.
+ */
+ private static final int PFLAG3_POINTER_ICON_CUSTOM = 2 << PFLAG3_POINTER_ICON_LSHIFT;
+
+ /**
+ * The base value for other pointer icon shapes.
+ */
+ private static final int PFLAG3_POINTER_ICON_VALUE_START = 3 << PFLAG3_POINTER_ICON_LSHIFT;
+
+ /**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
*
@@ -5341,21 +5372,53 @@
}
/**
- * Call this view's OnLongClickListener, if it is defined. Invokes the context menu if the
- * OnLongClickListener did not consume the event.
+ * Calls this view's OnLongClickListener, if it is defined. Invokes the
+ * context menu if the OnLongClickListener did not consume the event.
*
- * @return True if one of the above receivers consumed the event, false otherwise.
+ * @return {@code true} if one of the above receivers consumed the event,
+ * {@code false} otherwise
*/
public boolean performLongClick() {
+ return performLongClickInternal(false, 0, 0);
+ }
+
+ /**
+ * Calls this view's OnLongClickListener, if it is defined. Invokes the
+ * context menu if the OnLongClickListener did not consume the event,
+ * anchoring it to an (x,y) coordinate.
+ *
+ * @param x x coordinate of the anchoring touch event
+ * @param y y coordinate of the anchoring touch event
+ * @return {@code true} if one of the above receivers consumed the event,
+ * {@code false} otherwise
+ */
+ public boolean performLongClick(float x, float y) {
+ return performLongClickInternal(true, x, y);
+ }
+
+ /**
+ * Calls this view's OnLongClickListener, if it is defined. Invokes the
+ * context menu if the OnLongClickListener did not consume the event,
+ * optionally anchoring it to an (x,y) coordinate.
+ *
+ * @param isAnchored whether this long click is anchored to a touch event
+ * @param x x coordinate of the anchoring touch event, ignored if
+ * {@code isAnchored} is set to {@code false}
+ * @param y y coordinate of the anchoring touch event, ignored if
+ * {@code isAnchored} is set to {@code false}
+ * @return {@code true} if one of the above receivers consumed the event,
+ * {@code false} otherwise
+ */
+ private boolean performLongClickInternal(boolean isAnchored, float x, float y) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
boolean handled = false;
- ListenerInfo li = mListenerInfo;
+ final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLongClickListener != null) {
handled = li.mOnLongClickListener.onLongClick(View.this);
}
if (!handled) {
- handled = showContextMenu();
+ handled = isAnchored ? showContextMenu(x, y) : showContextMenu();
}
if (handled) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
@@ -10002,32 +10065,36 @@
* KeyEvent.Callback.onKeyDown()}: perform press of the view
* when {@link KeyEvent#KEYCODE_DPAD_CENTER} or {@link KeyEvent#KEYCODE_ENTER}
* is released, if the view is enabled and clickable.
+ * <p>
+ * Key presses in software keyboards will generally NOT trigger this
+ * listener, although some may elect to do so in some situations. Do not
+ * rely on this to catch software key presses.
*
- * <p>Key presses in software keyboards will generally NOT trigger this listener,
- * although some may elect to do so in some situations. Do not rely on this to
- * catch software key presses.
- *
- * @param keyCode A key code that represents the button pressed, from
- * {@link android.view.KeyEvent}.
- * @param event The KeyEvent object that defines the button action.
+ * @param keyCode a key code that represents the button pressed, from
+ * {@link android.view.KeyEvent}
+ * @param event the KeyEvent object that defines the button action
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
- boolean result = false;
-
if (KeyEvent.isConfirmKey(keyCode)) {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return true;
}
- // Long clickable items don't necessarily have to be clickable
- if (((mViewFlags & CLICKABLE) == CLICKABLE ||
- (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) &&
- (event.getRepeatCount() == 0)) {
- setPressed(true);
- checkForLongClick(0);
+
+ // Long clickable items don't necessarily have to be clickable.
+ if (((mViewFlags & CLICKABLE) == CLICKABLE
+ || (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
+ && (event.getRepeatCount() == 0)) {
+ // For the purposes of menu anchoring and drawable hotspots,
+ // key events are considered to be at the center of the view.
+ final float x = getWidth() / 2f;
+ final float y = getHeight() / 2f;
+ setPressed(true, x, y);
+ checkForLongClick(0, x, y);
return true;
}
}
- return result;
+
+ return false;
}
/**
@@ -10555,7 +10622,7 @@
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
- checkForLongClick(0);
+ checkForLongClick(0, x, y);
}
break;
@@ -19996,13 +20063,14 @@
}
}
- private void checkForLongClick(int delayOffset) {
+ private void checkForLongClick(int delayOffset, float x, float y) {
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
+ mPendingCheckForLongPress.setAnchor(x, y);
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress,
ViewConfiguration.getLongPressTimeout() - delayOffset);
@@ -21001,7 +21069,40 @@
/** @hide */
public int getPointerShape(MotionEvent event, float x, float y) {
- return PointerIcon.STYLE_NOT_SPECIFIED;
+ final int value = (mPrivateFlags3 & PFLAG3_POINTER_ICON_MASK);
+ switch (value) {
+ case PFLAG3_POINTER_ICON_NOT_SPECIFIED:
+ return PointerIcon.STYLE_NOT_SPECIFIED;
+ case PFLAG3_POINTER_ICON_NULL:
+ return PointerIcon.STYLE_NULL;
+ case PFLAG3_POINTER_ICON_CUSTOM:
+ return PointerIcon.STYLE_CUSTOM;
+ default:
+ return ((value - PFLAG3_POINTER_ICON_VALUE_START) >> PFLAG3_POINTER_ICON_LSHIFT)
+ + PointerIcon.STYLE_ARROW;
+ }
+ }
+
+ /** @hide */
+ public void setPointerShape(int pointerShape) {
+ int newValue;
+ if (pointerShape == PointerIcon.STYLE_NOT_SPECIFIED) {
+ newValue = PFLAG3_POINTER_ICON_NOT_SPECIFIED;
+ } else if (pointerShape == PointerIcon.STYLE_NULL) {
+ newValue = PFLAG3_POINTER_ICON_NULL;
+ } else if (pointerShape == PointerIcon.STYLE_CUSTOM) {
+ newValue = PFLAG3_POINTER_ICON_CUSTOM;
+ } else if (pointerShape >= PointerIcon.STYLE_ARROW
+ && pointerShape <= PointerIcon.STYLE_GRABBING) {
+ newValue = ((pointerShape - PointerIcon.STYLE_ARROW) << PFLAG3_POINTER_ICON_LSHIFT)
+ + PFLAG3_POINTER_ICON_VALUE_START;
+ } else {
+ Log.w(VIEW_LOG_TAG, "Invalid pointer shape " + pointerShape + " is specified.");
+ return;
+ }
+ if (newValue != (mPrivateFlags3 & PFLAG3_POINTER_ICON_MASK)) {
+ mPrivateFlags3 = (mPrivateFlags3 & ~PFLAG3_POINTER_ICON_MASK) | newValue;
+ }
}
//
@@ -21358,17 +21459,24 @@
private final class CheckForLongPress implements Runnable {
private int mOriginalWindowAttachCount;
+ private float mX;
+ private float mY;
@Override
public void run() {
if (isPressed() && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
- if (performLongClick()) {
+ if (performLongClick(mX, mY)) {
mHasPerformedLongPress = true;
}
}
}
+ public void setAnchor(float x, float y) {
+ mX = x;
+ mY = y;
+ }
+
public void rememberWindowAttachCount() {
mOriginalWindowAttachCount = mWindowAttachCount;
}
@@ -21382,7 +21490,7 @@
public void run() {
mPrivateFlags &= ~PFLAG_PREPRESSED;
setPressed(true, x, y);
- checkForLongClick(ViewConfiguration.getTapTimeout());
+ checkForLongClick(ViewConfiguration.getTapTimeout(), x, y);
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 7c7ad91..db978a6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4435,6 +4435,10 @@
}
}
}
+
+ if (mCurrentDragStartEvent != null && child.getVisibility() == VISIBLE) {
+ notifyChildOfDragStart(child);
+ }
}
private void addInArray(View child, int index) {
@@ -4686,6 +4690,10 @@
mTransientIndices.set(i, oldIndex - 1);
}
}
+
+ if (mCurrentDragStartEvent != null) {
+ mChildrenInterestedInDrag.remove(view);
+ }
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 1b17736..1735e1b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3083,8 +3083,32 @@
return "ACTION_PASTE";
case ACTION_SET_SELECTION:
return "ACTION_SET_SELECTION";
+ case ACTION_EXPAND:
+ return "ACTION_EXPAND";
+ case ACTION_COLLAPSE:
+ return "ACTION_COLLAPSE";
+ case ACTION_DISMISS:
+ return "ACTION_DISMISS";
+ case ACTION_SET_TEXT:
+ return "ACTION_SET_TEXT";
+ case R.id.accessibilityActionShowOnScreen:
+ return "ACTION_SHOW_ON_SCREEN";
+ case R.id.accessibilityActionScrollToPosition:
+ return "ACTION_SCROLL_TO_POSITION";
+ case R.id.accessibilityActionScrollUp:
+ return "ACTION_SCROLL_UP";
+ case R.id.accessibilityActionScrollLeft:
+ return "ACTION_SCROLL_LEFT";
+ case R.id.accessibilityActionScrollDown:
+ return "ACTION_SCROLL_DOWN";
+ case R.id.accessibilityActionScrollRight:
+ return "ACTION_SCROLL_RIGHT";
+ case R.id.accessibilityActionSetProgress:
+ return "ACTION_SET_PROGRESS";
+ case R.id.accessibilityActionContextClick:
+ return "ACTION_CONTEXT_CLICK";
default:
- return"ACTION_UNKNOWN";
+ return "ACTION_UNKNOWN";
}
}
diff --git a/core/java/android/webkit/WebResourceRequest.java b/core/java/android/webkit/WebResourceRequest.java
index 0760d2b..23e9a0d 100644
--- a/core/java/android/webkit/WebResourceRequest.java
+++ b/core/java/android/webkit/WebResourceRequest.java
@@ -40,6 +40,13 @@
boolean isForMainFrame();
/**
+ * Gets whether the request was a result of a redirect.
+ *
+ * @return whether the request was a result of a redirect.
+ */
+ boolean isRedirect();
+
+ /**
* Gets whether a gesture (such as a click) was associated with the request.
* For security reasons in certain situations this method may return false even though the
* sequence of events which caused the request to be created was initiated by a user gesture.
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index f2bb55a..0e5034d 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -38,12 +38,42 @@
* @param url The url to be loaded.
* @return True if the host application wants to leave the current WebView
* and handle the url itself, otherwise return false.
+ * @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest)
+ * shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead.
*/
+ @Deprecated
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
/**
+ * Give the host application a chance to take over the control when a new
+ * url is about to be loaded in the current WebView. If WebViewClient is not
+ * provided, by default WebView will ask Activity Manager to choose the
+ * proper handler for the url. If WebViewClient is provided, return true
+ * means the host application handles the url, while return false means the
+ * current WebView handles the url.
+ *
+ * <p>Notes:
+ * <ul>
+ * <li>This method is not called for requests using the POST "method".</li>
+ * <li>This method is also called for subframes with non-http schemes, thus it is
+ * strongly disadvised to unconditionally call {@link WebView#loadUrl(String)}
+ * with the request's url from inside the method and then return true,
+ * as this will make WebView to attempt loading a non-http url, and thus fail.</li>
+ * </ul>
+ * </p>
+ *
+ * @param view The WebView that is initiating the callback.
+ * @param request Object containing the details of the request.
+ * @return True if the host application wants to leave the current WebView
+ * and handle the url itself, otherwise return false.
+ */
+ public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+ return shouldOverrideUrlLoading(view, request.getUrl().toString());
+ }
+
+ /**
* Notify the host application that a page has started loading. This method
* is called once for each main frame load so a page with iframes or
* framesets will call onPageStarted one time for the main frame. This also
diff --git a/core/java/com/android/internal/alsa/AlsaCardsParser.java b/core/java/com/android/internal/alsa/AlsaCardsParser.java
index 67ee085..17e8c9c 100644
--- a/core/java/com/android/internal/alsa/AlsaCardsParser.java
+++ b/core/java/com/android/internal/alsa/AlsaCardsParser.java
@@ -94,6 +94,12 @@
public String textFormat() {
return mCardName + " : " + mCardDescription;
}
+
+ public void log(int listIndex) {
+ Slog.d(TAG, "" + listIndex +
+ " [" + mCardNum + " " + mCardName + " : " + mCardDescription +
+ " usb:" + mIsUsb);
+ }
}
public AlsaCardsParser() {}
@@ -169,9 +175,41 @@
// return -1 if none found
public int getDefaultUsbCard() {
+ // save the current list of devices
+ ArrayList<AlsaCardsParser.AlsaCardRecord> prevRecs = mCardRecords;
+ if (DEBUG) {
+ LogDevices("Previous Devices:", prevRecs);
+ }
+
+ // get the new list of devices
+ scan();
+ if (DEBUG) {
+ LogDevices("Current Devices:", mCardRecords);
+ }
+
+ // Calculate the difference between the old and new device list
+ ArrayList<AlsaCardRecord> newRecs = getNewCardRecords(prevRecs);
+ if (DEBUG) {
+ LogDevices("New Devices:", newRecs);
+ }
+
// Choose the most-recently added EXTERNAL card
+ // Check recently added devices
+ for (AlsaCardRecord rec : newRecs) {
+ if (DEBUG) {
+ Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
+ }
+ if (rec.mIsUsb) {
+ // Found it
+ return rec.mCardNum;
+ }
+ }
+
// or return the first added EXTERNAL card?
- for (AlsaCardRecord rec : mCardRecords) {
+ for (AlsaCardRecord rec : prevRecs) {
+ if (DEBUG) {
+ Slog.d(TAG, rec.mCardName + " card:" + rec.mCardNum + " usb:" + rec.mIsUsb);
+ }
if (rec.mIsUsb) {
return rec.mCardNum;
}
@@ -183,11 +221,17 @@
public int getDefaultCard() {
// return an external card if possible
int card = getDefaultUsbCard();
+ if (DEBUG) {
+ Slog.d(TAG, "getDefaultCard() default usb card:" + card);
+ }
if (card < 0 && getNumCardRecords() > 0) {
// otherwise return the (internal) card with the highest number
card = getCardRecordAt(getNumCardRecords() - 1).mCardNum;
}
+ if (DEBUG) {
+ Slog.d(TAG, " returns card:" + card);
+ }
return card;
}
@@ -222,4 +266,13 @@
}
}
}
+
+ static public void LogDevices(String caption, ArrayList<AlsaCardRecord> deviceList) {
+ Slog.d(TAG, caption + " ----------------");
+ int listIndex = 0;
+ for (AlsaCardRecord device : deviceList) {
+ device.log(listIndex++);
+ }
+ Slog.d(TAG, "----------------");
+ }
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index afef763..7fab31f 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -5409,7 +5409,7 @@
* @Return Returns true if the window should show a non client decor.
**/
private static boolean hasNonClientDecor(int workspaceId) {
- return workspaceId == FREEFORM_WORKSPACE_STACK_ID || workspaceId == PINNED_STACK_ID;
+ return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
}
/**
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 20d00b0..7b69c9e 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -189,6 +189,7 @@
external/pdfium/core/include/fpdfdoc \
external/pdfium/fpdfsdk/include \
external/pdfium/public \
+ external/skia/include/private \
external/skia/src/core \
external/skia/src/effects \
external/skia/src/images \
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 28bc7fe..ecaf951 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -165,7 +165,7 @@
virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
const SkImageInfo& info = bitmap->info();
- if (info.fColorType == kUnknown_SkColorType) {
+ if (info.colorType() == kUnknown_SkColorType) {
ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration");
return false;
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 93259e7..068517a 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -486,7 +486,7 @@
android::Bitmap* GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable) {
const SkImageInfo& info = bitmap->info();
- if (info.fColorType == kUnknown_SkColorType) {
+ if (info.colorType() == kUnknown_SkColorType) {
doThrowIAE(env, "unknown bitmap configuration");
return NULL;
}
@@ -538,7 +538,7 @@
bool GraphicsJNI::allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) {
const SkImageInfo& info = bitmap->info();
- if (info.fColorType == kUnknown_SkColorType) {
+ if (info.colorType() == kUnknown_SkColorType) {
doThrowIAE(env, "unknown bitmap configuration");
return NULL;
}
@@ -581,7 +581,7 @@
int fd;
const SkImageInfo& info = bitmap->info();
- if (info.fColorType == kUnknown_SkColorType) {
+ if (info.colorType() == kUnknown_SkColorType) {
doThrowIAE(env, "unknown bitmap configuration");
return nullptr;
}
@@ -625,7 +625,7 @@
android::Bitmap* GraphicsJNI::mapAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
SkColorTable* ctable, int fd, void* addr, bool readOnly) {
const SkImageInfo& info = bitmap->info();
- if (info.fColorType == kUnknown_SkColorType) {
+ if (info.colorType() == kUnknown_SkColorType) {
doThrowIAE(env, "unknown bitmap configuration");
return nullptr;
}
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 24055e7..da96b93 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -313,12 +313,11 @@
return 0;
}
+
SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
convertPixelFormat(outBuffer.format),
- kPremul_SkAlphaType);
- if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
- info.fAlphaType = kOpaque_SkAlphaType;
- }
+ outBuffer.format == PIXEL_FORMAT_RGBX_8888 ?
+ kOpaque_SkAlphaType : kPremul_SkAlphaType);
SkBitmap bitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 65ebb663..931ad54 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -138,35 +138,36 @@
return NULL;
}
- SkImageInfo screenshotInfo;
- screenshotInfo.fWidth = screenshot->getWidth();
- screenshotInfo.fHeight = screenshot->getHeight();
-
+ SkColorType colorType;
+ SkAlphaType alphaType;
switch (screenshot->getFormat()) {
case PIXEL_FORMAT_RGBX_8888: {
- screenshotInfo.fColorType = kRGBA_8888_SkColorType;
- screenshotInfo.fAlphaType = kOpaque_SkAlphaType;
+ colorType = kRGBA_8888_SkColorType;
+ alphaType = kOpaque_SkAlphaType;
break;
}
case PIXEL_FORMAT_RGBA_8888: {
- screenshotInfo.fColorType = kRGBA_8888_SkColorType;
- screenshotInfo.fAlphaType = kPremul_SkAlphaType;
+ colorType = kRGBA_8888_SkColorType;
+ alphaType = kPremul_SkAlphaType;
break;
}
case PIXEL_FORMAT_RGB_565: {
- screenshotInfo.fColorType = kRGB_565_SkColorType;
- screenshotInfo.fAlphaType = kOpaque_SkAlphaType;
+ colorType = kRGB_565_SkColorType;
+ alphaType = kOpaque_SkAlphaType;
break;
}
default: {
return NULL;
}
}
+ SkImageInfo screenshotInfo = SkImageInfo::Make(screenshot->getWidth(),
+ screenshot->getHeight(),
+ colorType, alphaType);
const size_t rowBytes =
screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat());
- if (!screenshotInfo.fWidth || !screenshotInfo.fHeight) {
+ if (!screenshotInfo.width() || !screenshotInfo.height()) {
return NULL;
}
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index b736a17..e185281 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -72,29 +72,25 @@
// FIXME: consider exporting this to share (e.g. android_view_Surface.cpp)
static inline SkImageInfo convertPixelFormat(const ANativeWindow_Buffer& buffer) {
- SkImageInfo info;
- info.fWidth = buffer.width;
- info.fHeight = buffer.height;
+ SkColorType colorType = kUnknown_SkColorType;
+ SkAlphaType alphaType = kOpaque_SkAlphaType;
switch (buffer.format) {
case WINDOW_FORMAT_RGBA_8888:
- info.fColorType = kN32_SkColorType;
- info.fAlphaType = kPremul_SkAlphaType;
+ colorType = kN32_SkColorType;
+ alphaType = kPremul_SkAlphaType;
break;
case WINDOW_FORMAT_RGBX_8888:
- info.fColorType = kN32_SkColorType;
- info.fAlphaType = kOpaque_SkAlphaType;
+ colorType = kN32_SkColorType;
+ alphaType = kOpaque_SkAlphaType;
break;
case WINDOW_FORMAT_RGB_565:
- info.fColorType = kRGB_565_SkColorType;
- info.fAlphaType = kOpaque_SkAlphaType;
+ colorType = kRGB_565_SkColorType;
+ alphaType = kOpaque_SkAlphaType;
break;
default:
- info.fColorType = kUnknown_SkColorType;
- // switch to kUnknown_SkAlphaType when its in skia
- info.fAlphaType = kOpaque_SkAlphaType;
break;
}
- return info;
+ return SkImageInfo::Make(buffer.width, buffer.height, colorType, alphaType);
}
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 39eda58..5828829 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1476,7 +1476,7 @@
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:label="@string/permlab_systemAlertWindow"
android:description="@string/permdesc_systemAlertWindow"
- android:protectionLevel="signature|preinstalled|appop|pre23" />
+ android:protectionLevel="signature|preinstalled|appop|pre23|development" />
<!-- ================================== -->
<!-- Permissions affecting the system wallpaper -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 093ea80..d7dd3ec 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5018,6 +5018,12 @@
<attr name="autoMirrored" format="boolean" />
</declare-styleable>
+ <!-- Drawable class used to wrap other drawables. -->
+ <declare-styleable name="DrawableWrapper">
+ <!-- The wrapped drawable. -->
+ <attr name="drawable" />
+ </declare-styleable>
+
<!-- Drawable used to render several states. Each state is represented by
a child drawable. -->
<declare-styleable name="StateListDrawable">
@@ -5385,6 +5391,7 @@
<attr name="color" />
</declare-styleable>
+ <!-- Drawable used to wrap and inset another drawable. -->
<declare-styleable name="InsetDrawable">
<attr name="visible" />
<attr name="drawable" />
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 4fc5ede..971a3a2 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -50,7 +50,7 @@
* Creates a new animated rotating drawable with no wrapped drawable.
*/
public AnimatedRotateDrawable() {
- this(new AnimatedRotateState(null), null);
+ this(new AnimatedRotateState(null, null), null);
}
@Override
@@ -126,58 +126,22 @@
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimatedRotateDrawable);
- super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
+ super.inflate(r, parser, attrs, theme);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
updateLocalState();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
- // If we're not waiting on a theme, verify required attributes.
- if (getDrawable() == null && (mState.mThemeAttrs == null
- || mState.mThemeAttrs[R.styleable.AnimatedRotateDrawable_drawable] == 0)) {
- throw new XmlPullParserException(a.getPositionDescription()
- + ": <animated-rotate> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
- }
- }
-
@Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
final AnimatedRotateState state = mState;
-
- if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotX)) {
- final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
- state.mPivotXRel = tv.type == TypedValue.TYPE_FRACTION;
- state.mPivotX = state.mPivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
- }
-
- if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotY)) {
- final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
- state.mPivotYRel = tv.type == TypedValue.TYPE_FRACTION;
- state.mPivotY = state.mPivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
- }
-
- setFramesCount(a.getInt(
- R.styleable.AnimatedRotateDrawable_framesCount, state.mFramesCount));
- setFramesDuration(a.getInt(
- R.styleable.AnimatedRotateDrawable_frameDuration, state.mFrameDuration));
-
- final Drawable dr = a.getDrawable(R.styleable.AnimatedRotateDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
- }
-
- @Override
- public void applyTheme(@Nullable Theme t) {
- final AnimatedRotateState state = mState;
if (state == null) {
return;
}
@@ -195,13 +159,49 @@
}
}
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
-
updateLocalState();
}
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
+ // If we're not waiting on a theme, verify required attributes.
+ if (getDrawable() == null && (mState.mThemeAttrs == null
+ || mState.mThemeAttrs[R.styleable.AnimatedRotateDrawable_drawable] == 0)) {
+ throw new XmlPullParserException(a.getPositionDescription()
+ + ": <animated-rotate> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ }
+
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
+ final AnimatedRotateState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotX)) {
+ final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
+ state.mPivotXRel = tv.type == TypedValue.TYPE_FRACTION;
+ state.mPivotX = state.mPivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
+ }
+
+ if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotY)) {
+ final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
+ state.mPivotYRel = tv.type == TypedValue.TYPE_FRACTION;
+ state.mPivotY = state.mPivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
+ }
+
+ setFramesCount(a.getInt(
+ R.styleable.AnimatedRotateDrawable_framesCount, state.mFramesCount));
+ setFramesDuration(a.getInt(
+ R.styleable.AnimatedRotateDrawable_frameDuration, state.mFrameDuration));
+ }
+
public void setFramesCount(int framesCount) {
mState.mFramesCount = framesCount;
mIncrement = 360.0f / mState.mFramesCount;
@@ -211,7 +211,15 @@
mState.mFrameDuration = framesDuration;
}
+ @Override
+ DrawableWrapperState mutateConstantState() {
+ mState = new AnimatedRotateState(mState, null);
+ return mState;
+ }
+
static final class AnimatedRotateState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
boolean mPivotXRel = false;
float mPivotX = 0;
boolean mPivotYRel = false;
@@ -219,8 +227,8 @@
int mFrameDuration = 150;
int mFramesCount = 12;
- public AnimatedRotateState(AnimatedRotateState orig) {
- super(orig);
+ public AnimatedRotateState(AnimatedRotateState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mPivotXRel = orig.mPivotXRel;
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index 31fccd0..cdd336d 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -21,6 +21,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
@@ -59,7 +61,7 @@
private ClipState mState;
ClipDrawable() {
- this(new ClipState(null), null);
+ this(new ClipState(null, null), null);
}
/**
@@ -72,7 +74,7 @@
* {@link #VERTICAL}
*/
public ClipDrawable(Drawable drawable, int gravity, int orientation) {
- this(new ClipState(null), null);
+ this(new ClipState(null, null), null);
mState.mGravity = gravity;
mState.mOrientation = orientation;
@@ -81,46 +83,24 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ClipDrawable);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ClipDrawable);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
- // If we're not waiting on a theme, verify required attributes.
- if (getDrawable() == null && (mState.mThemeAttrs == null
- || mState.mThemeAttrs[R.styleable.ClipDrawable_drawable] == 0)) {
- throw new XmlPullParserException(a.getPositionDescription()
- + ": <clip> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
- }
- }
-
@Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
final ClipState state = mState;
- state.mOrientation = a.getInt(
- R.styleable.ClipDrawable_clipOrientation, state.mOrientation);
- state.mGravity = a.getInt(
- R.styleable.ClipDrawable_gravity, state.mGravity);
-
- final Drawable dr = a.getDrawable(R.styleable.ClipDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
- }
-
- @Override
- public void applyTheme(Theme t) {
- final ClipState state = mState;
if (state == null) {
return;
}
@@ -136,10 +116,34 @@
a.recycle();
}
}
+ }
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
+ // If we're not waiting on a theme, verify required attributes.
+ if (getDrawable() == null && (mState.mThemeAttrs == null
+ || mState.mThemeAttrs[R.styleable.ClipDrawable_drawable] == 0)) {
+ throw new XmlPullParserException(a.getPositionDescription()
+ + ": <clip> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ }
+
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
+ final ClipState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ state.mOrientation = a.getInt(
+ R.styleable.ClipDrawable_clipOrientation, state.mOrientation);
+ state.mGravity = a.getInt(
+ R.styleable.ClipDrawable_gravity, state.mGravity);
}
@Override
@@ -200,12 +204,20 @@
}
}
+ @Override
+ DrawableWrapperState mutateConstantState() {
+ mState = new ClipState(mState, null);
+ return mState;
+ }
+
static final class ClipState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
int mOrientation = HORIZONTAL;
int mGravity = Gravity.LEFT;
- ClipState(ClipState orig) {
- super(orig);
+ ClipState(ClipState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mOrientation = orig.mOrientation;
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index b95c183..ff28777 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -55,6 +55,8 @@
import java.util.Arrays;
import java.util.Collection;
+import com.android.internal.R;
+
/**
* A Drawable is a general abstraction for "something that can be drawn." Most
* often you will deal with Drawable as the type of resource retrieved for
@@ -791,8 +793,10 @@
/**
* Applies the specified theme to this Drawable and its children.
+ *
+ * @param t the theme to apply
*/
- public void applyTheme(@SuppressWarnings("unused") Theme t) {
+ public void applyTheme(@NonNull @SuppressWarnings("unused") Theme t) {
}
public boolean canApplyTheme() {
@@ -1177,8 +1181,8 @@
*
* @see #inflate(Resources, XmlPullParser, AttributeSet, Theme)
*/
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
- throws XmlPullParserException, IOException {
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs) throws XmlPullParserException, IOException {
inflate(r, parser, attrs, null);
}
@@ -1192,17 +1196,11 @@
* @throws XmlPullParserException
* @throws IOException
*/
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
- final TypedArray a;
- if (theme != null) {
- a = theme.obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.Drawable, 0, 0);
- } else {
- a = r.obtainAttributes(attrs, com.android.internal.R.styleable.Drawable);
- }
-
- inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.Drawable_visible);
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.Drawable);
+ mVisible = a.getBoolean(R.styleable.Drawable_visible, mVisible);
a.recycle();
}
@@ -1212,8 +1210,8 @@
* @throws XmlPullParserException
* @throws IOException
*/
- void inflateWithAttributes(Resources r, XmlPullParser parser, TypedArray attrs, int visibleAttr)
- throws XmlPullParserException, IOException {
+ void inflateWithAttributes(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull TypedArray attrs, int visibleAttr) throws XmlPullParserException, IOException {
mVisible = attrs.getBoolean(visibleAttr, mVisible);
}
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index 9185e1a..c427870 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -16,6 +16,8 @@
package android.graphics.drawable;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -23,6 +25,7 @@
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -33,6 +36,7 @@
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.view.View;
import java.io.IOException;
@@ -112,7 +116,66 @@
return mDrawable;
}
- void updateStateFromTypedArray(TypedArray a) {
+ @Override
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+
+ final DrawableWrapperState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // The density may have changed since the last update. This will
+ // apply scaling to any existing constant state properties.
+ final int densityDpi = r.getDisplayMetrics().densityDpi;
+ final int targetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ state.setDensity(targetDensity);
+
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.DrawableWrapper);
+ updateStateFromTypedArray(a);
+ a.recycle();
+
+ inflateChildDrawable(r, parser, attrs, theme);
+ }
+
+ @Override
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
+
+ // If we load the drawable later as part of updating from the typed
+ // array, it will already be themed correctly. So, we can theme the
+ // local drawable first.
+ if (mDrawable != null && mDrawable.canApplyTheme()) {
+ mDrawable.applyTheme(t);
+ }
+
+ final DrawableWrapperState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ final int densityDpi = t.getResources().getDisplayMetrics().densityDpi;
+ final int density = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ state.setDensity(density);
+
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(
+ state.mThemeAttrs, R.styleable.DrawableWrapper);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+ }
+
+ /**
+ * Updates constant state properties from the provided typed array.
+ * <p>
+ * Implementing subclasses should call through to the super method first.
+ *
+ * @param a the typed array rom which properties should be read
+ */
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
final DrawableWrapperState state = mState;
if (state == null) {
return;
@@ -124,20 +187,8 @@
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
- // TODO: Consider using R.styleable.DrawableWrapper_drawable
- }
-
- @Override
- public void applyTheme(Resources.Theme t) {
- super.applyTheme(t);
-
- final DrawableWrapperState state = mState;
- if (state == null) {
- return;
- }
-
- if (mDrawable != null && mDrawable.canApplyTheme()) {
- mDrawable.applyTheme(t);
+ if (a.hasValueOrEmpty(R.styleable.DrawableWrapper_drawable)) {
+ setDrawable(a.getDrawable(R.styleable.DrawableWrapper_drawable));
}
}
@@ -371,8 +422,9 @@
* child element will take precedence over any other child elements or
* explicit drawable attribute.
*/
- void inflateChildDrawable(Resources r, XmlPullParser parser, AttributeSet attrs,
- Resources.Theme theme) throws XmlPullParserException, IOException {
+ private void inflateChildDrawable(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
// Seek to the first child element.
Drawable dr = null;
int type;
@@ -390,17 +442,61 @@
}
abstract static class DrawableWrapperState extends Drawable.ConstantState {
- int[] mThemeAttrs;
+ private int[] mThemeAttrs;
+
int mChangingConfigurations;
+ int mDensity = DisplayMetrics.DENSITY_DEFAULT;
Drawable.ConstantState mDrawableState;
- DrawableWrapperState(DrawableWrapperState orig) {
+ DrawableWrapperState(@Nullable DrawableWrapperState orig, @Nullable Resources res) {
if (orig != null) {
mThemeAttrs = orig.mThemeAttrs;
mChangingConfigurations = orig.mChangingConfigurations;
mDrawableState = orig.mDrawableState;
}
+
+ final int density;
+ if (res != null) {
+ density = res.getDisplayMetrics().densityDpi;
+ } else if (orig != null) {
+ density = orig.mDensity;
+ } else {
+ density = 0;
+ }
+
+ mDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
+ }
+
+ /**
+ * Sets the constant state density.
+ * <p>
+ * If the density has been previously set, dispatches the change to
+ * subclasses so that density-dependent properties may be scaled as
+ * necessary.
+ *
+ * @param targetDensity the new constant state density
+ */
+ public final void setDensity(int targetDensity) {
+ if (mDensity != targetDensity) {
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
+
+ onDensityChanged(sourceDensity, targetDensity);
+ }
+ }
+
+ /**
+ * Called when the constant state density changes.
+ * <p>
+ * Subclasses with density-dependent constant state properties should
+ * override this method and scale their properties as necessary.
+ *
+ * @param sourceDensity the previous constant state density
+ * @param targetDensity the new constant state density
+ */
+ void onDensityChanged(int sourceDensity, int targetDensity) {
+ // Stub method.
}
@Override
@@ -425,7 +521,7 @@
}
@Override
- public abstract Drawable newDrawable(Resources res);
+ public abstract Drawable newDrawable(@Nullable Resources res);
@Override
public int getChangingConfigurations() {
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index e1ebdbb..927b9c9 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -22,14 +22,17 @@
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import java.io.IOException;
@@ -58,7 +61,7 @@
* No-arg constructor used by drawable inflation.
*/
InsetDrawable() {
- this(new InsetState(null), null);
+ this(new InsetState(null, null), null);
}
/**
@@ -67,7 +70,7 @@
* @param drawable The drawable to inset.
* @param inset Inset in pixels around the drawable.
*/
- public InsetDrawable(Drawable drawable, int inset) {
+ public InsetDrawable(@Nullable Drawable drawable, int inset) {
this(drawable, inset, inset, inset, inset);
}
@@ -80,9 +83,9 @@
* @param insetRight Right inset in pixels.
* @param insetBottom Bottom inset in pixels.
*/
- public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,int insetRight,
- int insetBottom) {
- this(new InsetState(null), null);
+ public InsetDrawable(@Nullable Drawable drawable, int insetLeft, int insetTop,
+ int insetRight, int insetBottom) {
+ this(new InsetState(null, null), null);
mState.mInsetLeft = insetLeft;
mState.mInsetTop = insetTop;
@@ -93,70 +96,24 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.InsetDrawable);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.InsetDrawable);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
- // If we're not waiting on a theme, verify required attributes.
- if (getDrawable() == null && (mState.mThemeAttrs == null
- || mState.mThemeAttrs[R.styleable.InsetDrawable_drawable] == 0)) {
- throw new XmlPullParserException(a.getPositionDescription()
- + ": <inset> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
- }
- }
-
@Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
final InsetState state = mState;
- final int N = a.getIndexCount();
- for (int i = 0; i < N; i++) {
- final int attr = a.getIndex(i);
- switch (attr) {
- case R.styleable.InsetDrawable_drawable:
- final Drawable dr = a.getDrawable(attr);
- if (dr != null) {
- setDrawable(dr);
- }
- break;
- case R.styleable.InsetDrawable_inset:
- final int inset = a.getDimensionPixelOffset(attr, Integer.MIN_VALUE);
- if (inset != Integer.MIN_VALUE) {
- state.mInsetLeft = inset;
- state.mInsetTop = inset;
- state.mInsetRight = inset;
- state.mInsetBottom = inset;
- }
- break;
- case R.styleable.InsetDrawable_insetLeft:
- state.mInsetLeft = a.getDimensionPixelOffset(attr, state.mInsetLeft);
- break;
- case R.styleable.InsetDrawable_insetTop:
- state.mInsetTop = a.getDimensionPixelOffset(attr, state.mInsetTop);
- break;
- case R.styleable.InsetDrawable_insetRight:
- state.mInsetRight = a.getDimensionPixelOffset(attr, state.mInsetRight);
- break;
- case R.styleable.InsetDrawable_insetBottom:
- state.mInsetBottom = a.getDimensionPixelOffset(attr, state.mInsetBottom);
- break;
- }
- }
- }
-
- @Override
- public void applyTheme(Theme t) {
- final InsetState state = mState;
if (state == null) {
return;
}
@@ -172,10 +129,47 @@
a.recycle();
}
}
+ }
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
+ // If we're not waiting on a theme, verify required attributes.
+ if (getDrawable() == null && (mState.mThemeAttrs == null
+ || mState.mThemeAttrs[R.styleable.InsetDrawable_drawable] == 0)) {
+ throw new XmlPullParserException(a.getPositionDescription()
+ + ": <inset> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ }
+
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
+ final InsetState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ // Inset attribute may be overridden by more specific attributes.
+ if (a.hasValue(R.styleable.InsetDrawable_inset)) {
+ final int inset = a.getDimensionPixelOffset(R.styleable.InsetDrawable_inset, 0);
+ state.mInsetLeft = inset;
+ state.mInsetTop = inset;
+ state.mInsetRight = inset;
+ state.mInsetBottom = inset;
+ }
+
+ state.mInsetLeft = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetLeft, state.mInsetLeft);
+ state.mInsetRight = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetRight, state.mInsetRight);
+ state.mInsetTop = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetTop, state.mInsetTop);
+ state.mInsetBottom = a.getDimensionPixelOffset(
+ R.styleable.InsetDrawable_insetBottom, state.mInsetBottom);
}
@Override
@@ -243,30 +237,72 @@
@Override
DrawableWrapperState mutateConstantState() {
- mState = new InsetState(mState);
+ mState = new InsetState(mState, null);
return mState;
}
static final class InsetState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
int mInsetLeft = 0;
int mInsetTop = 0;
int mInsetRight = 0;
int mInsetBottom = 0;
- InsetState(InsetState orig) {
- super(orig);
+ InsetState(@Nullable InsetState orig, @Nullable Resources res) {
+ super(orig, res);
if (orig != null) {
mInsetLeft = orig.mInsetLeft;
mInsetTop = orig.mInsetTop;
mInsetRight = orig.mInsetRight;
mInsetBottom = orig.mInsetBottom;
+
+ if (orig.mDensity != mDensity) {
+ applyDensityScaling(orig.mDensity, mDensity);
+ }
}
}
@Override
- public Drawable newDrawable(Resources res) {
- return new InsetDrawable(this, res);
+ void onDensityChanged(int sourceDensity, int targetDensity) {
+ super.onDensityChanged(sourceDensity, targetDensity);
+
+ applyDensityScaling(sourceDensity, targetDensity);
+ }
+
+ /**
+ * Called when the constant state density changes to scale
+ * density-dependent properties specific to insets.
+ *
+ * @param sourceDensity the previous constant state density
+ * @param targetDensity the new constant state density
+ */
+ private void applyDensityScaling(int sourceDensity, int targetDensity) {
+ mInsetLeft = Bitmap.scaleFromDensity(mInsetLeft, sourceDensity, targetDensity);
+ mInsetTop = Bitmap.scaleFromDensity(mInsetTop, sourceDensity, targetDensity);
+ mInsetRight = Bitmap.scaleFromDensity(mInsetRight, sourceDensity, targetDensity);
+ mInsetBottom = Bitmap.scaleFromDensity(mInsetBottom, sourceDensity, targetDensity);
+ }
+
+ @Override
+ public Drawable newDrawable(@Nullable Resources res) {
+ // If this drawable is being created for a different density,
+ // just create a new constant state and call it a day.
+ final InsetState state;
+ if (res != null) {
+ final int densityDpi = res.getDisplayMetrics().densityDpi;
+ final int density = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ if (density != mDensity) {
+ state = new InsetState(this, res);
+ } else {
+ state = this;
+ }
+ } else {
+ state = this;
+ }
+
+ return new InsetDrawable(state, res);
}
}
@@ -274,7 +310,7 @@
* The one constructor to rule them all. This is called by all public
* constructors to set the state and initialize local properties.
*/
- private InsetDrawable(InsetState state, Resources res) {
+ private InsetDrawable(@NonNull InsetState state, @Nullable Resources res) {
super(state, res);
mState = state;
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 036a078..1531ba2 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -21,6 +21,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.content.res.Resources;
@@ -58,22 +60,46 @@
* Creates a new rotating drawable with no wrapped drawable.
*/
public RotateDrawable() {
- this(new RotateState(null), null);
+ this(new RotateState(null, null), null);
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.RotateDrawable);
- super.inflateWithAttributes(r, parser, a, R.styleable.RotateDrawable_visible);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
+ super.inflate(r, parser, attrs, theme);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
+ @Override
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
+
+ final RotateState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.RotateDrawable);
+ try {
+ updateStateFromTypedArray(a);
+ verifyRequiredAttributes(a);
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException(e);
+ } finally {
+ a.recycle();
+ }
+ }
+ }
+
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.RotateDrawable_drawable] == 0)) {
@@ -83,11 +109,14 @@
}
}
- @Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
-
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
final RotateState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
@@ -109,35 +138,6 @@
state.mToDegrees = a.getFloat(
R.styleable.RotateDrawable_toDegrees, state.mToDegrees);
state.mCurrentDegrees = state.mFromDegrees;
-
- final Drawable dr = a.getDrawable(R.styleable.RotateDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
- }
-
- @Override
- public void applyTheme(Theme t) {
- final RotateState state = mState;
- if (state == null) {
- return;
- }
-
- if (state.mThemeAttrs != null) {
- final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.RotateDrawable);
- try {
- updateStateFromTypedArray(a);
- verifyRequiredAttributes(a);
- } catch (XmlPullParserException e) {
- throw new RuntimeException(e);
- } finally {
- a.recycle();
- }
- }
-
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
}
@Override
@@ -316,11 +316,13 @@
@Override
DrawableWrapperState mutateConstantState() {
- mState = new RotateState(mState);
+ mState = new RotateState(mState, null);
return mState;
}
static final class RotateState extends DrawableWrapper.DrawableWrapperState {
+ private int[] mThemeAttrs;
+
boolean mPivotXRel = true;
float mPivotX = 0.5f;
boolean mPivotYRel = true;
@@ -329,8 +331,8 @@
float mToDegrees = 360.0f;
float mCurrentDegrees = 0.0f;
- RotateState(RotateState orig) {
- super(orig);
+ RotateState(RotateState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mPivotXRel = orig.mPivotXRel;
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index f9206b7..f87c19a 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -21,6 +21,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -67,7 +69,7 @@
private ScaleState mState;
ScaleDrawable() {
- this(new ScaleState(null), null);
+ this(new ScaleState(null, null), null);
}
/**
@@ -83,7 +85,7 @@
* is at the maximum value, or -1 to not scale height
*/
public ScaleDrawable(Drawable drawable, int gravity, float scaleWidth, float scaleHeight) {
- this(new ScaleState(null), null);
+ this(new ScaleState(null, null), null);
mState.mGravity = gravity;
mState.mScaleWidth = scaleWidth;
@@ -93,20 +95,46 @@
}
@Override
- public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ScaleDrawable);
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ScaleDrawable);
updateStateFromTypedArray(a);
- inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
updateLocalState();
}
- private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
+ @Override
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
+
+ final ScaleState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ScaleDrawable);
+ try {
+ updateStateFromTypedArray(a);
+ verifyRequiredAttributes(a);
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException(e);
+ } finally {
+ a.recycle();
+ }
+ }
+
+ updateLocalState();
+ }
+
+ private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.ScaleDrawable_drawable] == 0)) {
@@ -116,11 +144,18 @@
}
}
- @Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
-
+ private void updateStateFromTypedArray(@NonNull TypedArray a) {
final ScaleState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
state.mScaleWidth = getPercent(a,
R.styleable.ScaleDrawable_scaleWidth, state.mScaleWidth);
state.mScaleHeight = getPercent(a,
@@ -131,11 +166,6 @@
R.styleable.ScaleDrawable_useIntrinsicSizeAsMinimum, state.mUseIntrinsicSizeAsMin);
state.mInitialLevel = a.getInt(
R.styleable.ScaleDrawable_level, state.mInitialLevel);
-
- final Drawable dr = a.getDrawable(R.styleable.ScaleDrawable_drawable);
- if (dr != null) {
- setDrawable(dr);
- }
}
private static float getPercent(TypedArray a, int index, float defaultValue) {
@@ -157,33 +187,6 @@
}
@Override
- public void applyTheme(Theme t) {
- final ScaleState state = mState;
- if (state == null) {
- return;
- }
-
- if (state.mThemeAttrs != null) {
- final TypedArray a = t.resolveAttributes(
- state.mThemeAttrs, R.styleable.ScaleDrawable);
- try {
- updateStateFromTypedArray(a);
- verifyRequiredAttributes(a);
- } catch (XmlPullParserException e) {
- throw new RuntimeException(e);
- } finally {
- a.recycle();
- }
- }
-
- // The drawable may have changed as a result of applying the theme, so
- // apply the theme to the wrapped drawable last.
- super.applyTheme(t);
-
- updateLocalState();
- }
-
- @Override
public void draw(Canvas canvas) {
final Drawable d = getDrawable();
if (d != null && d.getLevel() != 0) {
@@ -243,7 +246,7 @@
@Override
DrawableWrapperState mutateConstantState() {
- mState = new ScaleState(mState);
+ mState = new ScaleState(mState, null);
return mState;
}
@@ -251,14 +254,16 @@
/** Constant used to disable scaling for a particular dimension. */
private static final float DO_NOT_SCALE = -1.0f;
+ private int[] mThemeAttrs;
+
float mScaleWidth = DO_NOT_SCALE;
float mScaleHeight = DO_NOT_SCALE;
int mGravity = Gravity.LEFT;
boolean mUseIntrinsicSizeAsMin = false;
int mInitialLevel = 0;
- ScaleState(ScaleState orig) {
- super(orig);
+ ScaleState(ScaleState orig, Resources res) {
+ super(orig, res);
if (orig != null) {
mScaleWidth = orig.mScaleWidth;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 4385e70..d94c91d 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -113,6 +113,7 @@
endef
hwui_c_includes += \
+ external/skia/include/private \
external/skia/src/core
hwui_shared_libraries := \
@@ -202,6 +203,7 @@
unit_tests/CanvasStateTests.cpp \
unit_tests/ClipAreaTests.cpp \
unit_tests/DamageAccumulatorTests.cpp \
+ unit_tests/FatVectorTests.cpp \
unit_tests/LinearAllocatorTests.cpp \
unit_tests/StringUtilsTests.cpp
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index bad3972..f5e5735 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -584,7 +584,7 @@
// it to the bitmap pile
SkBitmap bitmap;
SkShader::TileMode xy[2];
- if (shader->asABitmap(&bitmap, nullptr, xy) == SkShader::kDefault_BitmapType) {
+ if (shader->isABitmap(&bitmap, nullptr, xy)) {
refBitmap(bitmap);
return;
}
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 1f113bc..273af3a 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -458,7 +458,7 @@
// it to the bitmap pile
SkBitmap bitmap;
SkShader::TileMode xy[2];
- if (shader->asABitmap(&bitmap, nullptr, xy) == SkShader::kDefault_BitmapType) {
+ if (shader->isABitmap(&bitmap, nullptr, xy)) {
refBitmap(bitmap);
return;
}
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 9c32b1a..454ee24 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -27,6 +27,8 @@
#include "Snapshot.h"
#include "SkDrawFilter.h"
+#include "SkPaint.h"
+#include "SkTLazy.h"
#include <vector>
namespace android {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 8011869..39cb8e9 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -254,7 +254,8 @@
bool transformUpdateNeeded = false;
if (!mLayer) {
- mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight());
+ mLayer = LayerRenderer::createRenderLayer(
+ info.canvasContext.getRenderState(), getWidth(), getHeight());
applyLayerPropertiesToLayer(info);
damageSelf(info);
transformUpdateNeeded = true;
@@ -304,12 +305,10 @@
info.renderer->pushLayerUpdate(mLayer);
}
- if (info.canvasContext) {
- // There might be prefetched layers that need to be accounted for.
- // That might be us, so tell CanvasContext that this layer is in the
- // tree and should not be destroyed.
- info.canvasContext->markLayerInUse(this);
- }
+ // There might be prefetched layers that need to be accounted for.
+ // That might be us, so tell CanvasContext that this layer is in the
+ // tree and should not be destroyed.
+ info.canvasContext.markLayerInUse(this);
}
/**
@@ -430,7 +429,8 @@
TextureCache& cache = Caches::getInstance().textureCache;
info.out.hasFunctors |= subtree->getFunctors().size();
for (auto&& bitmapResource : subtree->getBitmapResources()) {
- info.prepareTextures = cache.prefetchAndMarkInUse(info.canvasContext, bitmapResource);
+ void* ownerToken = &info.canvasContext;
+ info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource);
}
for (auto&& op : subtree->getChildren()) {
RenderNode* childNode = op->renderNode;
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 36633b5..a8f8134 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -194,15 +194,13 @@
void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
SkCanvas* newCanvas = new SkCanvas(bitmap);
- SkASSERT(newCanvas);
if (!bitmap.isNull()) {
// Copy the canvas matrix & clip state.
newCanvas->setMatrix(mCanvas->getTotalMatrix());
- if (NULL != mCanvas->getDevice() && NULL != newCanvas->getDevice()) {
- ClipCopier copier(newCanvas);
- mCanvas->replayClips(&copier);
- }
+
+ ClipCopier copier(newCanvas);
+ mCanvas->replayClips(&copier);
}
// unrefs the existing canvas
@@ -217,15 +215,15 @@
// ----------------------------------------------------------------------------
bool SkiaCanvas::isOpaque() {
- return mCanvas->getDevice()->accessBitmap(false).isOpaque();
+ return mCanvas->imageInfo().isOpaque();
}
int SkiaCanvas::width() {
- return mCanvas->getBaseLayerSize().width();
+ return mCanvas->imageInfo().width();
}
int SkiaCanvas::height() {
- return mCanvas->getBaseLayerSize().height();
+ return mCanvas->imageInfo().height();
}
// ----------------------------------------------------------------------------
@@ -581,7 +579,7 @@
float dstRight, float dstBottom, const SkPaint* paint) {
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- mCanvas->drawBitmapRectToRect(bitmap, &srcRect, dstRect, paint);
+ mCanvas->drawBitmapRect(bitmap, srcRect, dstRect, paint);
}
void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index c3f5eb2..2d5f70f 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -125,7 +125,7 @@
}
void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr,
- const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags) {
+ const SkRect& dst, const SkPaint* paint, SrcRectConstraint) {
SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height());
// TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
index 0de9650..2fe4327 100644
--- a/libs/hwui/SkiaCanvasProxy.h
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -63,7 +63,7 @@
virtual void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
const SkPaint*) override;
virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst,
- const SkPaint* paint, DrawBitmapRectFlags flags) override;
+ const SkPaint* paint, SrcRectConstraint) override;
virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
const SkRect& dst, const SkPaint*) override;
virtual void onDrawSprite(const SkBitmap&, int left, int top,
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 6c105cf..83652c6 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -204,7 +204,7 @@
SkiaShaderData::BitmapShaderData* outData) {
SkBitmap bitmap;
SkShader::TileMode xy[2];
- if (shader.asABitmap(&bitmap, nullptr, xy) != SkShader::kDefault_BitmapType) {
+ if (!shader.isABitmap(&bitmap, nullptr, xy)) {
return false;
}
@@ -272,7 +272,7 @@
}
// The shader is not a gradient. Check for a bitmap shader.
- if (shader.asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) {
+ if (shader.isABitmap()) {
return kBitmap_SkiaShaderType;
}
return kNone_SkiaShaderType;
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 98e6146..1c31487 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -55,70 +55,46 @@
MODE_RT_ONLY,
};
- explicit TreeInfo(TraversalMode mode, RenderState& renderState)
- : mode(mode)
- , prepareTextures(mode == MODE_FULL)
- , runAnimations(true)
- , damageAccumulator(nullptr)
- , renderState(renderState)
- , renderer(nullptr)
- , errorHandler(nullptr)
- , canvasContext(nullptr)
- {}
-
- explicit TreeInfo(TraversalMode mode, const TreeInfo& clone)
- : mode(mode)
- , prepareTextures(mode == MODE_FULL)
- , runAnimations(clone.runAnimations)
- , damageAccumulator(clone.damageAccumulator)
- , renderState(clone.renderState)
- , renderer(clone.renderer)
- , errorHandler(clone.errorHandler)
- , canvasContext(clone.canvasContext)
+ TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext)
+ : mode(mode)
+ , prepareTextures(mode == MODE_FULL)
+ , canvasContext(canvasContext)
{}
TraversalMode mode;
// TODO: Remove this? Currently this is used to signal to stop preparing
// textures if we run out of cache space.
bool prepareTextures;
+ renderthread::CanvasContext& canvasContext;
// TODO: buildLayer uses this to suppress running any animations, but this
// should probably be refactored somehow. The reason this is done is
// because buildLayer is not setup for injecting the animationHook, as well
// as this being otherwise wasted work as all the animators will be
// re-evaluated when the frame is actually drawn
- bool runAnimations;
+ bool runAnimations = true;
// Must not be null during actual usage
- DamageAccumulator* damageAccumulator;
- RenderState& renderState;
+ DamageAccumulator* damageAccumulator = nullptr;
// The renderer that will be drawing the next frame. Use this to push any
// layer updates or similar. May be NULL.
- OpenGLRenderer* renderer;
- ErrorHandler* errorHandler;
- // May be NULL (TODO: can it really?)
- renderthread::CanvasContext* canvasContext;
+ OpenGLRenderer* renderer = nullptr;
+ ErrorHandler* errorHandler = nullptr;
struct Out {
- Out()
- : hasFunctors(false)
- , hasAnimations(false)
- , requiresUiRedraw(false)
- , canDrawThisFrame(true)
- {}
- bool hasFunctors;
+ bool hasFunctors = false;
// This is only updated if evaluateAnimations is true
- bool hasAnimations;
+ bool hasAnimations = false;
// This is set to true if there is an animation that RenderThread cannot
// animate itself, such as if hasFunctors is true
// This is only set if hasAnimations is true
- bool requiresUiRedraw;
+ bool requiresUiRedraw = false;
// This is set to true if draw() can be called this frame
// false means that we must delay until the next vsync pulse as frame
// production is outrunning consumption
// NOTE that if this is false CanvasContext will set either requiresUiRedraw
// *OR* will post itself for the next vsync automatically, use this
// only to avoid calling draw()
- bool canDrawThisFrame;
+ bool canDrawThisFrame = true;
} out;
// TODO: Damage calculations
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 73af4c4..fac26dc 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -199,7 +199,6 @@
info.damageAccumulator = &mDamageAccumulator;
info.renderer = mCanvas;
- info.canvasContext = this;
mAnimationContext->startFrame(info.mode);
for (const sp<RenderNode>& node : mRenderNodes) {
@@ -507,7 +506,7 @@
.setVsync(mRenderThread.timeLord().computeFrameTimeNanos(),
mRenderThread.timeLord().latestVsync());
- TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
+ TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node);
if (info.out.canDrawThisFrame) {
draw();
@@ -551,7 +550,7 @@
// buildLayer() will leave the tree in an unknown state, so we must stop drawing
stopDrawing();
- TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
+ TreeInfo info(TreeInfo::MODE_FULL, *this);
info.damageAccumulator = &mDamageAccumulator;
info.renderer = mCanvas;
info.runAnimations = false;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index f362584..30e6562 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -130,6 +130,10 @@
mContentDrawBounds.set(left, top, right, bottom);
}
+ RenderState& getRenderState() {
+ return mRenderThread.renderState();
+ }
+
private:
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index a47c9ec..ab860c7 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -87,7 +87,7 @@
bool canUnblockUiThread;
bool canDrawThisFrame;
{
- TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
+ TreeInfo info(TreeInfo::MODE_FULL, *mContext);
canUnblockUiThread = syncFrameState(info);
canDrawThisFrame = info.out.canDrawThisFrame;
}
diff --git a/libs/hwui/unit_tests/FatVectorTests.cpp b/libs/hwui/unit_tests/FatVectorTests.cpp
new file mode 100644
index 0000000..fb760ac5
--- /dev/null
+++ b/libs/hwui/unit_tests/FatVectorTests.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <utils/FatVector.h>
+
+#include <unit_tests/TestUtils.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+template<class VectorType>
+static bool allocationIsInternal(VectorType& v) {
+ // allocation array (from &v[0] to &v[0] + v.capacity) is
+ // located within the vector object itself
+ return (char*)(&v) <= (char*)(&v[0])
+ && (char*)(&v + 1) >= (char*)(&v[0] + v.capacity());
+}
+
+TEST(FatVector, baseline) {
+ // Verify allocation behavior FatVector contrasts against - allocations are always external
+ std::vector<int> v;
+ for (int i = 0; i < 50; i++) {
+ v.push_back(i);
+ EXPECT_FALSE(allocationIsInternal(v));
+ }
+}
+
+TEST(FatVector, simpleAllocate) {
+ FatVector<int, 4> v;
+ EXPECT_EQ(4u, v.capacity());
+
+ // can insert 4 items into internal buffer
+ for (int i = 0; i < 4; i++) {
+ v.push_back(i);
+ EXPECT_TRUE(allocationIsInternal(v));
+ }
+
+ // then will fall back to external allocation
+ for (int i = 5; i < 50; i++) {
+ v.push_back(i);
+ EXPECT_FALSE(allocationIsInternal(v));
+ }
+}
+
+TEST(FatVector, shrink) {
+ FatVector<int, 10> v;
+ EXPECT_TRUE(allocationIsInternal(v));
+
+ // push into external alloc
+ v.resize(11);
+ EXPECT_FALSE(allocationIsInternal(v));
+
+ // shrinking back to internal alloc succeeds
+ // note that shrinking further will succeed, but is a waste
+ v.resize(10);
+ v.shrink_to_fit();
+ EXPECT_TRUE(allocationIsInternal(v));
+}
+
+TEST(FatVector, destructorInternal) {
+ int count = 0;
+ {
+ // push 1 into external allocation, verify destruction happens once
+ FatVector<TestUtils::SignalingDtor, 0> v;
+ v.emplace_back(&count);
+ EXPECT_FALSE(allocationIsInternal(v));
+ EXPECT_EQ(0, count);
+ }
+ EXPECT_EQ(1, count);
+}
+
+TEST(FatVector, destructorExternal) {
+ int count = 0;
+ {
+ // push 10 into internal allocation, verify 10 destructors called
+ FatVector<TestUtils::SignalingDtor, 10> v;
+ for (int i = 0; i < 10; i++) {
+ v.emplace_back(&count);
+ EXPECT_TRUE(allocationIsInternal(v));
+ }
+ EXPECT_EQ(0, count);
+ }
+ EXPECT_EQ(10, count);
+}
diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
index 02cd77a..0f6b249 100644
--- a/libs/hwui/unit_tests/LinearAllocatorTests.cpp
+++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
@@ -17,6 +17,8 @@
#include <gtest/gtest.h>
#include <utils/LinearAllocator.h>
+#include <unit_tests/TestUtils.h>
+
using namespace android;
using namespace android::uirenderer;
@@ -25,27 +27,6 @@
int two = 2;
};
-class SignalingDtor {
-public:
- SignalingDtor() {
- mDestroyed = nullptr;
- }
- SignalingDtor(bool* destroyedSignal) {
- mDestroyed = destroyedSignal;
- *mDestroyed = false;
- }
- virtual ~SignalingDtor() {
- if (mDestroyed) {
- *mDestroyed = true;
- }
- }
- void setSignal(bool* destroyedSignal) {
- mDestroyed = destroyedSignal;
- }
-private:
- bool* mDestroyed;
-};
-
TEST(LinearAllocator, alloc) {
LinearAllocator la;
EXPECT_EQ(0u, la.usedSize());
@@ -62,31 +43,31 @@
}
TEST(LinearAllocator, dtor) {
- bool destroyed[10];
+ int destroyed[10] = { 0 };
{
LinearAllocator la;
for (int i = 0; i < 5; i++) {
- la.alloc<SignalingDtor>()->setSignal(destroyed + i);
+ la.alloc<TestUtils::SignalingDtor>()->setSignal(destroyed + i);
la.alloc<SimplePair>();
}
la.alloc(100);
for (int i = 0; i < 5; i++) {
- auto sd = new (la) SignalingDtor(destroyed + 5 + i);
+ auto sd = new (la) TestUtils::SignalingDtor(destroyed + 5 + i);
la.autoDestroy(sd);
new (la) SimplePair();
}
la.alloc(100);
for (int i = 0; i < 10; i++) {
- EXPECT_FALSE(destroyed[i]);
+ EXPECT_EQ(0, destroyed[i]);
}
}
for (int i = 0; i < 10; i++) {
- EXPECT_TRUE(destroyed[i]);
+ EXPECT_EQ(1, destroyed[i]);
}
}
TEST(LinearAllocator, rewind) {
- bool destroyed;
+ int destroyed = 0;
{
LinearAllocator la;
auto addr = la.alloc(100);
@@ -94,17 +75,16 @@
la.rewindIfLastAlloc(addr, 100);
EXPECT_GT(16u, la.usedSize());
size_t emptySize = la.usedSize();
- auto sigdtor = la.alloc<SignalingDtor>();
+ auto sigdtor = la.alloc<TestUtils::SignalingDtor>();
sigdtor->setSignal(&destroyed);
- EXPECT_FALSE(destroyed);
+ EXPECT_EQ(0, destroyed);
EXPECT_LE(emptySize, la.usedSize());
la.rewindIfLastAlloc(sigdtor);
- EXPECT_TRUE(destroyed);
+ EXPECT_EQ(1, destroyed);
EXPECT_EQ(emptySize, la.usedSize());
- destroyed = false;
}
// Checking for a double-destroy case
- EXPECT_EQ(destroyed, false);
+ EXPECT_EQ(1, destroyed);
}
TEST(LinearStdAllocator, simpleAllocate) {
diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/unit_tests/TestUtils.h
index 99ecc9b..5b09fda 100644
--- a/libs/hwui/unit_tests/TestUtils.h
+++ b/libs/hwui/unit_tests/TestUtils.h
@@ -39,6 +39,24 @@
class TestUtils {
public:
+ class SignalingDtor {
+ public:
+ SignalingDtor()
+ : mSignal(nullptr) {}
+ SignalingDtor(int* signal)
+ : mSignal(signal) {}
+ void setSignal(int* signal) {
+ mSignal = signal;
+ }
+ ~SignalingDtor() {
+ if (mSignal) {
+ (*mSignal)++;
+ }
+ }
+ private:
+ int* mSignal;
+ };
+
static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) {
for (int i = 0; i < 16; i++) {
if (!MathUtils::areEqual(a[i], b[i])) {
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
new file mode 100644
index 0000000..c3c16c5a
--- /dev/null
+++ b/libs/hwui/utils/FatVector.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_FAT_VECTOR_H
+#define ANDROID_FAT_VECTOR_H
+
+#include "utils/Macros.h"
+
+#include <stddef.h>
+#include <type_traits>
+#include <utils/Log.h>
+
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+template <typename T, size_t SIZE>
+class InlineStdAllocator {
+public:
+ struct Allocation {
+ PREVENT_COPY_AND_ASSIGN(Allocation);
+ public:
+ Allocation() {};
+ // char array instead of T array, so memory is uninitialized, with no destructors run
+ char array[sizeof(T) * SIZE];
+ bool inUse = false;
+ };
+
+ typedef T value_type; // needed to implement std::allocator
+ typedef T* pointer; // needed to implement std::allocator
+
+ InlineStdAllocator(Allocation& allocation)
+ : mAllocation(allocation) {}
+ InlineStdAllocator(const InlineStdAllocator& other)
+ : mAllocation(other.mAllocation) {}
+ ~InlineStdAllocator() {}
+
+ T* allocate(size_t num, const void* = 0) {
+ if (!mAllocation.inUse && num <= SIZE) {
+ mAllocation.inUse = true;
+ return (T*) mAllocation.array;
+ } else {
+ return (T*) malloc(num * sizeof(T));
+ }
+ }
+
+ void deallocate(pointer p, size_t num) {
+ if (p == (T*)mAllocation.array) {
+ mAllocation.inUse = false;
+ } else {
+ // 'free' instead of delete here - destruction handled separately
+ free(p);
+ }
+ }
+ Allocation& mAllocation;
+};
+
+/**
+ * std::vector with SIZE elements preallocated into an internal buffer.
+ *
+ * Useful for avoiding the cost of malloc in cases where only SIZE or
+ * fewer elements are needed in the common case.
+ */
+template <typename T, size_t SIZE>
+class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
+public:
+ FatVector() : std::vector<T, InlineStdAllocator<T, SIZE>>(
+ InlineStdAllocator<T, SIZE>(mAllocation)) {
+ this->reserve(SIZE);
+ }
+private:
+ typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_FAT_VECTOR_H
diff --git a/libs/hwui/utils/NinePatchImpl.cpp b/libs/hwui/utils/NinePatchImpl.cpp
index f51f5df..985f3fb 100644
--- a/libs/hwui/utils/NinePatchImpl.cpp
+++ b/libs/hwui/utils/NinePatchImpl.cpp
@@ -82,7 +82,7 @@
}
} else {
SLOW_CASE:
- canvas->drawBitmapRect(bitmap, &src, dst, &paint);
+ canvas->drawBitmapRect(bitmap, SkRect::Make(src), dst, &paint);
}
}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 5476651..e0a8026 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -776,68 +776,88 @@
*/
private class MediaServiceConnection implements ServiceConnection {
@Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- if (DBG) {
- Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name
- + " binder=" + binder);
- dump();
- }
+ public void onServiceConnected(final ComponentName name, final IBinder binder) {
+ postOrRun(new Runnable() {
+ @Override
+ public void run() {
+ if (DBG) {
+ Log.d(TAG, "MediaServiceConnection.onServiceConnected name=" + name
+ + " binder=" + binder);
+ dump();
+ }
- // Make sure we are still the current connection, and that they haven't called
- // disconnect().
- if (!isCurrent("onServiceConnected")) {
- return;
- }
+ // Make sure we are still the current connection, and that they haven't called
+ // disconnect().
+ if (!isCurrent("onServiceConnected")) {
+ return;
+ }
- // Save their binder
- mServiceBinder = IMediaBrowserService.Stub.asInterface(binder);
+ // Save their binder
+ mServiceBinder = IMediaBrowserService.Stub.asInterface(binder);
- // We make a new mServiceCallbacks each time we connect so that we can drop
- // responses from previous connections.
- mServiceCallbacks = getNewServiceCallbacks();
- mState = CONNECT_STATE_CONNECTING;
+ // We make a new mServiceCallbacks each time we connect so that we can drop
+ // responses from previous connections.
+ mServiceCallbacks = getNewServiceCallbacks();
+ mState = CONNECT_STATE_CONNECTING;
- // Call connect, which is async. When we get a response from that we will
- // say that we're connected.
- try {
- if (DBG) {
- Log.d(TAG, "ServiceCallbacks.onConnect...");
- dump();
+ // Call connect, which is async. When we get a response from that we will
+ // say that we're connected.
+ try {
+ if (DBG) {
+ Log.d(TAG, "ServiceCallbacks.onConnect...");
+ dump();
+ }
+ mServiceBinder.connect(mContext.getPackageName(), mRootHints,
+ mServiceCallbacks);
+ } catch (RemoteException ex) {
+ // Connect failed, which isn't good. But the auto-reconnect on the service
+ // will take over and we will come back. We will also get the
+ // onServiceDisconnected, which has all the cleanup code. So let that do
+ // it.
+ Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
+ if (DBG) {
+ Log.d(TAG, "ServiceCallbacks.onConnect...");
+ dump();
+ }
+ }
}
- mServiceBinder.connect(mContext.getPackageName(), mRootHints, mServiceCallbacks);
- } catch (RemoteException ex) {
- // Connect failed, which isn't good. But the auto-reconnect on the service
- // will take over and we will come back. We will also get the
- // onServiceDisconnected, which has all the cleanup code. So let that do it.
- Log.w(TAG, "RemoteException during connect for " + mServiceComponent);
- if (DBG) {
- Log.d(TAG, "ServiceCallbacks.onConnect...");
- dump();
- }
- }
+ });
}
@Override
- public void onServiceDisconnected(ComponentName name) {
- if (DBG) {
- Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name
- + " this=" + this + " mServiceConnection=" + mServiceConnection);
- dump();
+ public void onServiceDisconnected(final ComponentName name) {
+ postOrRun(new Runnable() {
+ @Override
+ public void run() {
+ if (DBG) {
+ Log.d(TAG, "MediaServiceConnection.onServiceDisconnected name=" + name
+ + " this=" + this + " mServiceConnection=" + mServiceConnection);
+ dump();
+ }
+
+ // Make sure we are still the current connection, and that they haven't called
+ // disconnect().
+ if (!isCurrent("onServiceDisconnected")) {
+ return;
+ }
+
+ // Clear out what we set in onServiceConnected
+ mServiceBinder = null;
+ mServiceCallbacks = null;
+
+ // And tell the app that it's suspended.
+ mState = CONNECT_STATE_SUSPENDED;
+ mCallback.onConnectionSuspended();
+ }
+ });
+ }
+
+ private void postOrRun(Runnable r) {
+ if (Thread.currentThread() == mHandler.getLooper().getThread()) {
+ r.run();
+ } else {
+ mHandler.post(r);
}
-
- // Make sure we are still the current connection, and that they haven't called
- // disconnect().
- if (!isCurrent("onServiceDisconnected")) {
- return;
- }
-
- // Clear out what we set in onServiceConnected
- mServiceBinder = null;
- mServiceCallbacks = null;
-
- // And tell the app that it's suspended.
- mState = CONNECT_STATE_SUSPENDED;
- mCallback.onConnectionSuspended();
}
/**
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 45a8907..a09a22a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -17,7 +17,6 @@
package com.android.documentsui;
import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.State.ACTION_BROWSE;
import static com.android.documentsui.State.ACTION_CREATE;
import static com.android.documentsui.State.ACTION_MANAGE;
import static com.android.documentsui.State.MODE_GRID;
@@ -55,7 +54,6 @@
import android.os.Looper;
import android.os.OperationCanceledException;
import android.os.Parcelable;
-import android.os.SystemProperties;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.support.annotation.Nullable;
@@ -95,6 +93,7 @@
import com.android.documentsui.MultiSelectManager.Selection;
import com.android.documentsui.ProviderExecutor.Preemptable;
import com.android.documentsui.RecentsProvider.StateColumns;
+import com.android.documentsui.dirlist.FragmentTuner;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.RootInfo;
@@ -341,7 +340,7 @@
mType = getArguments().getInt(EXTRA_TYPE);
mStateKey = buildStateKey(root, doc);
- mFragmentTuner = pickFragmentTuner(state);
+ mFragmentTuner = FragmentTuner.pick(state);
mClipper = new DocumentClipper(context);
if (mType == TYPE_RECENT_OPEN) {
@@ -429,7 +428,6 @@
// Kick off loader at least once
getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
- mFragmentTuner.afterActivityCreated(this);
updateDisplayState();
}
@@ -1638,21 +1636,6 @@
}
}
- private FragmentTuner pickFragmentTuner(final State state) {
- return state.action == ACTION_BROWSE
- ? new FilesTuner()
- : new DefaultTuner(state.action);
- }
-
- /**
- * Interface for specializing the Fragment for the "host" Activity.
- * Feel free to expand the role of this class to handle other specializations.
- */
- private interface FragmentTuner {
- void updateActionMenu(Menu menu, int dirType, boolean canDelete);
- void afterActivityCreated(DirectoryFragment fragment);
- }
-
/**
* Abstract task providing support for loading documents *off*
* the main thread. And if it isn't obvious, creating a list
@@ -1673,65 +1656,6 @@
abstract void onDocumentsReady(List<DocumentInfo> docs);
}
- /**
- * Provides support for Platform specific specializations of DirectoryFragment.
- */
- private static final class DefaultTuner implements FragmentTuner {
-
- private final boolean mManaging;
-
- public DefaultTuner(int action) {
- mManaging = (action == ACTION_MANAGE);
- }
-
- @Override
- public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
- boolean copyEnabled = mManaging && dirType != TYPE_RECENT_OPEN;
- // TODO: The selection needs to be deletable.
- boolean moveEnabled =
- SystemProperties.getBoolean("debug.documentsui.enable_move", false);
- menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(copyEnabled);
-
- final MenuItem open = menu.findItem(R.id.menu_open);
- final MenuItem share = menu.findItem(R.id.menu_share);
- final MenuItem delete = menu.findItem(R.id.menu_delete);
- final MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
- final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
-
- open.setVisible(!mManaging);
- share.setVisible(mManaging);
- delete.setVisible(mManaging && canDelete);
- copyTo.setVisible(copyEnabled);
- copyTo.setEnabled(copyEnabled);
- moveTo.setVisible(moveEnabled);
- moveTo.setEnabled(moveEnabled);
- }
-
- @Override
- public void afterActivityCreated(DirectoryFragment fragment) {}
- }
-
- /**
- * Provides support for Files activity specific specializations of DirectoryFragment.
- */
- private static final class FilesTuner implements FragmentTuner {
- @Override
- public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
-
- menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(dirType != TYPE_RECENT_OPEN);
-
- menu.findItem(R.id.menu_share).setVisible(true);
- menu.findItem(R.id.menu_delete).setVisible(canDelete);
-
- menu.findItem(R.id.menu_open).setVisible(false);
- menu.findItem(R.id.menu_copy_to).setVisible(true);
- menu.findItem(R.id.menu_move_to).setVisible(true);
- }
-
- @Override
- public void afterActivityCreated(DirectoryFragment fragment) {}
- }
-
boolean isSelected(int position) {
return mSelectionManager.getSelection().contains(position);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Menus.java b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
index 3f43a3d..5277d2b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Menus.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
@@ -19,14 +19,14 @@
import android.view.Menu;
import android.view.MenuItem;
-final class Menus {
+public final class Menus {
private Menus() {}
/**
* Disables hidden menu items so that they are not invokable via command shortcuts
*/
- static void disableHiddenItems(Menu menu, MenuItem... exclusions) {
+ public static void disableHiddenItems(Menu menu, MenuItem... exclusions) {
for (int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
if (item.isVisible()) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
new file mode 100644
index 0000000..ca85cff
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui.dirlist;
+
+import static com.android.documentsui.State.ACTION_BROWSE;
+import static com.android.documentsui.State.ACTION_MANAGE;
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.os.SystemProperties;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.android.documentsui.DirectoryFragment;
+import com.android.documentsui.Menus;
+import com.android.documentsui.R;
+import com.android.documentsui.State;
+
+/**
+ * Providers support for specializing the DirectoryFragment to the "host" Activity.
+ * Feel free to expand the role of this class to handle other specializations.
+ */
+public abstract class FragmentTuner {
+ public static FragmentTuner pick(State state) {
+ switch (state.action) {
+ case ACTION_BROWSE:
+ return new FilesTuner();
+ case ACTION_MANAGE:
+ return new ManageTuner();
+ default:
+ return new DocumentsTuner();
+ }
+ }
+
+ public abstract void updateActionMenu(Menu menu, int dirType, boolean canDelete);
+
+ /**
+ * Provides support for Platform specific specializations of DirectoryFragment.
+ */
+ private static final class DocumentsTuner extends FragmentTuner {
+ @Override
+ public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+
+ boolean copyEnabled = dirType != DirectoryFragment.TYPE_RECENT_OPEN;
+ boolean moveEnabled =
+ SystemProperties.getBoolean("debug.documentsui.enable_move", false);
+ menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(copyEnabled);
+
+ final MenuItem open = menu.findItem(R.id.menu_open);
+ final MenuItem share = menu.findItem(R.id.menu_share);
+ final MenuItem delete = menu.findItem(R.id.menu_delete);
+ final MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
+ final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
+
+ open.setVisible(true);
+ share.setVisible(false);
+ delete.setVisible(false);
+ copyTo.setVisible(copyEnabled);
+ copyTo.setEnabled(copyEnabled);
+ moveTo.setVisible(moveEnabled);
+ moveTo.setEnabled(moveEnabled);
+ }
+ }
+
+ /**
+ * Provides support for Platform specific specializations of DirectoryFragment.
+ */
+ private static final class ManageTuner extends FragmentTuner {
+
+ @Override
+ public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+ checkArgument(dirType != DirectoryFragment.TYPE_RECENT_OPEN);
+
+ boolean moveEnabled =
+ SystemProperties.getBoolean("debug.documentsui.enable_move", false);
+ menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(true);
+
+ final MenuItem open = menu.findItem(R.id.menu_open);
+ final MenuItem share = menu.findItem(R.id.menu_share);
+ final MenuItem delete = menu.findItem(R.id.menu_delete);
+ final MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
+ final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
+
+ open.setVisible(false);
+ share.setVisible(false);
+ delete.setVisible(canDelete);
+ copyTo.setVisible(true);
+ copyTo.setEnabled(true);
+ moveTo.setVisible(moveEnabled);
+ moveTo.setEnabled(moveEnabled);
+ }
+ }
+
+ /**
+ * Provides support for Files activity specific specializations of DirectoryFragment.
+ */
+ private static final class FilesTuner extends FragmentTuner {
+ @Override
+ public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+
+ MenuItem copy = menu.findItem(R.id.menu_copy_to_clipboard);
+ MenuItem paste = menu.findItem(R.id.menu_paste_from_clipboard);
+ copy.setEnabled(dirType != DirectoryFragment.TYPE_RECENT_OPEN);
+
+ menu.findItem(R.id.menu_share).setVisible(true);
+ menu.findItem(R.id.menu_delete).setVisible(canDelete);
+
+ menu.findItem(R.id.menu_open).setVisible(false);
+ menu.findItem(R.id.menu_copy_to).setVisible(true);
+ menu.findItem(R.id.menu_move_to).setVisible(true);
+
+ Menus.disableHiddenItems(menu, copy, paste);
+ }
+ }
+}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index df9d44a..df4a9f0 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -20,8 +20,15 @@
* Database for MTP objects.
* The object handle which is identifier for object in MTP protocol is not stable over sessions.
* When we resume the process, we need to remap our document ID with MTP's object handle.
- * The database object remembers the map of document ID and fullpath, and helps to remap object
- * handle and document ID by comparing fullpath.
+ *
+ * If the remote MTP device is backed by typical file system, the file name
+ * is unique among files in a directory. However, MTP protocol itself does
+ * not guarantee the uniqueness of name so we cannot use fullpath as ID.
+ *
+ * Instead of fullpath, we use artificial ID generated by MtpDatabase itself. The database object
+ * remembers the map of document ID and object handle, and remaps new object handle with document ID
+ * by comparing the directory structure and object name.
+ *
* TODO: Remove @VisibleForTesting annotation when we start to use this class.
*/
@VisibleForTesting
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 1c99a23..6d9d43f 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -219,7 +219,7 @@
<string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"داده موقتاً متوقف شده است"</string>
<string name="data_usage_disabled_dialog" msgid="8453242888903772524">"چون به محدودیت داده تنظیم شده رسیدهاید، دستگاه مصرف داده را برای باقیمانده این دوره موقتاً متوقف کرده است.\n\nاگر ادامه دهید شاید موجب کسر هزینه از طرف شرکت مخابراتی شما شود."</string>
<string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"از سرگیری"</string>
- <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"اتصال اینترنتی وجود ندارد"</string>
+ <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"اتصال اینترنتی ندارید"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi متصل شد"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"جستجو برای GPS"</string>
<string name="gps_notification_found_text" msgid="4619274244146446464">"مکان تنظیم شده توسط GPS"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 3fee28e..6beed78 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -328,7 +328,7 @@
<string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarmes\nuniquement"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours… (chargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charge rapide… (chargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
- <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charge lente… (chargé à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charge lente… (chargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Changer d\'utilisateur (utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index e839aaa..185fc05 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -393,8 +393,7 @@
<string name="accessibility_volume_expand" msgid="5946812790999244205">"දිග හරින්න"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"හකුළන්න"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"තීරය අමුණන ලදි"</string>
- <!-- no translation found for screen_pinning_description (3577937698406151604) -->
- <skip />
+ <string name="screen_pinning_description" msgid="3577937698406151604">"මෙය ඔබ ගලවන තෙක් එය දසුන තුළ තබයි. ගැලවීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"හරි, තේරුණා"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"එපා ස්තූතියි"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> සඟවන්නද?"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 07c59a9..0c638a2 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -269,9 +269,6 @@
<!-- Duration of the expansion animation in the volume dialog -->
<item name="volume_expand_animation_duration" type="integer">300</item>
- <!-- Whether to show a "shelf" of apps at the bottom of the screen. -->
- <bool name="config_enableAppShelf">false</bool>
-
<!-- Whether to show the full screen user switcher. -->
<bool name="config_enableFullscreenUserSwitcher">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 00f484d..a4137b9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -232,6 +232,9 @@
<!-- The size of the lock-to-app button icon. -->
<dimen name="recents_lock_to_app_icon_size">28dp</dimen>
+ <!-- The amount to allow the stack to overscroll. -->
+ <dimen name="recents_stack_overscroll">24dp</dimen>
+
<!-- Space reserved for the cards behind the top card in the top stack -->
<dimen name="top_stack_peek_amount">12dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 6668df9..ebfacac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -64,8 +64,6 @@
}
public static class TaskStackView {
- public static final int TaskStackMinOverscrollRange = 32;
- public static final int TaskStackMaxOverscrollRange = 128;
public static final int FilterStartDelay = 25;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 9400108..4d40cb7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -54,6 +54,7 @@
private static SystemServicesProxy sSystemServicesProxy;
private static RecentsTaskLoader sTaskLoader;
+ private static RecentsConfiguration sConfiguration;
private Handler mHandler;
private RecentsImpl mImpl;
@@ -129,10 +130,15 @@
return sSystemServicesProxy;
}
+ public static RecentsConfiguration getConfiguration() {
+ return sConfiguration;
+ }
+
@Override
public void start() {
sSystemServicesProxy = new SystemServicesProxy(mContext);
sTaskLoader = new RecentsTaskLoader(mContext);
+ sConfiguration = new RecentsConfiguration(mContext);
mHandler = new Handler();
mImpl = new RecentsImpl(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 0adad85..6c8bf0f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -80,7 +80,6 @@
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
- RecentsConfiguration mConfig;
RecentsPackageMonitor mPackageMonitor;
long mLastTabKeyEventTime;
boolean mFinishedOnStartup;
@@ -174,7 +173,8 @@
}
// Start loading tasks according to the load plan
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
if (!plan.hasTasks()) {
loader.preloadTasks(plan, launchState.launchedFromHome);
}
@@ -280,7 +280,8 @@
* Dismisses recents if we are already visible and the intent is to toggle the recents view.
*/
boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
// If we currently have filtered stacks, then unfilter those first
@@ -352,7 +353,6 @@
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
// Initialize the widget host (the host id is static and does not change)
- mConfig = RecentsConfiguration.getInstance();
if (!Constants.DebugFlags.App.DisableSearchBar) {
mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
}
@@ -399,7 +399,8 @@
// If this is a new instance from a configuration change, then we have to manually trigger
// the enter animation state, or if recents was relaunched by AM, without going through
// the normal mechanisms
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
!launchState.launchedFromAppWithThumbnail;
if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
@@ -442,7 +443,8 @@
// Workaround for b/22542869, if the RecentsActivity is started again, but without going
// through SystemUI, we need to reset the config launch flags to ensure that we do not
// wait on the system to send a signal that was never queued.
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
launchState.launchedFromHome = false;
launchState.launchedFromSearchHome = false;
launchState.launchedFromAppWithThumbnail = false;
@@ -629,7 +631,8 @@
public void run() {
// If we are not launching with alt-tab and fast-toggle is enabled, then start
// the dozer now
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
if (Constants.DebugFlags.App.EnableFastToggleRecents &&
!launchState.launchedWithAltTab) {
mIterateTrigger.startDozing();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 76b666f..aca816e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -28,8 +28,6 @@
*/
public class RecentsActivityLaunchState {
- public RecentsConfiguration mConfig;
-
public boolean launchedWithAltTab;
public boolean launchedWithNoRecentTasks;
public boolean launchedFromAppWithThumbnail;
@@ -41,10 +39,6 @@
public int launchedNumVisibleTasks;
public int launchedNumVisibleThumbnails;
- RecentsActivityLaunchState(RecentsConfiguration config) {
- mConfig = config;
- }
-
/** Called when the configuration has changed, and we want to reset any configuration specific
* members. */
public void updateOnConfigurationChange() {
@@ -72,6 +66,7 @@
/** Returns whether the nav bar scrim should be visible. */
public boolean hasNavBarScrim() {
// Only show the scrim if we have recent tasks, and if the nav bar is not transposed
- return !launchedWithNoRecentTasks && !mConfig.hasTransposedNavBar;
+ RecentsConfiguration config = Recents.getConfiguration();
+ return !launchedWithNoRecentTasks && !config.hasTransposedNavBar;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index d8f4023..e2f20fd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -28,7 +28,6 @@
* tied to the current activity.
*/
public class RecentsConfiguration {
- static RecentsConfiguration sInstance;
private static final int LARGE_SCREEN_MIN_DP = 600;
private static final int XLARGE_SCREEN_MIN_DP = 720;
@@ -53,7 +52,7 @@
public static final int SVELTE_DISABLE_LOADING = 3;
// Launch states
- public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState(this);
+ public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState();
// TODO: Values determined by the current context, needs to be refactored into something that is
// agnostic of the activity context, but still calculable from the Recents component for
@@ -79,10 +78,10 @@
/** Dev options and global settings */
public boolean lockToAppEnabled;
- /** Private constructor */
- private RecentsConfiguration(Context context, SystemServicesProxy ssp) {
+ public RecentsConfiguration(Context context) {
// Load only resources that can not change after the first load either through developer
// settings or via multi window
+ SystemServicesProxy ssp = Recents.getSystemServices();
Context appContext = context.getApplicationContext();
Resources res = appContext.getResources();
useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
@@ -121,19 +120,6 @@
hasTransposedSearchBar = isLandscape && isLargeScreen && !isXLargeScreen;
}
- /** Updates the configuration to the current context */
- public static RecentsConfiguration initialize(Context context, SystemServicesProxy ssp) {
- if (sInstance == null) {
- sInstance = new RecentsConfiguration(context, ssp);
- }
- return sInstance;
- }
-
- /** Returns the current recents configuration */
- public static RecentsConfiguration getInstance() {
- return sInstance;
- }
-
/**
* Returns the activity launch state.
* TODO: This will be refactored out of RecentsConfiguration.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index b05e1fe..d02e2af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ITaskStackListener;
@@ -31,8 +33,10 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.MutableBoolean;
+import android.view.AppTransitionAnimationSpec;
import android.view.LayoutInflater;
import android.view.View;
+
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
@@ -135,7 +139,6 @@
boolean mCanReuseTaskStackViews = true;
// Task launching
- RecentsConfiguration mConfig;
Rect mSearchBarBounds = new Rect();
Rect mTaskStackBounds = new Rect();
Rect mLastTaskViewBounds = new Rect();
@@ -174,7 +177,6 @@
ssp.registerTaskStackListener(mTaskStackListener);
// Initialize the static configuration resources
- mConfig = RecentsConfiguration.initialize(mContext, ssp);
mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
@@ -205,7 +207,7 @@
public void onConfigurationChanged() {
// Don't reuse task stack views if the configuration changes
mCanReuseTaskStackViews = false;
- mConfig.updateOnConfigurationChange();
+ Recents.getConfiguration().updateOnConfigurationChange();
}
/**
@@ -445,31 +447,32 @@
* is not already bound (can be expensive)
*/
private void reloadHeaderBarLayout(boolean tryAndBindSearchWidget) {
+ RecentsConfiguration config = Recents.getConfiguration();
SystemServicesProxy ssp = Recents.getSystemServices();
Rect windowRect = ssp.getWindowRect();
// Update the configuration for the current state
- mConfig.update(mContext, ssp, ssp.getWindowRect());
+ config.update(mContext, ssp, ssp.getWindowRect());
if (!Constants.DebugFlags.App.DisableSearchBar && tryAndBindSearchWidget) {
// Try and pre-emptively bind the search widget on startup to ensure that we
// have the right thumbnail bounds to animate to.
// Note: We have to reload the widget id before we get the task stack bounds below
if (ssp.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
- mConfig.getSearchBarBounds(windowRect, mStatusBarHeight, mSearchBarBounds);
+ config.getSearchBarBounds(windowRect, mStatusBarHeight, mSearchBarBounds);
}
}
Rect systemInsets = new Rect(0, mStatusBarHeight,
- (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
- (mConfig.hasTransposedNavBar ? 0 : mNavBarHeight));
- mConfig.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
+ (config.hasTransposedNavBar ? mNavBarWidth : 0),
+ (config.hasTransposedNavBar ? 0 : mNavBarHeight));
+ config.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
mSearchBarBounds, mTaskStackBounds);
// Rebind the header bar and draw it for the transition
TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
Rect taskStackBounds = new Rect(mTaskStackBounds);
algo.setSystemInsets(systemInsets);
- algo.computeRects(windowRect.width(), windowRect.height(), taskStackBounds);
+ algo.computeRects(taskStackBounds);
Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
if (!taskViewBounds.equals(mLastTaskViewBounds)) {
mLastTaskViewBounds.set(taskViewBounds);
@@ -558,12 +561,44 @@
*/
private ActivityOptions getThumbnailTransitionActivityOptions(
ActivityManager.RunningTaskInfo topTask, TaskStack stack, TaskStackView stackView) {
+ if (topTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
+ ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
+ stackView.getScroller().setStackScrollToInitialState();
+ ArrayList<Task> tasks = stack.getTasks();
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ Task task = tasks.get(i);
+ if (SystemServicesProxy.isFreeformStack(task.key.stackId)) {
+ mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
+ stackView.getScroller().getStackScroll(), mTmpTransform, null);
+ Rect toTaskRect = new Rect();
+ mTmpTransform.rect.round(toTaskRect);
+ Bitmap thumbnail = getThumbnailBitmap(topTask, task, mTmpTransform);
+ specs.add(new AppTransitionAnimationSpec(task.key.id, thumbnail, toTaskRect));
+ }
+ }
+ AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
+ specs.toArray(specsArray);
+ return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
+ specsArray, mHandler, this);
+ } else {
+ // Update the destination rect
+ Task toTask = new Task();
+ TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
+ topTask.id, toTask);
+ RectF toTaskRect = toTransform.rect;
+ Bitmap thumbnail = getThumbnailBitmap(topTask, toTask, toTransform);
+ if (thumbnail != null) {
+ return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
+ thumbnail, (int) toTaskRect.left, (int) toTaskRect.top,
+ (int) toTaskRect.width(), (int) toTaskRect.height(), mHandler, this);
+ }
+ // If both the screenshot and thumbnail fails, then just fall back to the default transition
+ return getUnknownTransitionActivityOptions();
+ }
+ }
- // Update the destination rect
- Task toTask = new Task();
- TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
- topTask.id, toTask);
- RectF toTaskRect = toTransform.rect;
+ private Bitmap getThumbnailBitmap(ActivityManager.RunningTaskInfo topTask, Task toTask,
+ TaskViewTransform toTransform) {
Bitmap thumbnail;
if (mThumbnailTransitionBitmapCacheKey != null
&& mThumbnailTransitionBitmapCacheKey.key != null
@@ -575,14 +610,7 @@
preloadIcon(topTask);
thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
}
- if (thumbnail != null) {
- return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
- thumbnail, (int) toTaskRect.left, (int) toTaskRect.top,
- (int) toTaskRect.width(), (int) toTaskRect.height(), mHandler, this);
- }
-
- // If both the screenshot and thumbnail fails, then just fall back to the default transition
- return getUnknownTransitionActivityOptions();
+ return thumbnail;
}
/**
@@ -718,7 +746,8 @@
mStartAnimationTriggered = false;
// Update the configuration based on the launch options
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
launchState.launchedFromHome = fromSearchHome || fromHome;
launchState.launchedFromSearchHome = fromSearchHome;
launchState.launchedFromAppWithThumbnail = fromThumbnail;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
index 8e85bfc..ea6821f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java
@@ -48,6 +48,7 @@
float[] xp;
float[] px;
+ float mLength;
CurveFunction mFn;
ParametricCurveFunction mScaleFn;
@@ -79,6 +80,7 @@
dx[xStep] = (float) Math.hypot(fx[xStep] - fx[xStep - 1], step);
pLength += dx[xStep];
}
+ mLength = pLength;
// Approximate p(x), a function of cumulative progress with x, normalized to 0..1
float p = 0;
px[0] = 0f;
@@ -260,4 +262,11 @@
}
return 1f - xToP(maxX, bounds);
}
+
+ /**
+ * Returns the length of this curve.
+ */
+ public float getArcLength() {
+ return mLength;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 8de8e15..6fc4e20 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -41,6 +41,7 @@
* options specified, such that we can transition into the Recents activity seamlessly
*/
public class RecentsTaskLoadPlan {
+
private static String TAG = "RecentsTaskLoadPlan";
private static boolean DEBUG = false;
@@ -58,39 +59,50 @@
}
Context mContext;
- RecentsConfiguration mConfig;
List<ActivityManager.RecentTaskInfo> mRawTasks;
TaskStack mStack;
/** Package level ctor */
- RecentsTaskLoadPlan(Context context, RecentsConfiguration config) {
+ RecentsTaskLoadPlan(Context context) {
mContext = context;
- mConfig = config;
}
/**
- * An optimization to preload the raw list of tasks.
+ * An optimization to preload the raw list of tasks. The raw tasks are saved in least-recent
+ * to most-recent order.
*/
public synchronized void preloadRawTasks(boolean isTopTaskHome) {
SystemServicesProxy ssp = Recents.getSystemServices();
mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
UserHandle.CURRENT.getIdentifier(), isTopTaskHome);
+ // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
Collections.reverse(mRawTasks);
- if (DEBUG) Log.d(TAG, "preloadRawTasks, tasks: " + mRawTasks.size());
+ if (DEBUG) {
+ Log.d(TAG, "preloadRawTasks, tasks: " + mRawTasks.size());
+ for (ActivityManager.RecentTaskInfo info : mRawTasks) {
+ Log.d(TAG, " " + info.baseIntent + ", " + info.lastActiveTime);
+ }
+ }
}
/**
* Preloads the list of recent tasks from the system. After this call, the TaskStack will
* have a list of all the recent tasks with their metadata, not including icons or
* thumbnails which were not cached and have to be loaded.
+ *
+ * The tasks will be ordered by:
+ * - least-recent to most-recent stack tasks
+ * - least-recent to most-recent freeform tasks
*/
public synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) {
if (DEBUG) Log.d(TAG, "preloadPlan");
+ RecentsConfiguration config = Recents.getConfiguration();
SystemServicesProxy ssp = Recents.getSystemServices();
Resources res = mContext.getResources();
+ ArrayList<Task> freeformTasks = new ArrayList<>();
ArrayList<Task> stackTasks = new ArrayList<>();
if (mRawTasks == null) {
preloadRawTasks(isTopTaskHome);
@@ -113,16 +125,14 @@
int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
Bitmap icon = t.taskDescription != null
- ? t.taskDescription.getInMemoryIcon()
- : null;
+ ? t.taskDescription.getInMemoryIcon() : null;
String iconFilename = t.taskDescription != null
- ? t.taskDescription.getIconFilename()
- : null;
+ ? t.taskDescription.getIconFilename() : null;
// Add the task to the stack
Task task = new Task(taskKey, (t.id != INVALID_TASK_ID), t.affiliatedTaskId,
t.affiliatedTaskColor, activityLabel, contentDescription, activityIcon,
- activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled, icon,
+ activityColor, (i == (taskCount - 1)), config.lockToAppEnabled, icon,
iconFilename);
task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, false);
if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);
@@ -145,6 +155,7 @@
", # thumbnails: " + opts.numVisibleTaskThumbnails +
", running task id: " + opts.runningTaskId);
+ RecentsConfiguration config = Recents.getConfiguration();
SystemServicesProxy ssp = Recents.getSystemServices();
Resources res = mContext.getResources();
@@ -175,9 +186,9 @@
if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
if (task.thumbnail == null || isRunningTask) {
if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
- if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
+ if (config.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, true);
- } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
+ } else if (config.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
loadQueue.addTask(task);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index ea97b71..71e3957 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -155,7 +155,7 @@
}
}
} else {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
+ RecentsConfiguration config = Recents.getConfiguration();
SystemServicesProxy ssp = Recents.getSystemServices();
// If we've stopped the loader, then fall through to the above logic to wait on
// the load thread
@@ -320,8 +320,7 @@
/** Creates a new plan for loading the recent tasks. */
public RecentsTaskLoadPlan createLoadPlan(Context context) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context, config);
+ RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context);
return plan;
}
@@ -383,7 +382,7 @@
* out of memory.
*/
public void onTrimMemory(int level) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
+ RecentsConfiguration config = Recents.getConfiguration();
switch (level) {
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
// Stop the loader immediately when the UI is no longer visible
@@ -528,7 +527,7 @@
}
if (loadIfNotCached) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
+ RecentsConfiguration config = Recents.getConfiguration();
if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
// Load the thumbnail from the system
thumbnail = ssp.getTaskThumbnail(taskKey.id);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 0793180..60051b8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -170,6 +170,12 @@
}
}
+ public boolean isFreeformTask() {
+ // Temporarily disable:
+ return false;
+ // return SystemServicesProxy.isFreeformStack(key.stackId);
+ }
+
/** Notifies the callback listeners that this task has been loaded */
public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
this.applicationIcon = applicationIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index fb05c01..21b6bd8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -20,13 +20,12 @@
import android.graphics.Rect;
import android.view.View;
import android.view.ViewOutlineProvider;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
/* An outline provider that has a clip and outline that can be animated. */
public class AnimateableViewBounds extends ViewOutlineProvider {
- RecentsConfiguration mConfig;
-
TaskView mSourceView;
Rect mClipRect = new Rect();
Rect mClipBounds = new Rect();
@@ -35,7 +34,6 @@
final float mMinAlpha = 0.25f;
public AnimateableViewBounds(TaskView source, int cornerRadius) {
- mConfig = RecentsConfiguration.getInstance();
mSourceView = source;
mCornerRadius = cornerRadius;
setClipBottom(getClipBottom());
@@ -64,7 +62,9 @@
mClipRect.bottom = bottom;
mSourceView.invalidateOutline();
updateClipBounds();
- if (!mConfig.useHardwareLayers) {
+
+ RecentsConfiguration config = Recents.getConfiguration();
+ if (!config.useHardwareLayers) {
mSourceView.mThumbnailView.updateThumbnailVisibility(
bottom - mSourceView.getPaddingBottom());
}
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 d5d0713..a8a8259 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -81,7 +81,6 @@
public void runAfterPause(Runnable r);
}
- RecentsConfiguration mConfig;
LayoutInflater mInflater;
ArrayList<TaskStack> mStacks;
@@ -117,7 +116,6 @@
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setWillNotDraw(false);
- mConfig = RecentsConfiguration.getInstance();
mInflater = LayoutInflater.from(context);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
@@ -131,7 +129,8 @@
/** Set/get the bsp root node */
public void setTaskStack(TaskStack stack) {
- if (mConfig.getLaunchState().launchedReuseTaskStackViews) {
+ RecentsConfiguration config = Recents.getConfiguration();
+ if (config.getLaunchState().launchedReuseTaskStackViews) {
if (mTaskStackView != null) {
// If onRecentsHidden is not triggered, we need to the stack view again here
mTaskStackView.reset();
@@ -311,13 +310,14 @@
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ RecentsConfiguration config = Recents.getConfiguration();
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
// Get the search bar bounds and measure the search bar layout
Rect searchBarSpaceBounds = new Rect();
if (mSearchBar != null) {
- mConfig.getSearchBarBounds(new Rect(0, 0, width, height), mSystemInsets.top,
+ config.getSearchBarBounds(new Rect(0, 0, width, height), mSystemInsets.top,
searchBarSpaceBounds);
mSearchBar.measure(
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
@@ -325,7 +325,7 @@
}
Rect taskStackBounds = new Rect();
- mConfig.getTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
+ config.getTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
mSystemInsets.right, searchBarSpaceBounds, taskStackBounds);
if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
mTaskStackView.setTaskStackBounds(taskStackBounds, mSystemInsets);
@@ -346,11 +346,13 @@
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ RecentsConfiguration config = Recents.getConfiguration();
+
// Get the search bar bounds so that we lay it out
Rect measuredRect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
Rect searchBarSpaceBounds = new Rect();
if (mSearchBar != null) {
- mConfig.getSearchBarBounds(measuredRect,
+ config.getSearchBarBounds(measuredRect,
mSystemInsets.top, searchBarSpaceBounds);
mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top,
searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index bf045f9..f3a4390 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -19,6 +19,7 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.view.MotionEvent;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent;
@@ -73,7 +74,7 @@
public TaskStack.DockState[] getDockStatesForCurrentOrientation() {
boolean isLandscape = mRv.getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_LANDSCAPE;
- RecentsConfiguration config = RecentsConfiguration.getInstance();
+ RecentsConfiguration config = Recents.getConfiguration();
TaskStack.DockState[] dockStates = isLandscape ?
(config.isLargeScreen ? DockRegion.TABLET_LANDSCAPE : DockRegion.PHONE_LANDSCAPE) :
(config.isLargeScreen ? DockRegion.TABLET_PORTRAIT : DockRegion.PHONE_PORTRAIT);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index e04699c1..b7c1de3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -68,6 +68,7 @@
private float mInitialTouchPos;
private boolean mDragging;
+ private float mSnapBackTranslationX;
private View mCurrView;
private boolean mCanCurrViewBeDimissed;
@@ -92,6 +93,10 @@
mDensityScale = densityScale;
}
+ public void setSnapBackTranslationX(float translationX) {
+ mSnapBackTranslationX = translationX;
+ }
+
public void setPagingTouchSlop(float pagingTouchSlop) {
mPagingTouchSlop = pagingTouchSlop;
}
@@ -267,7 +272,7 @@
private void snapChild(final View view, float velocity) {
final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
- ValueAnimator anim = createTranslationAnimation(view, 0);
+ ValueAnimator anim = createTranslationAnimation(view, mSnapBackTranslationX);
int duration = SNAP_ANIM_LEN;
anim.setDuration(duration);
anim.setInterpolator(mLinearOutSlowInInterpolator);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 5fbea00..c4e2d8a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -22,6 +22,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
@@ -31,7 +32,6 @@
public class SystemBarScrimViews {
Context mContext;
- RecentsConfiguration mConfig;
View mStatusBarScrimView;
View mNavBarScrimView;
@@ -48,7 +48,6 @@
public SystemBarScrimViews(Activity activity) {
mContext = activity;
- mConfig = RecentsConfiguration.getInstance();
mStatusBarScrimView = activity.findViewById(R.id.status_bar_scrim);
mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
mNavBarScrimEnterDuration = activity.getResources().getInteger(
@@ -64,7 +63,8 @@
* the first draw.
*/
public void prepareEnterRecentsAnimation() {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
mHasNavBarScrim = launchState.hasNavBarScrim();
mShouldAnimateNavBarScrim = launchState.shouldAnimateNavBarScrim();
mHasStatusBarScrim = launchState.hasStatusBarScrim();
@@ -82,7 +82,8 @@
* Starts animating the scrim views when entering Recents.
*/
public final void onBusEvent(EnterRecentsWindowAnimationStartedEvent event) {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
int transitionEnterFromAppDelay = mContext.getResources().getInteger(
R.integer.recents_enter_from_app_transition_duration);
int transitionEnterFromHomeDelay = mContext.getResources().getInteger(
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 757e695..dfa36d8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -23,11 +23,11 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
-import android.os.SystemService;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
@@ -77,7 +77,6 @@
public void onTaskStackFilterTriggered();
public void onTaskStackUnfilterTriggered();
}
- RecentsConfiguration mConfig;
TaskStack mStack;
TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
@@ -121,10 +120,9 @@
super(context);
// Set the stack first
setStack(stack);
- mConfig = RecentsConfiguration.getInstance();
mViewPool = new ViewPool<>(context, this);
mInflater = LayoutInflater.from(context);
- mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(context, mConfig);
+ mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(context);
mFilterAlgorithm = new TaskStackViewFilterAlgorithm(this, mViewPool);
mStackScroller = new TaskStackViewScroller(context, mLayoutAlgorithm);
mStackScroller.setCallbacks(this);
@@ -633,10 +631,9 @@
}
/** Computes the stack and task rects */
- public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds,
- boolean launchedWithAltTab, boolean launchedFromHome) {
+ public void computeRects(Rect taskStackBounds) {
// Compute the rects in the stack algorithm
- mLayoutAlgorithm.computeRects(windowWidth, windowHeight, taskStackBounds);
+ mLayoutAlgorithm.computeRects(taskStackBounds);
// Update the scroll bounds
updateMinMaxScroll(false);
@@ -674,9 +671,7 @@
int height = MeasureSpec.getSize(heightMeasureSpec);
// Compute our stack/task rects
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- computeRects(width, height, mTaskStackBounds, launchState.launchedWithAltTab,
- launchState.launchedFromHome);
+ computeRects(mTaskStackBounds);
// If this is the first layout, then scroll to the front of the stack and synchronize the
// stack views immediately to load all the views
@@ -777,7 +772,8 @@
setFocusedTask(mStack.indexOfTask(launchTargetTask), false /* scrollToTask */,
false /* animated */, false /* requestViewFocus */);
} else {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
int taskOffset = launchState.launchedFromHome ? -1 : -2;
setFocusedTask(mStack.getTaskCount() + taskOffset, false /* scrollToTask */,
false /* animated */, false /* requestViewFocus */);
@@ -837,7 +833,8 @@
// animate the focused state if we are alt-tabbing now, after the window enter
// animation is completed
if (mFocusedTaskIndex != -1) {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
setFocusedTask(mFocusedTaskIndex, false /* scrollToTask */,
launchState.launchedWithAltTab);
}
@@ -1253,7 +1250,8 @@
if (taskWasFocused || ssp.isTouchExplorationEnabled()) {
// If the dismissed task was focused or if we are in touch exploration mode, then focus
// the next task
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
setFocusedTask(taskIndex - 1, true /* scrollToTask */, launchState.launchedWithAltTab);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 9a5d9bd..c74c654 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -21,6 +21,7 @@
import android.graphics.Rect;
import android.util.Log;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.ParametricCurve;
import com.android.systemui.recents.misc.Utilities;
@@ -32,14 +33,15 @@
/**
* The layout logic for a TaskStackView.
+ *
*/
public class TaskStackViewLayoutAlgorithm {
- private static final boolean DEBUG = false;
private static final String TAG = "TaskStackViewLayoutAlgorithm";
+ private static final boolean DEBUG = false;
// The min scale of the last task at the top of the curve
- private static final float STACK_PEEK_MIN_SCALE = 0.75f;
+ private static final float STACK_PEEK_MIN_SCALE = 0.85f;
// The scale of the last task
private static final float SINGLE_TASK_SCALE = 0.95f;
// The percentage of height of task to show between tasks
@@ -58,14 +60,17 @@
}
Context mContext;
- RecentsConfiguration mConfig;
// This is the view bounds inset exactly by the search bar, but without the bottom inset
// see RecentsConfiguration.getTaskStackBounds()
public Rect mStackRect = new Rect();
+
// This is the task view bounds for layout (untransformed), the rect is top-aligned to the top
// of the stack rect
public Rect mTaskRect = new Rect();
+
+ // The bounds of the freeform workspace, the rect is top-aligned to the top of the stack rect
+ public Rect mFreeformRect = new Rect();
// This is the current system insets
public Rect mSystemInsets = new Rect();
@@ -74,8 +79,13 @@
// The largest scroll progress, at this value, the front most task will be visible above the
// navigation bar
float mMaxScrollP;
+ // The scroll progress at which the stack scroll ends and the overscroll begins. This serves
+ // as the point at which we can show the freeform space.
+ float mMaxStackScrollP;
// The initial progress that the scroller is set
float mInitialScrollP;
+ // The task progress for the front-most task in the stack
+ float mFrontMostTaskP;
// The relative progress to ensure that the height between affiliated tasks is respected
float mWithinAffiliationPOffset;
@@ -89,11 +99,21 @@
// The relative progress to ensure that the offset from the bottom of the stack to the bottom
// of the task is respected
float mTaskBottomPOffset;
+ // The relative progress to ensure that the freeform workspace height is respected
+ float mFreeformWorkspacePOffset;
// The front-most task bottom offset
int mTaskBottomOffset;
- // The last computed task count
- int mNumTasks;
+ // The number of cells in the freeform workspace
+ int mFreeformCellXCount;
+ int mFreeformCellYCount;
+ // The width and height of the cells in the freeform workspace
+ int mFreeformCellWidth;
+ int mFreeformCellHeight;
+
+ // The last computed task counts
+ int mNumStackTasks;
+ int mNumFreeformTasks;
// The min/max z translations
int mMinTranslationZ;
int mMaxTranslationZ;
@@ -104,12 +124,11 @@
// Log function
static ParametricCurve sCurve;
- public TaskStackViewLayoutAlgorithm(Context context, RecentsConfiguration config) {
+ public TaskStackViewLayoutAlgorithm(Context context) {
Resources res = context.getResources();
mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
mContext = context;
- mConfig = config;
if (sCurve == null) {
sCurve = new ParametricCurve(new ParametricCurve.CurveFunction() {
// The large the XScale, the longer the flat area of the curve
@@ -153,23 +172,25 @@
}
/**
- * Computes the stack and task rects
+ * Computes the stack and task rects.
*/
- public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
- int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * taskStackBounds.width());
+ public void computeRects(Rect taskStackBounds) {
+ RecentsConfiguration config = Recents.getConfiguration();
+ int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width());
int heightPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_stack_top_padding);
// Compute the stack rect, inset from the given task stack bounds
mStackRect.set(taskStackBounds.left + widthPadding, taskStackBounds.top + heightPadding,
- taskStackBounds.right - widthPadding, windowHeight);
+ taskStackBounds.right - widthPadding, taskStackBounds.bottom);
mTaskBottomOffset = mSystemInsets.bottom + heightPadding;
// Compute the task rect, align it to the top-center square in the stack rect
- int size = Math.min(mStackRect.width(), taskStackBounds.height() - mTaskBottomOffset);
+ int size = Math.min(mStackRect.width(), mStackRect.height() - mTaskBottomOffset);
int xOffset = (mStackRect.width() - size) / 2;
mTaskRect.set(mStackRect.left + xOffset, mStackRect.top,
mStackRect.right - xOffset, mStackRect.top + size);
+ mFreeformRect.set(mTaskRect);
// Compute the progress offsets
int withinAffiliationOffset = mContext.getResources().getDimensionPixelSize(
@@ -184,9 +205,12 @@
mTaskHalfHeightPOffset = sCurve.computePOffsetForScaledHeight(mTaskRect.height() / 2,
mStackRect);
mTaskBottomPOffset = sCurve.computePOffsetForHeight(mTaskBottomOffset, mStackRect);
+ mFreeformWorkspacePOffset = sCurve.computePOffsetForHeight(mFreeformRect.height(),
+ mStackRect);
if (DEBUG) {
Log.d(TAG, "computeRects");
+ Log.d(TAG, "\tarclength: " + sCurve.getArcLength());
Log.d(TAG, "\tmStackRect: " + mStackRect);
Log.d(TAG, "\tmTaskRect: " + mTaskRect);
Log.d(TAG, "\tmSystemInsets: " + mSystemInsets);
@@ -222,29 +246,38 @@
// Clear the progress map
mTaskProgressMap.clear();
- mNumTasks = tasks.size();
// Return early if we have no tasks
if (tasks.isEmpty()) {
- mMinScrollP = mMaxScrollP = 0;
+ mMinScrollP = mMaxScrollP = mMaxStackScrollP = 0;
+ mNumStackTasks = mNumFreeformTasks = 0;
return;
}
- // We calculate the progress by taking the progress of the element from the bottom of the
- // screen
- if (mNumTasks == 1) {
- // Just center the task in the visible stack rect
- mMinScrollP = mMaxScrollP = mInitialScrollP = 0f;
- mTaskProgressMap.put(tasks.get(0).key, 0f);
- } else {
- // Update the tasks from back to front with the new progresses. We set the initial
- // progress to the progress at which the top of the last task is near the center of the
- // visible stack rect.
+ // Filter the set of freeform and stack tasks
+ ArrayList<Task> freeformTasks = new ArrayList<>();
+ ArrayList<Task> stackTasks = new ArrayList<>();
+ for (int i = 0; i < tasks.size(); i++) {
+ Task task = tasks.get(i);
+ if (task.isFreeformTask()) {
+ freeformTasks.add(task);
+ } else {
+ stackTasks.add(task);
+ }
+ }
+ mNumStackTasks = stackTasks.size();
+ mNumFreeformTasks = freeformTasks.size();
+
+ // TODO: In the case where there is only freeform tasks, then the scrolls should be set to
+ // zero
+
+ if (!stackTasks.isEmpty()) {
+ // Update the for each task from back to front.
float pAtBackMostTaskTop = 0;
float pAtFrontMostTaskTop = pAtBackMostTaskTop;
- int taskCount = tasks.size();
+ int taskCount = stackTasks.size();
for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
+ Task task = stackTasks.get(i);
mTaskProgressMap.put(task.key, pAtFrontMostTaskTop);
if (i < (taskCount - 1)) {
@@ -255,15 +288,47 @@
}
}
- // Set the max scroll progress to the point at which the bottom of the front-most task
- // is aligned to the bottom of the stack (including nav bar and stack padding)
- mMaxScrollP = pAtFrontMostTaskTop - 1f + mTaskBottomPOffset + mTaskHeightPOffset;
+ mFrontMostTaskP = pAtFrontMostTaskTop;
+ // Set the max scroll progress to the point at which the top of the front-most task
+ // is aligned to the bottom of the stack (offset by nav bar, padding, and task height)
+ mMaxStackScrollP = getBottomAlignedScrollProgress(pAtFrontMostTaskTop,
+ mTaskBottomPOffset + mTaskHeightPOffset);
// Basically align the back-most task such that its progress is the same as the top of
- // the front most task at the max scroll
- mMinScrollP = pAtBackMostTaskTop - 1f + mTaskBottomPOffset + mTaskHeightPOffset;
- // The offset the inital scroll position to the front of the stack, with half the front
- // task height visible
- mInitialScrollP = Math.max(mMinScrollP, mMaxScrollP - mTaskHalfHeightPOffset);
+ // the front most task at the max stack scroll
+ mMinScrollP = getBottomAlignedScrollProgress(pAtBackMostTaskTop,
+ mTaskBottomPOffset + mTaskHeightPOffset);
+ }
+
+ if (!freeformTasks.isEmpty()) {
+ // Calculate the cell width/height depending on the number of freeform tasks
+ mFreeformCellXCount = Math.max(2, (int) Math.ceil(Math.sqrt(mNumFreeformTasks)));
+ mFreeformCellYCount = Math.max(2, (int) Math.ceil((float) mNumFreeformTasks / mFreeformCellXCount));
+ mFreeformCellWidth = mFreeformRect.width() / mFreeformCellXCount;
+ mFreeformCellHeight = mFreeformRect.height() / mFreeformCellYCount;
+
+ // Put each of the tasks in the progress map at a fixed index (does not need to actually
+ // map to a scroll position, just by index)
+ int taskCount = freeformTasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = freeformTasks.get(i);
+ mTaskProgressMap.put(task.key, (mFrontMostTaskP + 1) + i);
+ }
+
+ // The max scroll includes the freeform workspace offset. As the scroll progress exceeds
+ // mMaxStackScrollP up to mMaxScrollP, the stack will translate upwards and the freeform
+ // workspace will be visible
+ mMaxScrollP = mMaxStackScrollP + mFreeformWorkspacePOffset;
+ mInitialScrollP = mMaxScrollP;
+ } else {
+ mMaxScrollP = mMaxStackScrollP;
+ mInitialScrollP = Math.max(mMinScrollP, mMaxStackScrollP - mTaskHalfHeightPOffset);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "mNumStackTasks: " + mNumStackTasks);
+ Log.d(TAG, "mNumFreeformTasks: " + mNumFreeformTasks);
+ Log.d(TAG, "mMinScrollP: " + mMinScrollP);
+ Log.d(TAG, "mMaxStackScrollP: " + mMaxStackScrollP);
+ Log.d(TAG, "mMaxScrollP: " + mMaxScrollP);
}
}
@@ -272,11 +337,24 @@
* computeMinMaxScroll() is called first.
*/
public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
+ // Ensure minimum visibility count
if (tasks.size() <= 1) {
return new VisibilityReport(1, 1);
}
- // Walk backwards in the task stack and count the number of tasks and visible thumbnails
+ // If there are freeform tasks, then they will be the only ones visible
+ int freeformTaskCount = 0;
+ for (Task t : tasks) {
+ if (t.isFreeformTask()) {
+ freeformTaskCount++;
+ }
+ }
+ if (freeformTaskCount > 0) {
+ return new VisibilityReport(freeformTaskCount, freeformTaskCount);
+ }
+
+ // Otherwise, walk backwards in the stack and count the number of tasks and visible
+ // thumbnails
int taskHeight = mTaskRect.height();
int taskBarHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_task_bar_height);
@@ -338,18 +416,38 @@
/** Update/get the transform */
public TaskViewTransform getStackTransform(float taskProgress, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform prevTransform) {
- if (DEBUG) {
- Log.d(TAG, "getStackTransform: " + stackScroll);
+ float stackOverscroll = (stackScroll - mMaxStackScrollP) / mFreeformWorkspacePOffset;
+ int overscrollYOffset = 0;
+ if (mNumFreeformTasks > 0) {
+ overscrollYOffset = (int) (Math.max(0, stackOverscroll) * mFreeformRect.height());
}
- if (mNumTasks == 1) {
+ if ((mNumFreeformTasks > 0) && (stackScroll > mMaxStackScrollP) &&
+ (taskProgress > mFrontMostTaskP)) {
+ // This is a freeform task, so lay it out in the freeform workspace
+ int taskIndex = Math.round(taskProgress - (mFrontMostTaskP + 1));
+ int x = taskIndex % mFreeformCellXCount;
+ int y = taskIndex / mFreeformCellXCount;
+ int frontTaskBottom = mStackRect.height() - mTaskBottomOffset;
+ float scale = (float) mFreeformCellWidth / mTaskRect.width();
+ int scaleXOffset = (int) (((1f - scale) * mTaskRect.width()) / 2);
+ int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
+ transformOut.scale = scale;
+ transformOut.translationX = x * mFreeformCellWidth - scaleXOffset;
+ transformOut.translationY = frontTaskBottom - overscrollYOffset +
+ (y * mFreeformCellHeight) - scaleYOffset;
+ transformOut.visible = true;
+ return transformOut;
+
+ } else if (mNumStackTasks == 1) {
// Center the task in the stack, changing the scale will not follow the curve, but just
// modulate some values directly
- float pTaskRelative = -stackScroll;
+ float pTaskRelative = mMinScrollP - stackScroll;
float scale = SINGLE_TASK_SCALE;
int topOffset = (mStackRect.height() - mTaskBottomOffset - mTaskRect.height()) / 2;
transformOut.scale = scale;
- transformOut.translationY = (int) (topOffset + (pTaskRelative * mStackRect.height()));
+ transformOut.translationY = (int) (topOffset + (pTaskRelative * mStackRect.height())) -
+ overscrollYOffset;
transformOut.translationZ = mMaxTranslationZ;
transformOut.rect.set(mTaskRect);
transformOut.rect.offset(0, transformOut.translationY);
@@ -357,8 +455,12 @@
transformOut.visible = true;
transformOut.p = pTaskRelative;
return transformOut;
+
} else {
float pTaskRelative = taskProgress - stackScroll;
+ if (mNumFreeformTasks > 0) {
+ pTaskRelative = Math.min(mMaxStackScrollP, pTaskRelative);
+ }
float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
// If the task top is outside of the bounds below the screen, then immediately reset it
if (pTaskRelative > 1f) {
@@ -379,7 +481,7 @@
int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
transformOut.scale = scale;
transformOut.translationY = sCurve.pToX(pBounded, mStackRect) - mStackRect.top -
- scaleYOffset;
+ scaleYOffset - overscrollYOffset;
transformOut.translationZ = Math.max(mMinTranslationZ,
mMinTranslationZ + (pBounded * (mMaxTranslationZ - mMinTranslationZ)));
transformOut.rect.set(mTaskRect);
@@ -392,6 +494,27 @@
}
/**
+ * Update/get the transform
+ */
+ public TaskViewTransform getFreeformWorkspaceBounds(float stackScroll,
+ TaskViewTransform transformOut) {
+ transformOut.reset();
+ if (mNumFreeformTasks == 0) {
+ return transformOut;
+ }
+
+ if (stackScroll > mMaxStackScrollP) {
+ float stackOverscroll = (stackScroll - mMaxStackScrollP) / mFreeformWorkspacePOffset;
+ int overscrollYOffset = (int) (stackOverscroll * mFreeformRect.height());
+ int frontTaskBottom = mStackRect.height() - mTaskBottomOffset;
+ transformOut.visible = true;
+ transformOut.alpha =
+ transformOut.translationY = frontTaskBottom - overscrollYOffset;
+ }
+ return transformOut;
+ }
+
+ /**
* Returns the untransformed task view bounds.
*/
public Rect getUntransformedTaskViewBounds() {
@@ -406,4 +529,29 @@
if (!mTaskProgressMap.containsKey(t.key)) return 0f;
return mTaskProgressMap.get(t.key);
}
+
+ /**
+ * Maps a movement in screen y, relative to {@param downY}, to a movement in along the arc
+ * length of the curve. We know the curve is mostly flat, so we just map the length of the
+ * screen along the arc-length proportionally (1/arclength).
+ */
+ public float getDeltaPForY(int downY, int y) {
+ float deltaP = (float) (y - downY) / mStackRect.height() * (1f / sCurve.getArcLength());
+ return -deltaP;
+ }
+
+ /**
+ * This is the inverse of {@link #getDeltaPForY}. Given a movement along the arc length
+ * of the curve, map back to the screen y.
+ */
+ public int getYForDeltaP(float downScrollP, float p) {
+ int y = (int) ((p - downScrollP) * mStackRect.height() * sCurve.getArcLength());
+ return -y;
+ }
+
+ private float getBottomAlignedScrollProgress(float p, float pOffsetFromBottom) {
+ // At scroll progress == p, then p is at the top of the stack
+ // At scroll progress == p + 1, then p is at the bottom of the stack
+ return p - (1 - pOffsetFromBottom);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 3d3b13d..15fcab4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -21,6 +21,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.util.Log;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.OverScroller;
@@ -29,8 +30,12 @@
/* The scrolling logic for a TaskStackView */
public class TaskStackViewScroller {
+
+ private static final String TAG = "TaskStackViewScroller";
+ private static final boolean DEBUG = false;
+
public interface TaskStackViewScrollerCallbacks {
- public void onScrollChanged(float p);
+ void onScrollChanged(float p);
}
Context mContext;
@@ -38,6 +43,8 @@
TaskStackViewScrollerCallbacks mCb;
float mStackScrollP;
+ float mFlingDownScrollP;
+ int mFlingDownY;
OverScroller mScroller;
ObjectAnimator mScrollAnimator;
@@ -77,11 +84,6 @@
}
}
- /** Sets the current stack scroll without calling the callback. */
- void setStackScrollRaw(float s) {
- mStackScrollP = s;
- }
-
/**
* Sets the current stack scroll to the initial state when you first enter recents.
* @return whether the stack progress changed.
@@ -92,6 +94,20 @@
return Float.compare(prevStackScrollP, mStackScrollP) != 0;
}
+ /**
+ * Starts a fling that is coordinated with the {@link TaskStackViewTouchHandler}.
+ */
+ public void fling(float downScrollP, int downY, int y, int velY, int minY, int maxY,
+ int overscroll) {
+ if (DEBUG) {
+ Log.d(TAG, "fling: " + downScrollP + ", downY: " + downY + ", y: " + y +
+ ", velY: " + velY + ", minY: " + minY + ", maxY: " + maxY);
+ }
+ mFlingDownScrollP = downScrollP;
+ mFlingDownY = downY;
+ mScroller.fling(0, y, 0, velY, 0, 0, minY, maxY, 0, overscroll);
+ }
+
/** Bounds the current scroll if necessary */
public boolean boundScroll() {
float curScroll = getStackScroll();
@@ -174,21 +190,20 @@
/**** OverScroller ****/
+ // TODO: Remove
+ @Deprecated
int progressToScrollRange(float p) {
return (int) (p * mLayoutAlgorithm.mStackRect.height());
}
- float scrollRangeToProgress(int s) {
- return (float) s / mLayoutAlgorithm.mStackRect.height();
- }
-
/** Called from the view draw, computes the next scroll. */
boolean computeScroll() {
if (mScroller.computeScrollOffset()) {
- float scroll = scrollRangeToProgress(mScroller.getCurrY());
- setStackScrollRaw(scroll);
- if (mCb != null) {
- mCb.onScrollChanged(scroll);
+ float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY());
+ float scroll = mFlingDownScrollP + deltaP;
+ setStackScroll(scroll);
+ if (DEBUG) {
+ Log.d(TAG, "computeScroll: " + scroll);
}
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 3a1a987..08889c5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents.views;
import android.content.Context;
+import android.content.res.Resources;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -34,7 +35,11 @@
/* Handles touch events for a TaskStackView. */
class TaskStackViewTouchHandler implements SwipeHelper.Callback {
- static int INACTIVE_POINTER_ID = -1;
+
+ private static final String TAG = "TaskStackViewTouchHandler";
+ private static final boolean DEBUG = true;
+
+ private static int INACTIVE_POINTER_ID = -1;
Context mContext;
TaskStackView mSv;
@@ -42,21 +47,16 @@
VelocityTracker mVelocityTracker;
boolean mIsScrolling;
-
- float mInitialP;
- float mLastP;
- float mTotalPMotion;
- int mInitialMotionX, mInitialMotionY;
- int mLastMotionX, mLastMotionY;
+ float mDownScrollP;
+ int mDownX, mDownY;
int mActivePointerId = INACTIVE_POINTER_ID;
+ int mOverscrollSize;
TaskView mActiveTaskView = null;
int mMinimumVelocity;
int mMaximumVelocity;
// The scroll touch slop is used to calculate when we start scrolling
int mScrollTouchSlop;
- // The page touch slop is used to calculate when we start swiping
- float mPagingTouchSlop;
// Used to calculate when a tap is outside a task view rectangle.
final int mWindowTouchSlop;
@@ -65,18 +65,20 @@
public TaskStackViewTouchHandler(Context context, TaskStackView sv,
TaskStackViewScroller scroller) {
- mContext = context;
+ Resources res = context.getResources();
ViewConfiguration configuration = ViewConfiguration.get(context);
+ mContext = context;
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mScrollTouchSlop = configuration.getScaledTouchSlop();
- mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
mWindowTouchSlop = configuration.getScaledWindowTouchSlop();
mSv = sv;
mScroller = scroller;
- float densityScale = context.getResources().getDisplayMetrics().density;
- mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale, mPagingTouchSlop);
+ float densityScale = res.getDisplayMetrics().density;
+ mOverscrollSize = res.getDimensionPixelSize(R.dimen.recents_stack_overscroll);
+ mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale,
+ configuration.getScaledPagingTouchSlop());
mSwipeHelper.setMinAlpha(1f);
}
@@ -88,11 +90,6 @@
mVelocityTracker.clear();
}
}
- void initVelocityTrackerIfNotExists() {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- }
void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
@@ -115,175 +112,70 @@
return null;
}
- /** Constructs a simulated motion event for the current stack scroll. */
- MotionEvent createMotionEventForStackScroll(MotionEvent ev) {
- MotionEvent pev = MotionEvent.obtainNoHistory(ev);
- pev.setLocation(0, mScroller.progressToScrollRange(mScroller.getStackScroll()));
- return pev;
- }
-
/** Touch preprocessing for handling below */
public boolean onInterceptTouchEvent(MotionEvent ev) {
- // Return early if we have no children
- boolean hasTaskViews = (mSv.getTaskViews().size() > 0);
- if (!hasTaskViews) {
- return false;
- }
-
// Pass through to swipe helper if we are swiping
mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
if (mInterceptedBySwipeHelper) {
return true;
}
- TaskStackViewLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
- boolean wasScrolling = mScroller.isScrolling() ||
- (mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
- int action = ev.getAction();
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- // Save the touch down info
- mInitialMotionX = mLastMotionX = (int) ev.getX();
- mInitialMotionY = mLastMotionY = (int) ev.getY();
- mInitialP = mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY,
- layoutAlgorithm.mStackRect);
- mActivePointerId = ev.getPointerId(0);
- mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
- // Stop the current scroll if it is still flinging
- mScroller.stopScroller();
- mScroller.stopBoundScrollAnimation();
- // Initialize the velocity tracker
- initOrResetVelocityTracker();
- mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
- break;
- }
- case MotionEvent.ACTION_POINTER_DOWN: {
- final int index = ev.getActionIndex();
- mActivePointerId = ev.getPointerId(index);
- mLastMotionX = (int) ev.getX(index);
- mLastMotionY = (int) ev.getY(index);
- mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
- break;
- }
- case MotionEvent.ACTION_MOVE: {
- if (mActivePointerId == INACTIVE_POINTER_ID) break;
-
- // Initialize the velocity tracker if necessary
- initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
-
- int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- int y = (int) ev.getY(activePointerIndex);
- int x = (int) ev.getX(activePointerIndex);
- if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
- // Save the touch move info
- mIsScrolling = true;
- // Disallow parents from intercepting touch events
- final ViewParent parent = mSv.getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
- }
-
- mLastMotionX = x;
- mLastMotionY = y;
- mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
- break;
- }
- case MotionEvent.ACTION_POINTER_UP: {
- int pointerIndex = ev.getActionIndex();
- int pointerId = ev.getPointerId(pointerIndex);
- if (pointerId == mActivePointerId) {
- // Select a new active pointer id and reset the motion state
- final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
- mActivePointerId = ev.getPointerId(newPointerIndex);
- mLastMotionX = (int) ev.getX(newPointerIndex);
- mLastMotionY = (int) ev.getY(newPointerIndex);
- mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
- mVelocityTracker.clear();
- }
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- // Animate the scroll back if we've cancelled
- mScroller.animateBoundScroll();
- // Reset the drag state and the velocity tracker
- mIsScrolling = false;
- mActivePointerId = INACTIVE_POINTER_ID;
- mActiveTaskView = null;
- mTotalPMotion = 0;
- recycleVelocityTracker();
- break;
- }
- }
-
- return wasScrolling || mIsScrolling;
+ return handleTouchEvent(ev);
}
/** Handles touch events once we have intercepted them */
public boolean onTouchEvent(MotionEvent ev) {
- // Short circuit if we have no children
- boolean hasTaskViews = (mSv.getTaskViews().size() > 0);
- if (!hasTaskViews) {
- return false;
- }
-
// Pass through to swipe helper if we are swiping
if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
return true;
}
- // Update the velocity tracker
- initVelocityTrackerIfNotExists();
+ handleTouchEvent(ev);
+ return true;
+ }
+
+ private boolean handleTouchEvent(MotionEvent ev) {
+ // Short circuit if we have no children
+ if (mSv.getTaskViews().size() == 0) {
+ return false;
+ }
TaskStackViewLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
- mInitialMotionX = mLastMotionX = (int) ev.getX();
- mInitialMotionY = mLastMotionY = (int) ev.getY();
- mInitialP = mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY,
- layoutAlgorithm.mStackRect);
+ mDownX = (int) ev.getX();
+ mDownY = (int) ev.getY();
+ mDownScrollP = mScroller.getStackScroll();
mActivePointerId = ev.getPointerId(0);
- mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
+ mActiveTaskView = findViewAtPoint(mDownX, mDownY);
+
// Stop the current scroll if it is still flinging
mScroller.stopScroller();
mScroller.stopBoundScrollAnimation();
+
// Initialize the velocity tracker
initOrResetVelocityTracker();
- mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
- // Disallow parents from intercepting touch events
- final ViewParent parent = mSv.getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
+ mVelocityTracker.addMovement(ev);
break;
}
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = ev.getActionIndex();
+ mDownX = (int) ev.getX();
+ mDownY = (int) ev.getY();
+ mDownScrollP = mScroller.getStackScroll();
mActivePointerId = ev.getPointerId(index);
- mLastMotionX = (int) ev.getX(index);
- mLastMotionY = (int) ev.getY(index);
- mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY,
- layoutAlgorithm.mStackRect);
+ mVelocityTracker.addMovement(ev);
break;
}
case MotionEvent.ACTION_MOVE: {
- if (mActivePointerId == INACTIVE_POINTER_ID) break;
-
- mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
-
int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- int x = (int) ev.getX(activePointerIndex);
int y = (int) ev.getY(activePointerIndex);
- int yTotal = Math.abs(y - mInitialMotionY);
- float curP = layoutAlgorithm.sCurve.xToP(y, layoutAlgorithm.mStackRect);
- float deltaP = mLastP - curP;
if (!mIsScrolling) {
- if (yTotal > mScrollTouchSlop) {
+ if (Math.abs(y - mDownY) > mScrollTouchSlop) {
mIsScrolling = true;
+
// Disallow parents from intercepting touch events
final ViewParent parent = mSv.getParent();
if (parent != null) {
@@ -292,54 +184,14 @@
}
}
if (mIsScrolling) {
- float curStackScroll = mScroller.getStackScroll();
- float overScrollAmount = mScroller.getScrollAmountOutOfBounds(curStackScroll + deltaP);
- if (Float.compare(overScrollAmount, 0f) != 0) {
- // Bound the overscroll to a fixed amount, and inversely scale the y-movement
- // relative to how close we are to the max overscroll
- float maxOverScroll = mContext.getResources().getFloat(
- R.dimen.recents_stack_overscroll_percentage);
- deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount)
- / maxOverScroll));
- }
- mScroller.setStackScroll(curStackScroll + deltaP);
- }
- mLastMotionX = x;
- mLastMotionY = y;
- mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
- mTotalPMotion += Math.abs(deltaP);
- break;
- }
- case MotionEvent.ACTION_UP: {
- mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int velocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
- if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
- float overscrollRangePct = Math.abs((float) velocity / mMaximumVelocity);
- int overscrollRange = (int) (Math.min(1f, overscrollRangePct) *
- (Constants.Values.TaskStackView.TaskStackMaxOverscrollRange -
- Constants.Values.TaskStackView.TaskStackMinOverscrollRange));
- mScroller.mScroller.fling(0,
- mScroller.progressToScrollRange(mScroller.getStackScroll()),
- 0, velocity,
- 0, 0,
- mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMinScrollP),
- mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMaxScrollP),
- 0, Constants.Values.TaskStackView.TaskStackMinOverscrollRange +
- overscrollRange);
- // Invalidate to kick off computeScroll
- mSv.invalidate();
- } else if (mIsScrolling && mScroller.isScrollOutOfBounds()) {
- // Animate the scroll back into bounds
- mScroller.animateBoundScroll();
- } else if (mActiveTaskView == null) {
- // This tap didn't start on a task.
- maybeHideRecentsFromBackgroundTap((int) ev.getX(), (int) ev.getY());
+ // If we just move linearly on the screen, then that would map to 1/arclength
+ // of the curve, so just move the scroll proportional to that
+ float deltaP = layoutAlgorithm.getDeltaPForY(mDownY, y);
+ float curScrollP = mDownScrollP + deltaP;
+ mScroller.setStackScroll(curScrollP);
}
- mActivePointerId = INACTIVE_POINTER_ID;
- mIsScrolling = false;
- mTotalPMotion = 0;
- recycleVelocityTracker();
+ mVelocityTracker.addMovement(ev);
break;
}
case MotionEvent.ACTION_POINTER_UP: {
@@ -349,11 +201,41 @@
// Select a new active pointer id and reset the motion state
final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
mActivePointerId = ev.getPointerId(newPointerIndex);
- mLastMotionX = (int) ev.getX(newPointerIndex);
- mLastMotionY = (int) ev.getY(newPointerIndex);
- mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
- mVelocityTracker.clear();
}
+ mVelocityTracker.addMovement(ev);
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ mVelocityTracker.addMovement(ev);
+ mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+ int y = (int) ev.getY(activePointerIndex);
+ int velocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
+ if (mIsScrolling) {
+ if (mScroller.isScrollOutOfBounds()) {
+ // Animate the scroll back into bounds
+ mScroller.animateBoundScroll();
+ } else if (Math.abs(velocity) > mMinimumVelocity) {
+ float deltaP = layoutAlgorithm.getDeltaPForY(mDownY, y);
+ float curScrollP = mDownScrollP + deltaP;
+ float downToCurY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
+ curScrollP);
+ float downToMinY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
+ layoutAlgorithm.mMaxScrollP);
+ float downToMaxY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
+ layoutAlgorithm.mMinScrollP);
+ mScroller.fling(mDownScrollP, mDownY, (int) downToCurY, velocity,
+ (int) downToMinY, (int) downToMaxY, mOverscrollSize);
+ mSv.invalidate();
+ }
+ } else if (mActiveTaskView == null) {
+ // This tap didn't start on a task.
+ maybeHideRecentsFromBackgroundTap((int) ev.getX(), (int) ev.getY());
+ }
+
+ mActivePointerId = INACTIVE_POINTER_ID;
+ mIsScrolling = false;
+ recycleVelocityTracker();
break;
}
case MotionEvent.ACTION_CANCEL: {
@@ -363,20 +245,19 @@
}
mActivePointerId = INACTIVE_POINTER_ID;
mIsScrolling = false;
- mTotalPMotion = 0;
recycleVelocityTracker();
break;
}
}
- return true;
+ return mIsScrolling;
}
/** Hides recents if the up event at (x, y) is a tap on the background area. */
void maybeHideRecentsFromBackgroundTap(int x, int y) {
// Ignore the up event if it's too far from its start position. The user might have been
// trying to scroll or swipe.
- int dx = Math.abs(mInitialMotionX - x);
- int dy = Math.abs(mInitialMotionY - y);
+ int dx = Math.abs(mDownX - x);
+ int dy = Math.abs(mDownY - y);
if (dx > mScrollTouchSlop || dy > mScrollTouchSlop) {
return;
}
@@ -427,12 +308,16 @@
@Override
public boolean canChildBeDismissed(View v) {
+ if (v instanceof TaskView) {
+ return !((TaskView) v).getTask().isFreeformTask();
+ }
return true;
}
@Override
public void onBeginDrag(View v) {
TaskView tv = (TaskView) v;
+ mSwipeHelper.setSnapBackTranslationX(tv.getTranslationX());
// Disable clipping with the stack while we are swiping
tv.setClipViewInStack(false);
// Disallow touch events from this task view
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 0a5ee79..aaacc6c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -66,8 +66,6 @@
public void onTaskViewClipStateChanged(TaskView tv);
}
- RecentsConfiguration mConfig;
-
float mTaskProgress;
ObjectAnimator mTaskProgressAnimator;
float mMaxDimScale;
@@ -121,8 +119,8 @@
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ RecentsConfiguration config = Recents.getConfiguration();
Resources res = context.getResources();
- mConfig = RecentsConfiguration.getInstance();
mMaxDimScale = res.getInteger(R.integer.recents_max_task_stack_view_dim) / 255f;
mClipViewInStack = true;
mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize(
@@ -135,8 +133,8 @@
com.android.internal.R.interpolator.decelerate_quint);
setTaskProgress(getTaskProgress());
setDim(getDim());
- if (mConfig.fakeShadows) {
- setBackground(new FakeShadowDrawable(res, mConfig));
+ if (config.fakeShadows) {
+ setBackground(new FakeShadowDrawable(res, config));
}
setOutlineProvider(mViewBounds);
}
@@ -224,9 +222,11 @@
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration,
ValueAnimator.AnimatorUpdateListener updateCallback) {
+ RecentsConfiguration config = Recents.getConfiguration();
+
// Apply the transform
toTransform.applyToTaskView(this, duration, mFastOutSlowInInterpolator, false,
- !mConfig.fakeShadows, updateCallback);
+ !config.fakeShadows, updateCallback);
// Update the task progress
Utilities.cancelAnimationWithoutCallbacks(mTaskProgressAnimator);
@@ -277,7 +277,8 @@
* first layout because the actual animation into recents may take a long time. */
void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask,
boolean occludesLaunchTarget, int offscreenY) {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
int initialDim = getDim();
if (launchState.launchedHasConfigurationChanged) {
// Just load the views as-is
@@ -307,7 +308,8 @@
/** Animates this task view as it enters recents */
void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
Resources res = mContext.getResources();
final TaskViewTransform transform = ctx.currentTaskTransform;
final int transitionEnterFromAppDelay = res.getInteger(
@@ -322,7 +324,6 @@
R.integer.recents_task_enter_from_home_stagger_delay);
final int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
R.dimen.recents_task_view_affiliate_group_enter_offset);
- int startDelay = 0;
if (launchState.launchedFromAppWithThumbnail) {
if (mTask.isLaunchTarget) {
@@ -371,7 +372,6 @@
ctx.postAnimationTrigger.increment();
}
}
- startDelay = transitionEnterFromAppDelay;
} else if (launchState.launchedFromHome) {
// Animate the tasks up
@@ -381,7 +381,7 @@
setScaleX(transform.scale);
setScaleY(transform.scale);
- if (!mConfig.fakeShadows) {
+ if (!config.fakeShadows) {
animate().translationZ(transform.translationZ);
}
animate()
@@ -400,7 +400,6 @@
})
.start();
ctx.postAnimationTrigger.increment();
- startDelay = delay;
}
}
@@ -580,8 +579,10 @@
/** Returns the current dim. */
public void setDim(int dim) {
+ RecentsConfiguration config = Recents.getConfiguration();
+
mDimAlpha = dim;
- if (mConfig.useHardwareLayers) {
+ if (config.useHardwareLayers) {
// Defer setting hardware layers if we have not yet measured, or there is no dim to draw
if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {
mDimColorFilter.setColor(Color.argb(mDimAlpha, 0, 0, 0));
@@ -703,13 +704,14 @@
@Override
public void onTaskDataLoaded() {
+ RecentsConfiguration config = Recents.getConfiguration();
if (mThumbnailView != null && mHeaderView != null) {
// Bind each of the views to the new task data
mThumbnailView.rebindToTask(mTask);
mHeaderView.rebindToTask(mTask);
// Rebind any listeners
mActionButtonView.setOnClickListener(this);
- setOnLongClickListener(mConfig.hasDockedTasks ? null : this);
+ setOnLongClickListener(config.hasDockedTasks ? null : this);
}
mTaskDataLoaded = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index ec50eb6..174ff33 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -26,6 +26,7 @@
/* The transform state for a task view */
public class TaskViewTransform {
public int startDelay = 0;
+ public int translationX = 0;
public int translationY = 0;
public float translationZ = 0;
public float scale = 1f;
@@ -43,6 +44,7 @@
public TaskViewTransform(TaskViewTransform o) {
startDelay = o.startDelay;
+ translationX = o.translationX;
translationY = o.translationY;
translationZ = o.translationZ;
scale = o.scale;
@@ -52,9 +54,12 @@
p = o.p;
}
- /** Resets the current transform */
+ /**
+ * Resets the current transform.
+ */
public void reset() {
startDelay = 0;
+ translationX = 0;
translationY = 0;
translationZ = 0;
scale = 1f;
@@ -71,6 +76,9 @@
public boolean hasScaleChangedFrom(float v) {
return (Float.compare(scale, v) != 0);
}
+ public boolean hasTranslationXChangedFrom(float v) {
+ return (Float.compare(translationX, v) != 0);
+ }
public boolean hasTranslationYChangedFrom(float v) {
return (Float.compare(translationY, v) != 0);
}
@@ -87,6 +95,9 @@
boolean requiresLayers = false;
// Animate to the final state
+ if (hasTranslationXChangedFrom(v.getTranslationX())) {
+ anim.translationX(translationX);
+ }
if (hasTranslationYChangedFrom(v.getTranslationY())) {
anim.translationY(translationY);
}
@@ -117,6 +128,9 @@
.start();
} else {
// Set the changed properties
+ if (hasTranslationXChangedFrom(v.getTranslationX())) {
+ v.setTranslationX(translationX);
+ }
if (hasTranslationYChangedFrom(v.getTranslationY())) {
v.setTranslationY(translationY);
}
@@ -147,7 +161,8 @@
@Override
public String toString() {
- return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ +
+ return "TaskViewTransform delay: " + startDelay +
+ " x: " + translationX + " y: " + translationY + " z: " + translationZ +
" scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
" p: " + p;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index cfdb01e..f3658eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -33,6 +33,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -246,15 +247,25 @@
* Prudently disable QS and notifications. */
private static final boolean ONLY_CORE_APPS;
+ /* If true, the device supports freeform window management.
+ * This affects the status bar UI. */
+ private static final boolean FREEFORM_WINDOW_MANAGEMENT;
+
static {
boolean onlyCoreApps;
+ boolean freeformWindowManagement;
try {
- onlyCoreApps = IPackageManager.Stub.asInterface(ServiceManager.getService("package"))
- .isOnlyCoreApps();
+ IPackageManager packageManager =
+ IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+ onlyCoreApps = packageManager.isOnlyCoreApps();
+ freeformWindowManagement = packageManager.hasSystemFeature(
+ PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
} catch (RemoteException e) {
onlyCoreApps = false;
+ freeformWindowManagement = false;
}
ONLY_CORE_APPS = onlyCoreApps;
+ FREEFORM_WINDOW_MANAGEMENT = freeformWindowManagement;
}
PhoneStatusBarPolicy mIconPolicy;
@@ -982,8 +993,8 @@
if (shelfOverride != DEFAULT) {
return shelfOverride != 0;
}
- // Otherwise default to the build setting.
- return mContext.getResources().getBoolean(R.bool.config_enableAppShelf);
+ // Otherwise default to the platform feature.
+ return FREEFORM_WINDOW_MANAGEMENT;
}
private void clearAllNotifications() {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 6b34612..1ff13b2 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3824,7 +3824,6 @@
boolean isPermitted =
isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
- Log.i(TAG, String.format("getTypesVisibleToCaller: isPermitted? %s", isPermitted));
return getTypesForCaller(callingUid, userId, isPermitted);
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 4671cb0..8061a92 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -53,6 +53,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
+import android.view.AppTransitionAnimationSpec;
import android.view.IApplicationToken;
import android.view.WindowManager;
@@ -863,17 +864,24 @@
break;
case ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP:
case ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
- service.mWindowManager.overridePendingAppTransitionAspectScaledThumb(
- pendingOptions.getThumbnail(),
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight(),
- pendingOptions.getOnAnimationStartListener(),
- (animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP));
- if (intent.getSourceBounds() == null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + pendingOptions.getWidth(),
- pendingOptions.getStartY() + pendingOptions.getHeight()));
+ final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
+ if (animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
+ && specs != null) {
+ service.mWindowManager.overridePendingAppTransitionMultiThumb(
+ specs, pendingOptions.getOnAnimationStartListener(), false);
+ } else {
+ service.mWindowManager.overridePendingAppTransitionAspectScaledThumb(
+ pendingOptions.getThumbnail(),
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight(),
+ pendingOptions.getOnAnimationStartListener(),
+ (animationType == ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP));
+ if (intent.getSourceBounds() == null) {
+ intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+ pendingOptions.getStartY(),
+ pendingOptions.getStartX() + pendingOptions.getWidth(),
+ pendingOptions.getStartY() + pendingOptions.getHeight()));
+ }
}
break;
default:
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 1cd3758..2104830 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -530,25 +530,35 @@
* @param task If non-null, the task will be moved to the top of the stack.
* */
void moveToFront(String reason, TaskRecord task) {
- if (isAttached()) {
- final ActivityStack lastFocusStack = mStacks.get(mStacks.size() - 1);
- // Need to move this stack to the front before calling
- // {@link ActivityStackSupervisor#setFocusStack} below.
- mStacks.remove(this);
- mStacks.add(this);
+ if (!isAttached()) {
+ return;
+ }
- // TODO(multi-display): Needs to also work if focus is moving to the non-home display.
- if (isOnHomeDisplay()) {
- mStackSupervisor.setFocusStack(reason, lastFocusStack);
+ mStacks.remove(this);
+ int addIndex = mStacks.size();
+
+ if (addIndex > 0) {
+ final ActivityStack topStack = mStacks.get(addIndex - 1);
+ if (topStack.mStackId == PINNED_STACK_ID && topStack != this) {
+ // The pinned stack is always the top most stack (always-on-top).
+ // So, stack is moved just below the pinned stack.
+ addIndex--;
}
- if (task != null) {
- insertTaskAtTop(task, null);
- } else {
- task = topTask();
- }
- if (task != null) {
- mWindowManager.moveTaskToTop(task.taskId);
- }
+ }
+
+ mStacks.add(addIndex, this);
+
+ // TODO(multi-display): Needs to also work if focus is moving to the non-home display.
+ if (isOnHomeDisplay()) {
+ mStackSupervisor.setFocusStack(reason, this);
+ }
+ if (task != null) {
+ insertTaskAtTop(task, null);
+ } else {
+ task = topTask();
+ }
+ if (task != null) {
+ mWindowManager.moveTaskToTop(task.taskId);
}
}
@@ -1304,12 +1314,7 @@
return false;
}
- if (mStackSupervisor.isFrontStack(this)) {
- return true;
- }
-
- if (mStackId == PINNED_STACK_ID) {
- // Pinned stack is always visible if it exist.
+ if (mStackSupervisor.isFrontStack(this) || mStackSupervisor.isFocusedStack(this)) {
return true;
}
@@ -2041,7 +2046,7 @@
// Have the window manager re-evaluate the orientation of
// the screen based on the new activity order.
boolean notUpdated = true;
- if (mStackSupervisor.isFrontStack(this)) {
+ if (mStackSupervisor.isFocusedStack(this)) {
Configuration config = mWindowManager.updateOrientationFromAppTokens(
mService.mConfiguration,
next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
@@ -2788,7 +2793,7 @@
}
private void adjustFocusedActivityLocked(ActivityRecord r, String reason) {
- if (mStackSupervisor.isFrontStack(this) && mService.mFocusedActivity == r) {
+ if (mStackSupervisor.isFocusedStack(this) && mService.mFocusedActivity == r) {
ActivityRecord next = topRunningActivityLocked();
final String myReason = reason + " adjustFocus";
if (next != r) {
@@ -3395,7 +3400,7 @@
if (task != null && task.removeActivity(r)) {
if (DEBUG_STACK) Slog.i(TAG_STACK,
"removeActivityFromHistoryLocked: last activity removed from " + this);
- if (mStackSupervisor.isFrontStack(this) && task == topTask() &&
+ if (mStackSupervisor.isFocusedStack(this) && task == topTask() &&
task.isOverHomeStack()) {
mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo(), reason);
}
@@ -4376,7 +4381,7 @@
RunningTaskInfo ci = new RunningTaskInfo();
ci.id = task.taskId;
- ci.stackId = (task.stack == null) ? INVALID_STACK_ID : task.stack.getStackId();
+ ci.stackId = mStackId;
ci.baseActivity = r.intent.getComponent();
ci.topActivity = top.intent.getComponent();
ci.lastActiveTime = task.lastActiveTime;
@@ -4569,7 +4574,7 @@
if (mTaskHistory.isEmpty()) {
if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
// We only need to adjust focused stack if this stack is in focus.
- if (isOnHomeDisplay() && mStackSupervisor.isFrontStack(this)) {
+ if (isOnHomeDisplay() && mStackSupervisor.isFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
if (mFullscreen || !adjustFocusToNextVisibleStackLocked(null, myReason)) {
mStackSupervisor.moveHomeStackToFront(myReason);
@@ -4677,7 +4682,7 @@
return;
}
- final boolean wasFocused = mStackSupervisor.isFrontStack(prevStack)
+ final boolean wasFocused = mStackSupervisor.isFocusedStack(prevStack)
&& (mStackSupervisor.topRunningActivityLocked() == r);
final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 3bf87d8..1cd71a1 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -458,10 +458,7 @@
return mLastFocusedStack;
}
- /** Top of all visible stacks is/should always be equal to the focused stack.
- * Use {@link ActivityStack#isStackVisibleLocked} to determine if a specific
- * stack is visible or not. */
- boolean isFrontStack(ActivityStack stack) {
+ boolean isFocusedStack(ActivityStack stack) {
if (stack == null) {
return false;
}
@@ -473,18 +470,22 @@
return stack == mFocusedStack;
}
- void setFocusStack(String reason, ActivityStack lastFocusedStack) {
- ArrayList<ActivityStack> stacks = mHomeStack.mStacks;
- final int topNdx = stacks.size() - 1;
- if (topNdx <= 0) {
- return;
+ /** The top most stack. */
+ boolean isFrontStack(ActivityStack stack) {
+ if (stack == null) {
+ return false;
}
- final ActivityStack topStack = stacks.get(topNdx);
- mFocusedStack = topStack;
- if (lastFocusedStack != null) {
- mLastFocusedStack = lastFocusedStack;
+ final ActivityRecord parent = stack.mActivityContainer.mParentActivity;
+ if (parent != null) {
+ stack = parent.task.stack;
}
+ return stack == mHomeStack.mStacks.get((mHomeStack.mStacks.size() - 1));
+ }
+
+ void setFocusStack(String reason, ActivityStack focusedStack) {
+ mLastFocusedStack = mFocusedStack;
+ mFocusedStack = focusedStack;
EventLogTags.writeAmFocusedStack(
mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
@@ -642,7 +643,7 @@
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- if (!isFrontStack(stack)) {
+ if (!isFocusedStack(stack)) {
continue;
}
ActivityRecord hr = stack.topRunningActivityLocked();
@@ -673,7 +674,7 @@
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- if (!isFrontStack(stack) || stack.numActivities() == 0) {
+ if (!isFocusedStack(stack) || stack.numActivities() == 0) {
continue;
}
final ActivityRecord resumedActivity = stack.mResumedActivity;
@@ -692,7 +693,7 @@
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
final ActivityRecord r = stack.mResumedActivity;
if (r != null && r.state != RESUMED) {
return false;
@@ -737,7 +738,7 @@
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- if (!isFrontStack(stack) && stack.mResumedActivity != null) {
+ if (!isFocusedStack(stack) && stack.mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
" mResumedActivity=" + stack.mResumedActivity);
someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
@@ -1374,7 +1375,7 @@
// launching the initial activity (that is, home), so that it can have
// a chance to initialize itself while in the background, making the
// switch back to it faster and look better.
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
mService.startSetupActivityLocked();
}
@@ -2621,7 +2622,7 @@
r.idle = true;
//Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
- if (isFrontStack(r.task.stack) || fromTimeout) {
+ if (isFocusedStack(r.task.stack) || fromTimeout) {
booting = checkFinishBootingLocked();
}
}
@@ -2773,7 +2774,7 @@
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
if (stack.mResumedActivity != null) {
fgApp = stack.mResumedActivity.app;
} else if (stack.mPausingActivity != null) {
@@ -2805,7 +2806,7 @@
}
// Do targetStack first.
boolean result = false;
- if (isFrontStack(targetStack)) {
+ if (isFocusedStack(targetStack)) {
result = targetStack.resumeTopActivityLocked(target, targetOptions);
}
@@ -2817,7 +2818,7 @@
// Already started above.
continue;
}
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
stack.resumeTopActivityLocked(null);
}
}
@@ -3243,7 +3244,7 @@
ActivityStack moveTaskToStackUncheckedLocked(
TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) {
final ActivityRecord r = task.getTopActivity();
- final boolean wasFocused = isFrontStack(task.stack) && (topRunningActivityLocked() == r);
+ final boolean wasFocused = isFocusedStack(task.stack) && (topRunningActivityLocked() == r);
final boolean wasResumed = wasFocused && (task.stack.mResumedActivity == r);
final boolean resizeable = task.mResizeable;
@@ -3469,7 +3470,7 @@
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
stack.awakeFromSleepingLocked();
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
resumeTopActivitiesLocked();
}
}
@@ -3536,7 +3537,7 @@
boolean reportResumedActivityLocked(ActivityRecord r) {
final ActivityStack stack = r.task.stack;
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
mService.updateUsageStats(r, true);
}
if (allResumedActivitiesComplete()) {
@@ -3829,7 +3830,7 @@
final ActivityStack stack = stacks.get(stackNdx);
final ActivityRecord r = stack.topRunningActivityLocked();
final ActivityState state = r == null ? DESTROYED : r.state;
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
if (r == null) Slog.e(TAG,
"validateTop...: null top activity, stack=" + stack);
else {
diff --git a/services/core/java/com/android/server/input/InputWindowHandle.java b/services/core/java/com/android/server/input/InputWindowHandle.java
index 9149fcc..207c05d 100644
--- a/services/core/java/com/android/server/input/InputWindowHandle.java
+++ b/services/core/java/com/android/server/input/InputWindowHandle.java
@@ -100,6 +100,17 @@
}
@Override
+ public String toString() {
+ return new StringBuilder(name)
+ .append(", layer=").append(layer)
+ .append(", frame=[").append(frameLeft).append(",").append(frameTop).append(",")
+ .append(frameRight).append(",").append(frameBottom).append("]")
+ .append(", touchableRegion=").append(touchableRegion)
+ .toString();
+
+ }
+
+ @Override
protected void finalize() throws Throwable {
try {
nativeDispose();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e149616..be9f44dc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1759,8 +1759,9 @@
synchronized (mPackages) {
for (String permission : pkg.requestedPermissions) {
BasePermission bp = mSettings.mPermissions.get(permission);
- if (bp != null && bp.isRuntime() && (grantedPermissions == null
- || ArrayUtils.contains(grantedPermissions, permission))) {
+ if (bp != null && (bp.isRuntime() || bp.isDevelopment())
+ && (grantedPermissions == null
+ || ArrayUtils.contains(grantedPermissions, permission))) {
final int flags = permissionsState.getPermissionFlags(permission, userId);
// Installer cannot change immutable permissions.
if ((flags & immutableFlags) == 0) {
@@ -3567,7 +3568,8 @@
killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
}
});
- } break;
+ }
+ break;
}
mOnPermissionChangeListeners.onPermissionsChanged(uid);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index dc34904..eb0ca23 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -64,7 +64,6 @@
import android.view.animation.PathInterpolator;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
-import android.view.animation.TranslateYAnimation;
import com.android.internal.util.DumpUtils.Dump;
import com.android.server.AttributeCache;
@@ -175,7 +174,7 @@
private Rect mTmpFromClipRect = new Rect();
private Rect mTmpToClipRect = new Rect();
- private final Rect mTmpStartRect = new Rect();
+ private final Rect mTmpRect = new Rect();
private final static int APP_STATE_IDLE = 0;
private final static int APP_STATE_READY = 1;
@@ -459,16 +458,16 @@
private Animation createScaleUpAnimationLocked(int transit, boolean enter,
Rect containingFrame) {
Animation a;
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ getDefaultNextAppTransitionStartRect(mTmpRect);
final int appWidth = containingFrame.width();
final int appHeight = containingFrame.height();
if (enter) {
// Entering app zooms out from the center of the initial rect.
- float scaleW = mTmpStartRect.width() / (float) appWidth;
- float scaleH = mTmpStartRect.height() / (float) appHeight;
+ float scaleW = mTmpRect.width() / (float) appWidth;
+ float scaleH = mTmpRect.height() / (float) appHeight;
Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mTmpStartRect.left, scaleW),
- computePivot(mTmpStartRect.right, scaleH));
+ computePivot(mTmpRect.left, scaleW),
+ computePivot(mTmpRect.right, scaleH));
scale.setInterpolator(mDecelerateInterpolator);
Animation alpha = new AlphaAnimation(0, 1);
@@ -545,20 +544,20 @@
final int appWidth = appFrame.width();
final int appHeight = appFrame.height();
- // mTmpStartRect will contain an area around the launcher icon that was pressed. We will
+ // mTmpRect will contain an area around the launcher icon that was pressed. We will
// clip reveal from that area in the final area of the app.
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ getDefaultNextAppTransitionStartRect(mTmpRect);
float t = 0f;
if (appHeight > 0) {
- t = (float) mTmpStartRect.left / appHeight;
+ t = (float) mTmpRect.left / appHeight;
}
int translationY = mClipRevealTranslationY + (int)(appHeight / 7f * t);
- int centerX = mTmpStartRect.centerX();
- int centerY = mTmpStartRect.centerY();
- int halfWidth = mTmpStartRect.width() / 2;
- int halfHeight = mTmpStartRect.height() / 2;
+ int centerX = mTmpRect.centerX();
+ int centerY = mTmpRect.centerY();
+ int halfWidth = mTmpRect.width() / 2;
+ int halfHeight = mTmpRect.height() / 2;
// Clip third of the from size of launch icon, expand to full width/height
Animation clipAnimLR = new ClipRectLRAnimation(
@@ -693,19 +692,19 @@
float scaleW = appWidth / thumbWidth;
float unscaledHeight = thumbHeight * scaleW;
- getNextAppTransitionStartRect(taskId, mTmpStartRect);
- float unscaledStartY = mTmpStartRect.top - (unscaledHeight - thumbHeight) / 2f;
+ getNextAppTransitionStartRect(taskId, mTmpRect);
+ float unscaledStartY = mTmpRect.top - (unscaledHeight - thumbHeight) / 2f;
if (mNextAppTransitionScaleUp) {
// Animation up from the thumbnail to the full screen
Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW,
- mTmpStartRect.left + (thumbWidth / 2f), mTmpStartRect.top + (thumbHeight / 2f));
+ mTmpRect.left + (thumbWidth / 2f), mTmpRect.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 -
- (mTmpStartRect.left + thumbWidth / 2);
+ (mTmpRect.left + thumbWidth / 2);
final float toY = appRect.top + mNextAppTransitionInsets.top + -unscaledStartY;
Animation translate = new TranslateAnimation(0, toX, 0, toY);
translate.setInterpolator(mTouchResponseInterpolator);
@@ -720,7 +719,7 @@
} else {
// Animation down from the full screen to the thumbnail
Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f,
- mTmpStartRect.left + (thumbWidth / 2f), mTmpStartRect.top + (thumbHeight / 2f));
+ mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f));
scale.setInterpolator(mTouchResponseInterpolator);
scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
Animation alpha = new AlphaAnimation(0f, 1f);
@@ -753,10 +752,10 @@
Animation a;
final int appWidth = containingFrame.width();
final int appHeight = containingFrame.height();
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
- final int thumbWidthI = mTmpStartRect.width();
+ getDefaultNextAppTransitionStartRect(mTmpRect);
+ final int thumbWidthI = mTmpRect.width();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = mTmpStartRect.height();
+ final int thumbHeightI = mTmpRect.height();
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
// Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
@@ -766,7 +765,7 @@
switch (thumbTransitState) {
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
if (freeform) {
- a = createAspectScaledThumbnailEnterNonFullscreenAnimationLocked(
+ a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
containingFrame, surfaceInsets, taskId);
} else {
mTmpFromClipRect.set(containingFrame);
@@ -797,8 +796,8 @@
mNextAppTransitionInsets.set(contentInsets);
Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
- computePivot(mTmpStartRect.left, scale),
- computePivot(mTmpStartRect.top, scale));
+ computePivot(mTmpRect.left, scale),
+ computePivot(mTmpRect.top, scale));
Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
@@ -834,44 +833,49 @@
}
case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
// App window scaling down from full screen
- mTmpFromClipRect.set(containingFrame);
- mTmpToClipRect.set(containingFrame);
- // exclude top screen decor (status bar) region from the destination clip.
- mTmpToClipRect.top = contentInsets.top;
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- // In portrait, we scale the width and clip to the top/left square
- scale = thumbWidth / appWidth;
- scaledTopDecor = (int) (scale * contentInsets.top);
- int unscaledThumbHeight = (int) (thumbHeight / scale);
- mTmpToClipRect.bottom = mTmpToClipRect.top + unscaledThumbHeight;
+ if (freeform) {
+ a = createAspectScaledThumbnailExitFreeformAnimationLocked(
+ containingFrame, surfaceInsets, taskId);
} else {
- // In landscape, we scale the height and clip to the top/left square. We only
- // scale the part that is not covered by status bar and the nav bar.
- scale = thumbHeight / (appHeight - contentInsets.top - contentInsets.bottom);
- scaledTopDecor = (int) (scale * contentInsets.top);
- int unscaledThumbWidth = (int) (thumbWidth / scale);
- mTmpToClipRect.right = mTmpToClipRect.left + unscaledThumbWidth;
- // This removes the navigation bar from the last frame, so it better matches the
- // thumbnail. We need to do this explicitly in landscape, because in portrait we
- // already crop vertically.
- mTmpToClipRect.bottom = mTmpToClipRect.bottom - contentInsets.bottom;
+ mTmpFromClipRect.set(containingFrame);
+ mTmpToClipRect.set(containingFrame);
+ // exclude top screen decor (status bar) region from the destination clip.
+ mTmpToClipRect.top = contentInsets.top;
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ // In portrait, we scale the width and clip to the top/left square
+ scale = thumbWidth / appWidth;
+ scaledTopDecor = (int) (scale * contentInsets.top);
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
+ mTmpToClipRect.bottom = mTmpToClipRect.top + unscaledThumbHeight;
+ } else {
+ // In landscape, we scale the height and clip to the top/left square. We only
+ // scale the part that is not covered by status bar and the nav bar.
+ scale = thumbHeight / (appHeight - contentInsets.top - contentInsets.bottom);
+ scaledTopDecor = (int) (scale * contentInsets.top);
+ int unscaledThumbWidth = (int) (thumbWidth / scale);
+ mTmpToClipRect.right = mTmpToClipRect.left + unscaledThumbWidth;
+ // This removes the navigation bar from the last frame, so it better matches the
+ // thumbnail. We need to do this explicitly in landscape, because in portrait we
+ // already crop vertically.
+ mTmpToClipRect.bottom = mTmpToClipRect.bottom - contentInsets.bottom;
+ }
+
+ mNextAppTransitionInsets.set(contentInsets);
+
+ Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
+ computePivot(mTmpRect.left, scale),
+ computePivot(mTmpRect.top, scale));
+ Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
+ Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
+
+ AnimationSet set = new AnimationSet(true);
+ set.addAnimation(clipAnim);
+ set.addAnimation(scaleAnim);
+ set.addAnimation(translateAnim);
+
+ a = set;
+ a.setZAdjustment(Animation.ZORDER_TOP);
}
-
- mNextAppTransitionInsets.set(contentInsets);
-
- Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
- computePivot(mTmpStartRect.left, scale),
- computePivot(mTmpStartRect.top, scale));
- Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
- Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
-
- AnimationSet set = new AnimationSet(true);
- set.addAnimation(clipAnim);
- set.addAnimation(scaleAnim);
- set.addAnimation(translateAnim);
-
- a = set;
- a.setZAdjustment(Animation.ZORDER_TOP);
break;
}
default:
@@ -884,27 +888,48 @@
mTouchResponseInterpolator);
}
- private Animation createAspectScaledThumbnailEnterNonFullscreenAnimationLocked(
- Rect frame, @Nullable Rect surfaceInsets, int taskId) {
- getNextAppTransitionStartRect(taskId, mTmpStartRect);
- float width = frame.width();
- float height = frame.height();
- float scaleWidth = mTmpStartRect.width() / width;
- float scaleHeight = mTmpStartRect.height() / height;
+ private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
+ @Nullable Rect surfaceInsets, int taskId) {
+ getNextAppTransitionStartRect(taskId, mTmpRect);
+ return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets,
+ true);
+ }
+
+ private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame,
+ @Nullable Rect surfaceInsets, int taskId) {
+ getNextAppTransitionStartRect(taskId, mTmpRect);
+ return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets,
+ false);
+ }
+
+ private AnimationSet createAspectScaledThumbnailFreeformAnimationLocked(Rect sourceFrame,
+ Rect destFrame, @Nullable Rect surfaceInsets, boolean enter) {
+ final float sourceWidth = sourceFrame.width();
+ final float sourceHeight = sourceFrame.height();
+ final float destWidth = destFrame.width();
+ final float destHeight = destFrame.height();
+ final float scaleH = enter ? sourceWidth / destWidth : destWidth / sourceWidth;
+ final float scaleV = enter ? sourceHeight / destHeight : destHeight / sourceHeight;
AnimationSet set = new AnimationSet(true);
- int surfaceInsetsHorizontal = surfaceInsets == null
+ final int surfaceInsetsH = surfaceInsets == null
? 0 : surfaceInsets.left + surfaceInsets.right;
- int surfaceInsetsVertical = surfaceInsets == null
+ final int surfaceInsetsV = surfaceInsets == null
? 0 : surfaceInsets.top + surfaceInsets.bottom;
// We want the scaling to happen from the center of the surface. In order to achieve that,
// 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 = mTmpStartRect.left + mTmpStartRect.width() / 2
- - (frame.left + frame.width() / 2);
- int fromY = mTmpStartRect.top + mTmpStartRect.height() / 2
- - (frame.top + frame.height() / 2);
- TranslateAnimation translation = new TranslateAnimation(fromX, 0, fromY, 0);
+ final float scaleHCenter = ((enter ? destWidth : sourceWidth) + surfaceInsetsH) / 2;
+ final float scaleVCenter = ((enter ? destHeight : sourceHeight) + surfaceInsetsV) / 2;
+ final ScaleAnimation scale = enter ?
+ new ScaleAnimation(scaleH, 1, scaleV, 1, scaleHCenter, scaleVCenter)
+ : new ScaleAnimation(1, scaleH, 1, scaleV, scaleHCenter, scaleVCenter);
+ final int sourceHCenter = sourceFrame.left + sourceFrame.width() / 2;
+ final int sourceVCenter = sourceFrame.top + sourceFrame.height() / 2;
+ final int destHCenter = destFrame.left + destFrame.width() / 2;
+ final int destVCenter = destFrame.top + destFrame.height() / 2;
+ final int fromX = enter ? sourceHCenter - destHCenter : destHCenter - sourceHCenter;
+ final int fromY = enter ? sourceVCenter - destVCenter : destVCenter - sourceVCenter;
+ final TranslateAnimation translation = enter ? new TranslateAnimation(fromX, 0, fromY, 0)
+ : new TranslateAnimation(0, fromX, 0, fromY);
set.addAnimation(scale);
set.addAnimation(translation);
return set;
@@ -917,7 +942,7 @@
Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit,
Bitmap thumbnailHeader) {
Animation a;
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ getDefaultNextAppTransitionStartRect(mTmpRect);
final int thumbWidthI = thumbnailHeader.getWidth();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = thumbnailHeader.getHeight();
@@ -928,8 +953,8 @@
float scaleW = appWidth / thumbWidth;
float scaleH = appHeight / thumbHeight;
Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mTmpStartRect.left, 1 / scaleW),
- computePivot(mTmpStartRect.top, 1 / scaleH));
+ computePivot(mTmpRect.left, 1 / scaleW),
+ computePivot(mTmpRect.top, 1 / scaleH));
scale.setInterpolator(mDecelerateInterpolator);
Animation alpha = new AlphaAnimation(1, 0);
@@ -945,8 +970,8 @@
float scaleW = appWidth / thumbWidth;
float scaleH = appHeight / thumbHeight;
a = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mTmpStartRect.left, 1 / scaleW),
- computePivot(mTmpStartRect.top, 1 / scaleH));
+ computePivot(mTmpRect.left, 1 / scaleW),
+ computePivot(mTmpRect.top, 1 / scaleH));
}
return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
@@ -962,7 +987,7 @@
final int appHeight = containingFrame.height();
Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
Animation a;
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ getDefaultNextAppTransitionStartRect(mTmpRect);
final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
@@ -974,8 +999,8 @@
float scaleW = thumbWidth / appWidth;
float scaleH = thumbHeight / appHeight;
a = new ScaleAnimation(scaleW, 1, scaleH, 1,
- computePivot(mTmpStartRect.left, scaleW),
- computePivot(mTmpStartRect.top, scaleH));
+ computePivot(mTmpRect.left, scaleW),
+ computePivot(mTmpRect.top, scaleH));
break;
}
case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
@@ -1002,8 +1027,8 @@
float scaleW = thumbWidth / appWidth;
float scaleH = thumbHeight / appHeight;
Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
- computePivot(mTmpStartRect.left, scaleW),
- computePivot(mTmpStartRect.top, scaleH));
+ computePivot(mTmpRect.left, scaleW),
+ computePivot(mTmpRect.top, scaleH));
Animation alpha = new AlphaAnimation(1, 0);
@@ -1492,15 +1517,15 @@
pw.print(Integer.toHexString(mNextAppTransitionInPlace));
break;
case NEXT_TRANSIT_TYPE_SCALE_UP: {
- getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ getDefaultNextAppTransitionStartRect(mTmpRect);
pw.print(prefix); pw.print("mNextAppTransitionStartX=");
- pw.print(mTmpStartRect.left);
+ pw.print(mTmpRect.left);
pw.print(" mNextAppTransitionStartY=");
- pw.println(mTmpStartRect.top);
+ pw.println(mTmpRect.top);
pw.print(prefix); pw.print("mNextAppTransitionStartWidth=");
- pw.print(mTmpStartRect.width());
+ pw.print(mTmpRect.width());
pw.print(" mNextAppTransitionStartHeight=");
- pw.println(mTmpStartRect.height());
+ pw.println(mTmpRect.height());
break;
}
case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 943e3ea..c5bd3a7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -17,6 +17,9 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerService.TAG;
import com.android.server.input.InputApplicationHandle;
import com.android.server.wm.WindowManagerService.H;
@@ -296,6 +299,52 @@
}
}
+ /**
+ * Checks whether we should save surfaces for this app.
+ *
+ * @return true if the surfaces should be saved, false otherwise.
+ */
+ boolean shouldSaveSurface() {
+ // We want to save surface if the app's windows are "allDrawn", or if we're
+ // currently animating with save surfaces. (If the app didn't even finish
+ // drawing when the user exits, but we have a saved surface from last time,
+ // we still want to keep that surface.)
+ mHasSavedSurface = allDrawn || mAnimatingWithSavedSurface;
+ if (mHasSavedSurface) {
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+ "Saving surface: " + this);
+ return true;
+ }
+ return false;
+ }
+
+ void restoreSavedSurfaces() {
+ if (!mHasSavedSurface) {
+ return;
+ }
+
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+ "Restoring saved surfaces: " + this + ", allDrawn=" + allDrawn);
+
+ mHasSavedSurface = false;
+ mAnimatingWithSavedSurface = true;
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ WindowState ws = windows.get(i);
+ ws.mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW;
+ }
+ }
+
+ void destroySavedSurfaces() {
+ if (mHasSavedSurface) {
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+ "Destroying saved surface: " + this);
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ final WindowState win = windows.get(i);
+ win.mWinAnimator.destroySurfaceLocked();
+ }
+ }
+ }
+
@Override
void removeAllWindows() {
for (int winNdx = allAppWindows.size() - 1; winNdx >= 0;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 39479c1..fab8ee5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -18,7 +18,7 @@
import static android.app.ActivityManager.DOCKED_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
-
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
@@ -243,10 +243,30 @@
}
void moveStack(TaskStack stack, boolean toTop) {
+ if (stack.mStackId == PINNED_STACK_ID && !toTop) {
+ // Pinned stack is always-on-top silly...
+ Slog.w(TAG, "Ignoring move of always-on-top stack=" + stack + " to bottom");
+ return;
+ }
+
if (!mStacks.remove(stack)) {
Slog.wtf(TAG, "moving stack that was not added: " + stack, new Throwable());
}
- mStacks.add(toTop ? mStacks.size() : 0, stack);
+
+ int addIndex = toTop ? mStacks.size() : 0;
+
+ if (toTop
+ && mService.isStackVisibleLocked(PINNED_STACK_ID)
+ && stack.mStackId != PINNED_STACK_ID) {
+ // The pinned stack is always the top most stack (always-on-top) when it is visible.
+ // So, stack is moved just below the pinned stack.
+ addIndex--;
+ TaskStack topStack = mStacks.get(addIndex);
+ if (topStack.mStackId != PINNED_STACK_ID) {
+ throw new IllegalStateException("Pinned stack isn't top stack??? " + mStacks);
+ }
+ }
+ mStacks.add(addIndex, stack);
}
void detachStack(TaskStack stack) {
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 2d87123..04cba81 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -18,6 +18,8 @@
import static android.app.ActivityManager.DOCKED_STACK_ID;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -87,6 +89,8 @@
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
final int width = landscape ? mDividerWidth : MATCH_PARENT;
final int height = landscape ? MATCH_PARENT : mDividerWidth;
+ view.setPointerShape(
+ landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
width, height, TYPE_DOCK_DIVIDER,
FLAG_TOUCHABLE_WHEN_WAKING | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 6c391ad..5f911c3 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static com.android.server.wm.WindowManagerService.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerService.DEBUG_INPUT;
import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
import android.app.ActivityManagerNative;
import android.graphics.Rect;
@@ -179,7 +181,17 @@
if (modal && child.mAppToken != null) {
// Limit the outer touch to the activity stack region.
flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- child.getVisibleBounds(mTmpRect, BOUNDS_FOR_TOUCH);
+ // If this is a modal window we need to dismiss it if it's not full screen and the touch
+ // happens outside of the frame that displays the content. This means we need to
+ // intercept touches outside of that window. The dim layer user associated with the
+ // window (task or stack) will give us the good bounds, as they would be used to display
+ // the dim layer.
+ final DimLayer.DimLayerUser dimLayerUser = child.getDimLayerUser();
+ if (dimLayerUser != null) {
+ dimLayerUser.getBounds(mTmpRect);
+ } else {
+ child.getVisibleBounds(mTmpRect, BOUNDS_FOR_TOUCH);
+ }
inputWindowHandle.touchableRegion.set(mTmpRect);
} else {
// Not modal or full screen modal
@@ -227,7 +239,9 @@
inputWindowHandle.scaleFactor = 1;
}
-
+ if (DEBUG_INPUT) {
+ Slog.d(WindowManagerService.TAG, "addInputWindowHandle: " + inputWindowHandle);
+ }
addInputWindowHandleLw(inputWindowHandle);
}
@@ -428,7 +442,7 @@
* Layer assignment is assumed to be complete by the time this is called.
*/
public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
- if (WindowManagerService.DEBUG_FOCUS_LIGHT || WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_FOCUS_LIGHT || DEBUG_INPUT) {
Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow);
}
@@ -464,7 +478,7 @@
public void pauseDispatchingLw(WindowToken window) {
if (! window.paused) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
}
@@ -475,7 +489,7 @@
public void resumeDispatchingLw(WindowToken window) {
if (window.paused) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
}
@@ -486,7 +500,7 @@
public void freezeInputDispatchingLw() {
if (! mInputDispatchFrozen) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
}
@@ -497,7 +511,7 @@
public void thawInputDispatchingLw() {
if (mInputDispatchFrozen) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
}
@@ -508,7 +522,7 @@
public void setEventDispatchingLw(boolean enabled) {
if (mInputDispatchEnabled != enabled) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 4d11452..ad44196 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -342,6 +342,11 @@
return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null;
}
+ AppWindowToken getTopAppWindowToken() {
+ final int tokensCount = mAppTokens.size();
+ return tokensCount > 0 ? mAppTokens.get(tokensCount - 1) : null;
+ }
+
@Override
public boolean isFullscreen() {
if (useCurrentBounds()) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 9b3d478..df664bd 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -409,7 +409,7 @@
final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode
== DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
- mDisplayContent.mDividerControllerLocked.getWidthAdjustment(),
+ mDisplayContent.mDividerControllerLocked.getWidth(),
dockedOnTopOrLeft);
}
@@ -459,7 +459,7 @@
dockedStack.getRawBounds(mTmpRect2);
final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2,
- mDisplayContent.mDividerControllerLocked.getWidthAdjustment(), dockedOnTopOrLeft);
+ mDisplayContent.mDividerControllerLocked.getWidth(), dockedOnTopOrLeft);
}
@@ -469,13 +469,13 @@
* @param outBounds Output bounds that should be used for the stack.
* @param stackId Id of stack we are calculating the bounds for.
* @param dockedBounds Bounds of the docked stack.
- * @param adjustment Additional adjustment to make to the output bounds close to the side of the
- * dock.
- * @param dockOntopOrLeft If the docked stack is on the top or left side of the screen.
+ * @param dockDividerWidth We need to know the width of the divider make to the output bounds
+ * close to the side of the dock.
+ * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
*/
private static void getStackDockedModeBounds(
- Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int adjustment,
- boolean dockOntopOrLeft) {
+ Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth,
+ boolean dockOnTopOrLeft) {
final boolean dockedStack = stackId == DOCKED_STACK_ID;
final boolean splitHorizontally = displayRect.width() > displayRect.height();
@@ -484,34 +484,34 @@
// The initial bounds of the docked stack when it is created half the screen space and
// its bounds can be adjusted after that. The bounds of all other stacks are adjusted
// to occupy whatever screen space the docked stack isn't occupying.
- if (dockOntopOrLeft) {
+ if (dockOnTopOrLeft) {
if (splitHorizontally) {
- outBounds.right = displayRect.centerX() - adjustment;
+ outBounds.right = displayRect.centerX() - dockDividerWidth / 2;
} else {
- outBounds.bottom = displayRect.centerY() - adjustment;
+ outBounds.bottom = displayRect.centerY() - dockDividerWidth / 2;
}
} else {
if (splitHorizontally) {
- outBounds.left = displayRect.centerX() + adjustment;
+ outBounds.left = displayRect.centerX() + dockDividerWidth / 2;
} else {
- outBounds.top = displayRect.centerY() + adjustment;
+ outBounds.top = displayRect.centerY() + dockDividerWidth / 2;
}
}
return;
}
// Other stacks occupy whatever space is left by the docked stack.
- if (!dockOntopOrLeft) {
+ if (!dockOnTopOrLeft) {
if (splitHorizontally) {
- outBounds.right = dockedBounds.left - adjustment;
+ outBounds.right = dockedBounds.left - dockDividerWidth;
} else {
- outBounds.bottom = dockedBounds.top - adjustment;
+ outBounds.bottom = dockedBounds.top - dockDividerWidth;
}
} else {
if (splitHorizontally) {
- outBounds.left = dockedBounds.right + adjustment;
+ outBounds.left = dockedBounds.right + dockDividerWidth;
} else {
- outBounds.top = dockedBounds.bottom + adjustment;
+ outBounds.top = dockedBounds.bottom + dockDividerWidth;
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 47995a7..140fbaf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2637,6 +2637,11 @@
}
}
dragResizing = win.isDragResizing();
+ if (win.isAnimatingWithSavedSurface()) {
+ // If we're animating with a saved surface now, request client to report draw.
+ // We still need to know when the real thing is drawn.
+ toBeDisplayed = true;
+ }
try {
if (!win.mHasSurface) {
surfaceChanged = true;
@@ -2692,11 +2697,9 @@
// the app sets visibility to invisible for the first time after resume,
// or when the user exits immediately after a resume. In both cases, we
// don't want to destroy the saved surface.
- final boolean isAnimatingWithSavedSurface =
- win.mAppToken != null && win.mAppToken.mAnimatingWithSavedSurface;
// If we are not currently running the exit animation, we
// need to see about starting one.
- if (!win.mExiting && !isAnimatingWithSavedSurface) {
+ if (!win.mExiting && !win.isAnimatingWithSavedSurface()) {
surfaceChanged = true;
// Try starting an animation; if there isn't one, we
// can destroy the surface right away.
@@ -2722,7 +2725,9 @@
if (mInputMethodWindow == win) {
mInputMethodWindow = null;
}
- winAnimator.destroySurfaceLocked();
+ if (!win.shouldSaveSurface()) {
+ winAnimator.destroySurfaceLocked();
+ }
}
//TODO (multidisplay): Magnification is supported only for the default
if (mAccessibilityController != null
@@ -2855,12 +2860,6 @@
getDefaultDisplayContentLocked().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
- if (win.mAppToken != null) {
- // App has drawn something to its windows, we're no longer animating with
- // the saved surfaces. If the user exits now, we only want to save again
- // if allDrawn is true.
- win.mAppToken.mAnimatingWithSavedSurface = false;
- }
win.setDisplayLayoutNeeded();
mWindowPlacerLocked.requestTraversal();
}
@@ -3284,10 +3283,8 @@
}
}
- final TaskStack dockedStack = mStackIdToStack.get(DOCKED_STACK_ID);
- final TaskStack freeformStack = mStackIdToStack.get(FREEFORM_WORKSPACE_STACK_ID);
- if ((dockedStack != null && dockedStack.isVisibleLocked())
- || (freeformStack != null && freeformStack.isVisibleLocked())) {
+ if (isStackVisibleLocked(DOCKED_STACK_ID)
+ || isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
// We don't let app affect the system orientation when in freeform or docked mode since
// they don't occupy the entire display and their request can conflict with other apps.
return SCREEN_ORIENTATION_UNSPECIFIED;
@@ -4518,6 +4515,11 @@
}
}
+ boolean isStackVisibleLocked(int stackId) {
+ final TaskStack stack = mStackIdToStack.get(stackId);
+ return (stack != null && stack.isVisibleLocked());
+ }
+
public void setDockedStackCreateMode(int mode) {
synchronized (mWindowMap) {
sDockedStackCreateMode = mode;
@@ -6112,6 +6114,10 @@
final WindowList windows = displayContent.getWindowList();
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState w = windows.get(i);
+ // Discard surface after orientation change, these can't be reused.
+ if (w.mAppToken != null) {
+ w.mAppToken.destroySavedSurfaces();
+ }
if (w.mHasSurface) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w);
w.mOrientationChanging = true;
@@ -8767,12 +8773,14 @@
} else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
Slog.w(TAG, "LEAKED SURFACE (app token hidden): "
+ ws + " surface=" + wsa.mSurfaceControl
- + " token=" + ws.mAppToken);
+ + " token=" + ws.mAppToken
+ + " saved=" + ws.mAppToken.mHasSavedSurface);
if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
wsa.mSurfaceControl.destroy();
wsa.mSurfaceShown = false;
wsa.mSurfaceControl = null;
ws.mHasSurface = false;
+ ws.mAppToken.mHasSavedSurface = false;
leakedSurface = true;
}
}
@@ -10214,8 +10222,7 @@
@Override
public boolean isStackVisible(int stackId) {
synchronized (mWindowMap) {
- final TaskStack stack = mStackIdToStack.get(stackId);
- return (stack != null && stack.isVisibleLocked());
+ return WindowManagerService.this.isStackVisibleLocked(stackId);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e9ea3a8..1d2cb75 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -29,6 +29,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerService.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
@@ -1505,29 +1507,26 @@
return mExiting || (mService.mClosingApps.contains(mAppToken));
}
- boolean saveSurfaceIfNeeded() {
+ boolean isAnimatingWithSavedSurface() {
+ return mAppToken != null && mAppToken.mAnimatingWithSavedSurface;
+ }
+
+ boolean shouldSaveSurface() {
if (ActivityManager.isLowRamDeviceStatic()) {
// Don't save surfaces on Svelte devices.
return false;
}
Task task = getTask();
- if (task == null || task.inHomeStack()) {
+ if (task == null || task.inHomeStack()
+ || task.getTopAppWindowToken() != mAppToken) {
// Don't save surfaces for home stack apps. These usually resume and draw
// first frame very fast. Saving surfaces are mostly a waste of memory.
+ // Don't save if the window is not the topmost window.
return false;
}
- // We want to save surface if the app's windows are "allDrawn", or if we're
- // currently animating with save surfaces. (If the app didn't even finish
- // drawing when the user exits, but we have a saved surface from last time,
- // we still want to keep that surface.)
- if (mAppToken.allDrawn || mAppToken.mAnimatingWithSavedSurface) {
- mAppToken.mHasSavedSurface = true;
- return true;
- }
-
- return false;
+ return mAppToken.shouldSaveSurface();
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 68c39ba..80f1094 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -525,6 +525,12 @@
Slog.v(TAG, "Finishing drawing window " + mWin + ": mDrawState="
+ drawStateToString());
}
+ if (mWin.mAppToken != null) {
+ // App has drawn something to its windows, we're no longer animating with
+ // the saved surfaces. If the user exits now, we only want to save again
+ // if allDrawn is true.
+ mWin.mAppToken.mAnimatingWithSavedSurface = false;
+ }
if (mDrawState == DRAW_PENDING) {
if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + this + " in "
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index b267860..aca0f5b 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -363,8 +363,8 @@
continue;
}
// Discard the saved surface if window size is changed, it can't be reused.
- if (win.mAppToken != null && win.mAppToken.mHasSavedSurface) {
- win.mWinAnimator.destroySurfaceLocked();
+ if (win.mAppToken != null) {
+ win.mAppToken.destroySavedSurfaces();
}
win.reportResized();
mService.mResizingWindows.remove(i);
@@ -397,7 +397,7 @@
if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
wallpaperDestroyed = true;
}
- if (!win.saveSurfaceIfNeeded()) {
+ if (!win.shouldSaveSurface()) {
win.mWinAnimator.destroySurfaceLocked();
}
} while (i > 0);
@@ -1191,7 +1191,10 @@
if (animLp != null) {
int layer = -1;
for (int j = 0; j < wtoken.windows.size(); j++) {
- WindowState win = wtoken.windows.get(j);
+ final WindowState win = wtoken.windows.get(j);
+ // Clearing the mExiting flag before entering animation. It will be set
+ // to true if app window is removed, or window relayout to invisible.
+ win.mExiting = false;
if (win.mWinAnimator.mAnimLayer > layer) {
layer = win.mWinAnimator.mAnimLayer;
}
@@ -1203,17 +1206,7 @@
}
createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
- if (wtoken.mHasSavedSurface) {
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "Early start of animation: " + wtoken + ", allDrawn=" + wtoken.allDrawn);
-
- for (int j = 0; j < wtoken.windows.size(); j++) {
- WindowState ws = wtoken.windows.get(j);
- ws.mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW;
- }
- wtoken.mHasSavedSurface = false;
- wtoken.mAnimatingWithSavedSurface = true;
- }
+ wtoken.restoreSavedSurfaces();
}
AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null :
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 28cb114..c9efc69 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -417,9 +417,10 @@
encap, mTransactionId, getSecs(), clientAddress,
DO_UNICAST, mHwAddr, requestedAddress,
serverAddress, REQUESTED_PARAMS, null);
+ String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
" request=" + requestedAddress.getHostAddress() +
- " to=" + serverAddress.getHostAddress();
+ " serverid=" + serverStr;
return transmitPacket(packet, description, to);
}
@@ -822,7 +823,8 @@
public void enter() {
super.enter();
if (!setIpAddress(mDhcpLease.ipAddress) ||
- !connectUdpSock((mDhcpLease.serverAddress))) {
+ (mDhcpLease.serverAddress != null &&
+ !connectUdpSock((mDhcpLease.serverAddress)))) {
notifyFailure();
// There's likely no point in going into DhcpInitState here, we'll probably just
// repeat the transaction, get the same IP address as before, and fail.
@@ -878,11 +880,15 @@
}
protected boolean sendPacket() {
+ // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
+ // http://b/25343517 . Try to make things work anyway by using broadcast renews.
+ Inet4Address to = (mDhcpLease.serverAddress != null) ?
+ mDhcpLease.serverAddress : INADDR_BROADCAST;
return sendRequestPacket(
(Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr
INADDR_ANY, // DHCP_REQUESTED_IP
- INADDR_ANY, // DHCP_SERVER_IDENTIFIER
- (Inet4Address) mDhcpLease.serverAddress); // packet destination address
+ null, // DHCP_SERVER_IDENTIFIER
+ to); // packet destination address
}
protected void receivePacket(DhcpPacket packet) {
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 701272e..129e537 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -212,6 +212,10 @@
}
private AlsaDevice waitForAlsaDevice(int card, int device, int type) {
+ if (DEBUG) {
+ Slog.e(TAG, "waitForAlsaDevice(c:" + card + " d:" + device + ")");
+ }
+
AlsaDevice testDevice = new AlsaDevice(type, card, device);
// This value was empirically determined.
@@ -292,7 +296,8 @@
*/
/* package */ UsbAudioDevice selectAudioCard(int card) {
if (DEBUG) {
- Slog.d(TAG, "selectAudioCard() card:" + card);
+ Slog.d(TAG, "selectAudioCard() card:" + card
+ + " isCardUsb(): " + mCardsParser.isCardUsb(card));
}
if (!mCardsParser.isCardUsb(card)) {
// Don't. AudioPolicyManager has logic for falling back to internal devices.
@@ -304,6 +309,10 @@
boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card);
boolean hasCapture = mDevicesParser.hasCaptureDevices(card);
+ if (DEBUG) {
+ Slog.d(TAG, "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture);
+ }
+
int deviceClass =
(mCardsParser.isCardUsb(card)
? UsbAudioDevice.kAudioDeviceClass_External
@@ -320,10 +329,6 @@
return null;
}
- if (DEBUG) {
- Slog.d(TAG, "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture);
- }
-
UsbAudioDevice audioDevice =
new UsbAudioDevice(card, device, hasPlayback, hasCapture, deviceClass);
AlsaCardsParser.AlsaCardRecord cardRecord = mCardsParser.getCardRecordFor(card);
@@ -339,14 +344,13 @@
if (DEBUG) {
Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()");
}
- mCardsParser.scan();
return selectAudioCard(mCardsParser.getDefaultCard());
}
/* package */ void usbDeviceAdded(UsbDevice usbDevice) {
if (DEBUG) {
Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName() +
- "nm:" + usbDevice.getProductName());
+ " nm:" + usbDevice.getProductName());
}
// Is there an audio interface in there?
@@ -361,27 +365,22 @@
isAudioDevice = true;
}
}
+
+ if (DEBUG) {
+ Slog.d(TAG, " isAudioDevice: " + isAudioDevice);
+ }
if (!isAudioDevice) {
return;
}
- ArrayList<AlsaCardsParser.AlsaCardRecord> prevScanRecs = mCardsParser.getScanRecords();
- mCardsParser.scan();
-
- int addedCard = -1;
- ArrayList<AlsaCardsParser.AlsaCardRecord>
- newScanRecs = mCardsParser.getNewCardRecords(prevScanRecs);
- if (newScanRecs.size() > 0) {
- // This is where we select the just connected device
- // NOTE - to switch to prefering the first-connected device, just always
- // take the else clause below.
- addedCard = newScanRecs.get(0).mCardNum;
- } else {
- addedCard = mCardsParser.getDefaultUsbCard();
- }
+ int addedCard = mCardsParser.getDefaultUsbCard();
// If the default isn't a USB device, let the existing "select internal mechanism"
// handle the selection.
+ if (DEBUG) {
+ Slog.d(TAG, " mCardsParser.isCardUsb(" + addedCard + ") = "
+ + mCardsParser.isCardUsb(addedCard));
+ }
if (mCardsParser.isCardUsb(addedCard)) {
UsbAudioDevice audioDevice = selectAudioCard(addedCard);
if (audioDevice != null) {
@@ -429,6 +428,10 @@
}
}
}
+
+ if (DEBUG) {
+ Slog.d(TAG, "deviceAdded() - done");
+ }
}
/* package */ void usbDeviceRemoved(UsbDevice usbDevice) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 3160e39..e0f95cf 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -756,6 +756,8 @@
if (mUsbDataUnlocked && active && mCurrentUser != UserHandle.USER_NULL) {
Slog.v(TAG, "Current user switched to " + mCurrentUser
+ "; resetting USB host stack for MTP or PTP");
+ // avoid leaking sensitive data from previous user
+ mUsbDataUnlocked = false;
setEnabledFunctions(mCurrentFunctions, true);
}
mCurrentUser = msg.arg1;