summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt5
-rw-r--r--api/system-current.txt5
-rw-r--r--core/java/android/app/ActivityOptions.java34
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/app/NotificationManager.java23
-rw-r--r--core/java/android/bluetooth/BluetoothHeadsetClient.java35
-rw-r--r--core/java/android/bluetooth/IBluetoothHeadsetClient.aidl2
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java2
-rw-r--r--core/java/android/os/ShellCommand.java3
-rw-r--r--core/java/android/provider/Settings.java5
-rw-r--r--core/java/android/service/notification/ConditionProviderService.java3
-rw-r--r--core/java/android/view/View.java160
-rw-r--r--core/java/android/view/ViewGroup.java8
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java26
-rw-r--r--core/java/android/webkit/WebResourceRequest.java7
-rw-r--r--core/java/android/webkit/WebViewClient.java30
-rw-r--r--core/java/com/android/internal/alsa/AlsaCardsParser.java55
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java2
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp2
-rw-r--r--core/jni/android/graphics/Graphics.cpp8
-rw-r--r--core/jni/android_view_Surface.cpp7
-rw-r--r--core/jni/android_view_SurfaceControl.cpp23
-rw-r--r--core/jni/android_view_TextureView.cpp22
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--core/res/res/values/attrs.xml7
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java92
-rw-r--r--graphics/java/android/graphics/drawable/ClipDrawable.java84
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java28
-rw-r--r--graphics/java/android/graphics/drawable/DrawableWrapper.java124
-rw-r--r--graphics/java/android/graphics/drawable/InsetDrawable.java170
-rw-r--r--graphics/java/android/graphics/drawable/RotateDrawable.java84
-rw-r--r--graphics/java/android/graphics/drawable/ScaleDrawable.java95
-rw-r--r--libs/hwui/Android.mk2
-rw-r--r--libs/hwui/DisplayListCanvas.cpp2
-rw-r--r--libs/hwui/RecordingCanvas.cpp2
-rw-r--r--libs/hwui/RecordingCanvas.h2
-rw-r--r--libs/hwui/RenderNode.cpp16
-rw-r--r--libs/hwui/SkiaCanvas.cpp16
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp2
-rw-r--r--libs/hwui/SkiaCanvasProxy.h2
-rw-r--r--libs/hwui/SkiaShader.cpp4
-rw-r--r--libs/hwui/TreeInfo.h50
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp5
-rw-r--r--libs/hwui/renderthread/CanvasContext.h4
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp2
-rw-r--r--libs/hwui/unit_tests/FatVectorTests.cpp98
-rw-r--r--libs/hwui/unit_tests/LinearAllocatorTests.cpp44
-rw-r--r--libs/hwui/unit_tests/TestUtils.h18
-rw-r--r--libs/hwui/utils/FatVector.h101
-rw-r--r--libs/hwui/utils/NinePatchImpl.cpp2
-rw-r--r--media/java/android/media/browse/MediaBrowser.java122
-rw-r--r--packages/DocumentsUI/AndroidManifest.xml4
-rw-r--r--packages/DocumentsUI/res/color/item_doc_grid_overlay.xml4
-rw-r--r--packages/DocumentsUI/res/values-sw720dp/styles.xml2
-rw-r--r--packages/DocumentsUI/res/values/colors.xml22
-rw-r--r--packages/DocumentsUI/res/values/styles.xml43
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java80
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java2
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/Menus.java4
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java128
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java11
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml2
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml2
-rw-r--r--packages/SystemUI/res/values-si-rLK/strings.xml3
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Constants.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/ParametricCurve.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/Task.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java230
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java281
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java23
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java86
-rw-r--r--services/core/java/com/android/server/BatteryService.java190
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java97
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java211
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java30
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java65
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java53
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java22
-rw-r--r--services/core/java/com/android/server/input/InputWindowHandle.java11
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java24
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java33
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java12
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java137
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java8
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java203
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java49
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java24
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java4
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java30
-rw-r--r--services/core/java/com/android/server/wm/Task.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java34
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java41
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java23
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java23
-rw-r--r--services/core/jni/com_android_server_tv_TvInputHal.cpp8
-rw-r--r--services/java/com/android/server/SystemServer.java17
-rw-r--r--services/net/java/android/net/dhcp/DhcpClient.java14
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaManager.java45
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java2
-rw-r--r--telephony/java/com/android/ims/ImsCallProfile.java2
123 files changed, 2842 insertions, 1530 deletions
diff --git a/api/current.txt b/api/current.txt
index 666bf83c2a05..eb888afc10dd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36477,6 +36477,7 @@ package android.view {
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 @@ package android.webkit {
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 @@ package android.webkit {
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 3554e0ffe967..d288ad1a4443 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -38774,6 +38774,7 @@ package android.view {
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 @@ package android.webkit {
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 @@ package android.webkit {
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 821621ef3239..0266a377349b 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -25,10 +25,12 @@ import android.graphics.Rect;
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 class ActivityOptions {
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 @@ public class ActivityOptions {
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 @@ public class ActivityOptions {
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 @@ public class ActivityOptions {
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 @@ public class ActivityOptions {
}
/** @hide */
+ public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; }
+
+ /** @hide */
public static void abort(Bundle options) {
if (options != null) {
(new ActivityOptions(options)).abort();
@@ -898,6 +928,7 @@ public class ActivityOptions {
mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
break;
}
+ mAnimSpecs = otherOptions.mAnimSpecs;
}
/**
@@ -964,6 +995,9 @@ public class ActivityOptions {
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/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index e95a35a23b6c..30232da96e90 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -83,10 +83,8 @@ interface INotificationManager
int getZenMode();
ZenModeConfig getZenModeConfig();
- boolean setZenModeConfig(in ZenModeConfig config, String reason);
oneway void setZenMode(int mode, in Uri conditionId, String reason);
oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
- oneway void requestZenModeConditions(in IConditionListener callback, int relevance);
boolean isNotificationPolicyAccessGranted(String pkg);
NotificationManager.Policy getNotificationPolicy(String pkg);
void setNotificationPolicy(String pkg, in NotificationManager.Policy policy);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index f75b22af1139..07b4d39d82f4 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -348,29 +348,6 @@ public class NotificationManager
/**
* @hide
*/
- public boolean setZenModeConfig(ZenModeConfig config, String reason) {
- INotificationManager service = getService();
- try {
- return service.setZenModeConfig(config, reason);
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
- * @hide
- */
- public void requestZenModeConditions(IConditionListener listener, int relevance) {
- INotificationManager service = getService();
- try {
- service.requestZenModeConditions(listener, relevance);
- } catch (RemoteException e) {
- }
- }
-
- /**
- * @hide
- */
public int getZenMode() {
INotificationManager service = getService();
try {
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index ff4ebee221e9..874026fb157d 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -1059,6 +1059,41 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
}
/**
+ * 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 e518b7d225f8..79ae4e48fa7d 100644
--- a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
@@ -62,6 +62,8 @@ interface IBluetoothHeadsetClient {
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 1fcfacab34a3..51796ebe463a 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -513,7 +513,7 @@ public final class CameraManager {
* {@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/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index d64273a36eac..73c2c804bdf1 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -44,7 +44,7 @@ public abstract class ShellCommand {
private FastPrintWriter mOutPrintWriter;
private FastPrintWriter mErrPrintWriter;
- public void exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) {
mTarget = target;
mIn = in;
@@ -89,6 +89,7 @@ public abstract class ShellCommand {
mResultReceiver.send(res, null);
}
if (DEBUG) Slog.d(TAG, "Finished command " + mCmd + " on " + mTarget);
+ return res;
}
public PrintWriter getOutPrintWriter() {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a1f9743d4d4e..ad46c3dadbd3 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5604,11 +5604,6 @@ public final class Settings {
public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES =
"enabled_notification_policy_access_packages";
- /**
- * @hide
- */
- public static final String ENABLED_CONDITION_PROVIDERS = "enabled_condition_providers";
-
/** @hide */
public static final String BAR_SERVICE_COMPONENT = "bar_service_component";
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index c679eda2fff2..bbac023b9a38 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -35,7 +35,8 @@ import android.util.Log;
* the {@link android.Manifest.permission#BIND_CONDITION_PROVIDER_SERVICE} permission
* and include an intent filter with the {@link #SERVICE_INTERFACE} action. If you want users to be
* able to create and update conditions for this service to monitor, include the
- * {@link #META_DATA_RULE_TYPE} and {@link #META_DATA_CONFIGURATION_ACTIVITY} tags. For example:</p>
+ * {@link #META_DATA_RULE_TYPE} and {@link #META_DATA_CONFIGURATION_ACTIVITY} tags and request the
+ * {@link android.Manifest.permission#ACCESS_NOTIFICATION_POLICY} permission. For example:</p>
* <pre>
* &lt;service android:name=".MyConditionProvider"
* android:label="&#64;string/service_name"
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d305f4dc7062..8b804e8ddc8a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2415,6 +2415,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* 1 PFLAG3_SCROLL_INDICATOR_START
* 1 PFLAG3_SCROLL_INDICATOR_END
* 1 PFLAG3_ASSIST_BLOCKED
+ * 1111111 PFLAG3_POINTER_ICON_MASK
* |-------|-------|-------|-------|
*/
@@ -2611,6 +2612,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
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 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
- 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/** @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 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
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 class View implements Drawable.Callback, KeyEvent.Callback,
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 7c7ad9102695..db978a6748f7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4435,6 +4435,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
}
+
+ if (mCurrentDragStartEvent != null && child.getVisibility() == VISIBLE) {
+ notifyChildOfDragStart(child);
+ }
}
private void addInArray(View child, int index) {
@@ -4686,6 +4690,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
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 1b1773669ce2..1735e1b9e829 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3083,8 +3083,32 @@ public class AccessibilityNodeInfo implements Parcelable {
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 0760d2b7bf9d..23e9a0df56fb 100644
--- a/core/java/android/webkit/WebResourceRequest.java
+++ b/core/java/android/webkit/WebResourceRequest.java
@@ -40,6 +40,13 @@ public interface WebResourceRequest {
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 f2bb55a7db13..0e5034de7f4f 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -38,12 +38,42 @@ public class WebViewClient {
* @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 &quot;method&quot;.</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 67ee08596553..17e8c9c80592 100644
--- a/core/java/com/android/internal/alsa/AlsaCardsParser.java
+++ b/core/java/com/android/internal/alsa/AlsaCardsParser.java
@@ -94,6 +94,12 @@ public class AlsaCardsParser {
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 @@ public class AlsaCardsParser {
// 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 class AlsaCardsParser {
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 @@ public class AlsaCardsParser {
}
}
}
+
+ 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 afef7633bc69..7fab31f6f9ea 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -5409,7 +5409,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
* @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 20d00b03092a..7b69c9e5b694 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -189,6 +189,7 @@ LOCAL_C_INCLUDES += \
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 28bc7fe47dd2..ecaf9519d8bc 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -165,7 +165,7 @@ public:
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 93259e70abbd..068517a12288 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -486,7 +486,7 @@ static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) {
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 @@ static void allocatePixelsReleaseProc(void* ptr, void* ctx) {
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 @@ android::Bitmap* GraphicsJNI::allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitm
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::allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitm
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 24055e76234e..da96b935883e 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -313,12 +313,11 @@ static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
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 65ebb6633a8f..931ad54fb5e8 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -138,35 +138,36 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
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 b736a17c1199..e185281dad8a 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -72,29 +72,25 @@ static struct {
// 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 39eda584e70c..5828829511c1 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 093ea80950b7..d7dd3ec92a56 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5018,6 +5018,12 @@ i
<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 @@ i
<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 4fc5edee94c6..971a3a295ca2 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -50,7 +50,7 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
* 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,17 +126,43 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
@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 {
+ @Override
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
+
+ final AnimatedRotateState state = mState;
+ if (state == null) {
+ return;
+ }
+
+ if (state.mThemeAttrs != null) {
+ final TypedArray a = t.resolveAttributes(
+ state.mThemeAttrs, R.styleable.AnimatedRotateDrawable);
+ 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.AnimatedRotateDrawable_drawable] == 0)) {
@@ -146,11 +172,17 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
}
}
- @Override
- void updateStateFromTypedArray(TypedArray a) {
- super.updateStateFromTypedArray(a);
-
+ 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);
@@ -168,38 +200,6 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
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;
- }
-
- if (state.mThemeAttrs != null) {
- final TypedArray a = t.resolveAttributes(
- state.mThemeAttrs, R.styleable.AnimatedRotateDrawable);
- 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();
}
public void setFramesCount(int framesCount) {
@@ -211,7 +211,15 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
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 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
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 31fccd0880c9..cdd336dded0a 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -21,6 +21,8 @@ import com.android.internal.R;
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 @@ public class ClipDrawable extends DrawableWrapper {
private ClipState mState;
ClipDrawable() {
- this(new ClipState(null), null);
+ this(new ClipState(null, null), null);
}
/**
@@ -72,7 +74,7 @@ public class ClipDrawable extends DrawableWrapper {
* {@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,45 +83,23 @@ public class ClipDrawable extends DrawableWrapper {
}
@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);
-
- 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);
- }
- }
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
- @Override
- public void applyTheme(Theme t) {
final ClipState state = mState;
if (state == null) {
return;
@@ -136,10 +116,34 @@ public class ClipDrawable extends DrawableWrapper {
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 @@ public class ClipDrawable extends DrawableWrapper {
}
}
+ @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 b95c183642ca..ff287776e1a3 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -55,6 +55,8 @@ import java.lang.ref.WeakReference;
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 @@ public abstract class Drawable {
/**
* 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 @@ public abstract class Drawable {
*
* @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 @@ public abstract class Drawable {
* @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 @@ public abstract class Drawable {
* @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 9185e1a06466..c42787000c49 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.NonNull;
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.PixelFormat;
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,32 +116,79 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
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;
}
- // Account for any configuration changes.
- state.mChangingConfigurations |= a.getChangingConfigurations();
+ // 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);
- // Extract the theme attributes, if any.
- state.mThemeAttrs = a.extractThemeAttrs();
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.DrawableWrapper);
+ updateStateFromTypedArray(a);
+ a.recycle();
- // TODO: Consider using R.styleable.DrawableWrapper_drawable
+ inflateChildDrawable(r, parser, attrs, theme);
}
@Override
- public void applyTheme(Resources.Theme t) {
+ 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;
}
- if (mDrawable != null && mDrawable.canApplyTheme()) {
- mDrawable.applyTheme(t);
+ 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;
+ }
+
+ // Account for any configuration changes.
+ state.mChangingConfigurations |= a.getChangingConfigurations();
+
+ // Extract the theme attributes, if any.
+ state.mThemeAttrs = a.extractThemeAttrs();
+
+ if (a.hasValueOrEmpty(R.styleable.DrawableWrapper_drawable)) {
+ setDrawable(a.getDrawable(R.styleable.DrawableWrapper_drawable));
}
}
@@ -371,8 +422,9 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
* 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 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
}
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 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
}
@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 e1ebdbb65a26..927b9c9d9f6c 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.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;
+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 @@ public class InsetDrawable extends DrawableWrapper {
* No-arg constructor used by drawable inflation.
*/
InsetDrawable() {
- this(new InsetState(null), null);
+ this(new InsetState(null, null), null);
}
/**
@@ -67,7 +70,7 @@ public class InsetDrawable extends DrawableWrapper {
* @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 @@ public class InsetDrawable extends DrawableWrapper {
* @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,69 +96,23 @@ public class InsetDrawable extends DrawableWrapper {
}
@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);
-
- 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;
- }
- }
- }
+ public void applyTheme(@NonNull Theme t) {
+ super.applyTheme(t);
- @Override
- public void applyTheme(Theme t) {
final InsetState state = mState;
if (state == null) {
return;
@@ -172,10 +129,47 @@ public class InsetDrawable extends DrawableWrapper {
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 @@ public class InsetDrawable extends DrawableWrapper {
@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 @@ public class InsetDrawable extends DrawableWrapper {
* 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 036a078eb00d..1531ba2380fb 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -21,6 +21,8 @@ import com.android.internal.R;
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 @@ public class RotateDrawable extends DrawableWrapper {
* 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 @@ public class RotateDrawable extends DrawableWrapper {
}
}
- @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 @@ public class RotateDrawable extends DrawableWrapper {
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 @@ public class RotateDrawable extends DrawableWrapper {
@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 @@ public class RotateDrawable extends DrawableWrapper {
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 f9206b7d3c26..f87c19a40538 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -21,6 +21,8 @@ import com.android.internal.R;
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 @@ public class ScaleDrawable extends DrawableWrapper {
private ScaleState mState;
ScaleDrawable() {
- this(new ScaleState(null), null);
+ this(new ScaleState(null, null), null);
}
/**
@@ -83,7 +85,7 @@ public class ScaleDrawable extends DrawableWrapper {
* 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 @@ public class ScaleDrawable extends DrawableWrapper {
}
@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 @@ public class ScaleDrawable extends DrawableWrapper {
}
}
- @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 @@ public class ScaleDrawable extends DrawableWrapper {
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 @@ public class ScaleDrawable extends DrawableWrapper {
}
@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 @@ public class ScaleDrawable extends DrawableWrapper {
@Override
DrawableWrapperState mutateConstantState() {
- mState = new ScaleState(mState);
+ mState = new ScaleState(mState, null);
return mState;
}
@@ -251,14 +254,16 @@ public class ScaleDrawable extends DrawableWrapper {
/** 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 4385e7012f54..d94c91d64572 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -113,6 +113,7 @@ $(call local-generated-sources-dir)/proto/$(LOCAL_PATH)
endef
hwui_c_includes += \
+ external/skia/include/private \
external/skia/src/core
hwui_shared_libraries := \
@@ -202,6 +203,7 @@ LOCAL_SRC_FILES += \
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 bad397219398..f5e57355138c 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -584,7 +584,7 @@ void DisplayListCanvas::refBitmapsInShader(const SkShader* shader) {
// 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 1f113bc19ebb..273af3ac5997 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -458,7 +458,7 @@ void RecordingCanvas::refBitmapsInShader(const SkShader* shader) {
// 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 9c32b1a769dc..454ee24cfc64 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 8011869360ca..39cb8e9229b1 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -254,7 +254,8 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
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 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
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 @@ void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayL
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 36633b5205a1..a8f8134170d3 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -194,15 +194,13 @@ private:
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 @@ void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
// ----------------------------------------------------------------------------
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 @@ void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
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 c3f5eb27e236..2d5f70f6a70c 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -125,7 +125,7 @@ void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScal
}
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 0de965094e64..2fe4327b9539 100644
--- a/libs/hwui/SkiaCanvasProxy.h
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -63,7 +63,7 @@ protected:
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 6c105cfc7b00..83652c6de21f 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -204,7 +204,7 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model
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 @@ SkiaShaderType getComposeSubType(const SkShader& shader) {
}
// 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 98e61468ea70..1c3148726b63 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -55,70 +55,46 @@ public:
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 73af4c4493bb..fac26dc2bfa2 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -199,7 +199,6 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
info.damageAccumulator = &mDamageAccumulator;
info.renderer = mCanvas;
- info.canvasContext = this;
mAnimationContext->startFrame(info.mode);
for (const sp<RenderNode>& node : mRenderNodes) {
@@ -507,7 +506,7 @@ void CanvasContext::prepareAndDraw(RenderNode* node) {
.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 @@ void CanvasContext::buildLayer(RenderNode* node) {
// 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 f362584af3dd..30e6562526d5 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -130,6 +130,10 @@ public:
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 a47c9ecf8b31..ab860c7d3b32 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -87,7 +87,7 @@ void DrawFrameTask::run() {
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 000000000000..fb760ac549cd
--- /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 02cd77ae93db..0f6b2494c237 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 @@ struct SimplePair {
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, alloc) {
}
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 @@ TEST(LinearAllocator, rewind) {
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 99ecc9b99284..5b09fdac4ff2 100644
--- a/libs/hwui/unit_tests/TestUtils.h
+++ b/libs/hwui/unit_tests/TestUtils.h
@@ -39,6 +39,24 @@ namespace uirenderer {
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 000000000000..c3c16c5ae27d
--- /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 f51f5df2402c..985f3fb66814 100644
--- a/libs/hwui/utils/NinePatchImpl.cpp
+++ b/libs/hwui/utils/NinePatchImpl.cpp
@@ -82,7 +82,7 @@ static void drawStretchyPatch(SkCanvas* canvas, SkIRect& src, const SkRect& dst,
}
} 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 547665184233..e0a8026221e7 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -776,68 +776,88 @@ public final class MediaBrowser {
*/
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();
- }
- 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();
+ // 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();
+ }
+ }
}
- }
+ });
}
@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;
- }
+ // 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;
- // 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();
+ }
+ });
+ }
- // 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);
+ }
}
/**
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index f9e80275b10d..d45345e41584 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -40,7 +40,7 @@
<activity
android:name=".ManageRootActivity"
- android:theme="@style/DocumentsNonDialogTheme"
+ android:theme="@style/DocumentsFullScreenTheme"
android:icon="@drawable/ic_doc_text">
<intent-filter>
<action android:name="android.provider.action.MANAGE_ROOT" />
@@ -63,7 +63,7 @@
<activity
android:name=".FilesActivity"
- android:theme="@style/FilesTheme"
+ android:theme="@style/DocumentsFullScreenTheme"
android:icon="@drawable/ic_files_app"
android:label="@string/files_label"
android:documentLaunchMode="intoExisting">
diff --git a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
index ab414a9bb6af..bf19d4e111a0 100644
--- a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
+++ b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
@@ -16,10 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:state_focused="true"
- android:color="@color/platform_blue_a200"
- android:alpha="0.1" />
- <item
android:state_activated="true"
android:color="?android:attr/colorAccent"
android:alpha="0.1" />
diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml
index d415a849d33f..a8dcbb0bbd8a 100644
--- a/packages/DocumentsUI/res/values-sw720dp/styles.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/styles.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="DocumentsBaseTheme" parent="@style/Theme.AppCompat.Dialog">
+ <style name="DocumentsBaseTheme" parent="@style/Theme.AppCompat.Light.Dialog">
<!-- We do not specify width of window here because the max size of
floating window specified by windowFixedWidthis is limited. -->
<item name="*android:windowFixedHeightMajor">80%</item>
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index cb6957dd3463..a376418e01c2 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -15,23 +15,15 @@
-->
<resources>
- <color name="material_grey_50">#fffafafa</color>
- <color name="material_grey_300">#ffeeeeee</color>
- <color name="material_grey_600">#ff757575</color>
- <color name="material_grey_800">#ff424242</color>
+ <color name="material_grey_400">#ffbdbdbd</color>
- <color name="primary_dark">@*android:color/material_blue_grey_900</color>
- <color name="primary">@*android:color/material_blue_grey_800</color>
- <color name="accent">@*android:color/material_deep_teal_500</color>
-
- <color name="platform_blue_100">#ffd0d9ff</color>
- <color name="platform_blue_500">#ff5677fc</color>
- <color name="platform_blue_700">#ff455ede</color>
- <color name="platform_blue_a100">#ffa6baff</color>
- <color name="platform_blue_a200">#ffff5177</color>
+ <color name="primary_dark">@*android:color/primary_dark_material_dark</color>
+ <color name="primary">@*android:color/material_blue_grey_900</color>
+ <color name="accent">@*android:color/accent_material_light</color>
+ <color name="action_mode">@color/material_grey_400</color>
- <color name="directory_background">@color/material_grey_300</color>
- <color name="item_doc_grid_background">#FFFFFFFF</color>
+ <color name="directory_background">@*android:color/material_grey_300</color>
+ <color name="item_doc_grid_background">@android:color/white</color>
<color name="item_doc_grid_protect_background">#88000000</color>
<color name="band_select_background">#88ffffff</color>
<color name="band_select_border">#44000000</color>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index c13f144d0717..15d17cc6b737 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -28,7 +28,7 @@
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<item name="android:colorPrimary">@color/primary</item>
<item name="android:colorAccent">@color/accent</item>
- <item name="colorActionMode">@color/material_grey_800</item>
+ <item name="colorActionMode">@color/action_mode</item>
<item name="android:listDivider">@*android:drawable/list_divider_material</item>
@@ -37,14 +37,17 @@
<item name="android:windowNoTitle">true</item>
<item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
- <item name="android:alertDialogTheme">@android:style/Theme.Material.Light.Dialog.Alert</item>
</style>
- <style name="DocumentsBaseTheme.FullScreen" parent="@style/Theme.AppCompat.Light.DarkActionBar">
+ <style name="DocumentsFullScreenTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
<item name="actionBarWidgetTheme">@null</item>
<item name="actionBarTheme">@style/ActionBarTheme</item>
<item name="actionBarPopupTheme">@style/ActionBarPopupTheme</item>
- <item name="colorActionMode">@color/material_grey_800</item>
+
+ <item name="android:colorPrimaryDark">@color/primary_dark</item>
+ <item name="android:colorPrimary">@color/primary</item>
+ <item name="android:colorAccent">@color/accent</item>
+ <item name="colorActionMode">@color/action_mode</item>
<item name="android:listDivider">@*android:drawable/list_divider_material</item>
@@ -55,38 +58,6 @@
<item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
</style>
- <style name="DocumentsNonDialogTheme" parent="@style/DocumentsBaseTheme.FullScreen">
- <item name="android:colorPrimaryDark">@color/primary_dark</item>
- <item name="android:colorPrimary">@color/primary</item>
- <item name="android:colorAccent">@color/accent</item>
-
- <item name="android:actionModeStyle">@style/ActionModeStyle</item>
-
- <item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
- </style>
-
- <style name="ActionModeStyle" parent="@android:style/Widget.Material.Light.ActionMode">
- <item name="android:background">@color/material_grey_600</item>
- </style>
-
- <style name="AlertDialogTheme" parent="@android:style/Theme.Material.Light.Dialog.Alert">
- <item name="android:colorAccent">@color/platform_blue_700</item>
- </style>
-
- <style name="FilesTheme" parent="@style/DocumentsBaseTheme.FullScreen">
- <item name="android:colorPrimaryDark">@color/platform_blue_700</item>
- <item name="android:colorPrimary">@color/platform_blue_500</item>
- <item name="android:colorAccent">@color/platform_blue_700</item>
- <item name="colorControlActivated">@color/platform_blue_a100</item>
- <item name="android:actionModeStyle">@style/FilesActionModeStyle</item>
- <item name="colorActionMode">@color/platform_blue_700</item>
- <item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
- </style>
-
- <style name="FilesActionModeStyle" parent="@android:style/Widget.Material.Light.ActionMode">
- <item name="android:background">@color/platform_blue_100</item>
- </style>
-
<style name="TrimmedHorizontalProgressBar" parent="android:Widget.Material.ProgressBar.Horizontal">
<item name="android:indeterminateDrawable">@drawable/progress_indeterminate_horizontal_material_trimmed</item>
<item name="android:minHeight">3dp</item>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 45a890702c0f..a09a22a19037 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.CancellationSignal;
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.BaseActivity.DocumentContext;
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 @@ public class DirectoryFragment extends Fragment {
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 @@ public class DirectoryFragment extends Fragment {
// Kick off loader at least once
getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
- mFragmentTuner.afterActivityCreated(this);
updateDisplayState();
}
@@ -1638,21 +1636,6 @@ public class DirectoryFragment extends Fragment {
}
}
- 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 @@ public class DirectoryFragment extends Fragment {
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/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index aae526930030..18957ee66e38 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -86,7 +86,7 @@ public class DocumentsActivity extends BaseActivity {
mShowAsDialog = res.getBoolean(R.bool.show_as_dialog);
if (!mShowAsDialog) {
- setTheme(R.style.DocumentsNonDialogTheme);
+ setTheme(R.style.DocumentsFullScreenTheme);
}
if (mShowAsDialog) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Menus.java b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
index 3f43a3d30c8d..5277d2bb86e9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Menus.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
@@ -19,14 +19,14 @@ package com.android.documentsui;
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 000000000000..ca85cff8a32b
--- /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 df9d44ac3755..df4a9f0b5325 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -20,8 +20,15 @@ import java.nio.charset.StandardCharsets;
* 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/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5d622a069412..be5c0fe143b2 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -214,6 +214,7 @@
android:resumeWhilePausing="true"
android:screenOrientation="behind"
android:resizeableActivity="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize"
android:theme="@style/RecentsTheme.Wallpaper">
<intent-filter>
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 1c99a2333b06..6d9d43f7a448 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 3fee28e2ce10..6beed78e2cc4 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 e839aaa39a63..185fc05769a7 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 07c59a9b3b94..0c638a2b7164 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 00f484d3cc76..a4137b99eb7a 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 6668df93511d..ebfacac1b1fb 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 class Constants {
}
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 94001084b76a..4d40cb7072f7 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 @@ public class Recents extends SystemUI
private static SystemServicesProxy sSystemServicesProxy;
private static RecentsTaskLoader sTaskLoader;
+ private static RecentsConfiguration sConfiguration;
private Handler mHandler;
private RecentsImpl mImpl;
@@ -129,10 +130,15 @@ public class Recents extends SystemUI
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 0adad855628f..6c8bf0f96bff 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 class RecentsActivity extends Activity implements RecentsView.RecentsView
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
- RecentsConfiguration mConfig;
RecentsPackageMonitor mPackageMonitor;
long mLastTabKeyEventTime;
boolean mFinishedOnStartup;
@@ -174,7 +173,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
}
// 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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
* 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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// 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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// 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 class RecentsActivity extends Activity implements RecentsView.RecentsView
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 76b666fec1b6..aca816e7f068 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 @@ package com.android.systemui.recents;
*/
public class RecentsActivityLaunchState {
- public RecentsConfiguration mConfig;
-
public boolean launchedWithAltTab;
public boolean launchedWithNoRecentTasks;
public boolean launchedFromAppWithThumbnail;
@@ -41,10 +39,6 @@ public class RecentsActivityLaunchState {
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 @@ public class RecentsActivityLaunchState {
/** 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 d8f40238eaf4..e2f20fdefbb3 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 @@ import com.android.systemui.recents.misc.SystemServicesProxy;
* 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 class RecentsConfiguration {
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 @@ public class RecentsConfiguration {
/** 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 @@ public class RecentsConfiguration {
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 b05e1fe4adba..d02e2affaaa7 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.Handler;
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 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
boolean mCanReuseTaskStackViews = true;
// Task launching
- RecentsConfiguration mConfig;
Rect mSearchBarBounds = new Rect();
Rect mTaskStackBounds = new Rect();
Rect mLastTaskViewBounds = new Rect();
@@ -174,7 +177,6 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
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 class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
public void onConfigurationChanged() {
// Don't reuse task stack views if the configuration changes
mCanReuseTaskStackViews = false;
- mConfig.updateOnConfigurationChange();
+ Recents.getConfiguration().updateOnConfigurationChange();
}
/**
@@ -445,31 +447,32 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
* 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 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
*/
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 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
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 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
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 8e85bfc4aca6..ea6821fe1696 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 @@ public class ParametricCurve {
float[] xp;
float[] px;
+ float mLength;
CurveFunction mFn;
ParametricCurveFunction mScaleFn;
@@ -79,6 +80,7 @@ public class ParametricCurve {
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 @@ public class ParametricCurve {
}
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 8de8e15f9427..6fc4e20a9810 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 @@ import java.util.List;
* 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 @@ public class RecentsTaskLoadPlan {
}
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 @@ public class RecentsTaskLoadPlan {
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 @@ public class RecentsTaskLoadPlan {
", # thumbnails: " + opts.numVisibleTaskThumbnails +
", running task id: " + opts.runningTaskId);
+ RecentsConfiguration config = Recents.getConfiguration();
SystemServicesProxy ssp = Recents.getSystemServices();
Resources res = mContext.getResources();
@@ -175,9 +186,9 @@ public class RecentsTaskLoadPlan {
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 ea97b712dedb..71e39573537a 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 @@ class BackgroundTaskLoader implements Runnable {
}
}
} 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 @@ public class RecentsTaskLoader {
/** 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 @@ public class RecentsTaskLoader {
* 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 @@ public class RecentsTaskLoader {
}
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 0793180549a9..60051b83ad43 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 class Task {
}
}
+ 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 fb05c01e5de9..21b6bd88bcbf 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.Outline;
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 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
final float mMinAlpha = 0.25f;
public AnimateableViewBounds(TaskView source, int cornerRadius) {
- mConfig = RecentsConfiguration.getInstance();
mSourceView = source;
mCornerRadius = cornerRadius;
setClipBottom(getClipBottom());
@@ -64,7 +62,9 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
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 d5d07131701a..a8a8259e79e5 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 class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public void runAfterPause(Runnable r);
}
- RecentsConfiguration mConfig;
LayoutInflater mInflater;
ArrayList<TaskStack> mStacks;
@@ -117,7 +116,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
/** 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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
*/
@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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
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 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
*/
@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 bf045f91b693..f3a43905a823 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 @@ package com.android.systemui.recents.views;
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 @@ class RecentsViewTouchHandler {
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 e04699c10fdd..b7c1de36279f 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 @@ public class SwipeHelper {
private float mInitialTouchPos;
private boolean mDragging;
+ private float mSnapBackTranslationX;
private View mCurrView;
private boolean mCanCurrViewBeDimissed;
@@ -92,6 +93,10 @@ public class SwipeHelper {
mDensityScale = densityScale;
}
+ public void setSnapBackTranslationX(float translationX) {
+ mSnapBackTranslationX = translationX;
+ }
+
public void setPagingTouchSlop(float pagingTouchSlop) {
mPagingTouchSlop = pagingTouchSlop;
}
@@ -267,7 +272,7 @@ public class SwipeHelper {
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 5fbea00b00d5..c4e2d8a92c42 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.View;
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 @@ import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationS
public class SystemBarScrimViews {
Context mContext;
- RecentsConfiguration mConfig;
View mStatusBarScrimView;
View mNavBarScrimView;
@@ -48,7 +48,6 @@ public class SystemBarScrimViews {
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 @@ public class SystemBarScrimViews {
* 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 @@ public class SystemBarScrimViews {
* 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 757e695f283b..dfa36d81affb 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.Canvas;
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 class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public void onTaskStackFilterTriggered();
public void onTaskStackUnfilterTriggered();
}
- RecentsConfiguration mConfig;
TaskStack mStack;
TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
@@ -121,10 +120,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
/** 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
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 9a5d9bdf7902..c74c65456bdb 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.content.res.Resources;
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 @@ import java.util.HashMap;
/**
* 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 @@ public class TaskStackViewLayoutAlgorithm {
}
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 @@ public class TaskStackViewLayoutAlgorithm {
// 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 @@ public class TaskStackViewLayoutAlgorithm {
// 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 @@ public class TaskStackViewLayoutAlgorithm {
// 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 @@ public class TaskStackViewLayoutAlgorithm {
}
/**
- * 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 @@ public class TaskStackViewLayoutAlgorithm {
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 @@ public class TaskStackViewLayoutAlgorithm {
// 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 @@ public class TaskStackViewLayoutAlgorithm {
}
}
- // 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 @@ public class TaskStackViewLayoutAlgorithm {
* 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 @@ public class TaskStackViewLayoutAlgorithm {
/** 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 @@ public class TaskStackViewLayoutAlgorithm {
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 @@ public class TaskStackViewLayoutAlgorithm {
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 @@ public class TaskStackViewLayoutAlgorithm {
}
/**
+ * 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 @@ public class TaskStackViewLayoutAlgorithm {
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 3d3b13dd3290..15fcab4c67cd 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.AnimatorListenerAdapter;
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 @@ import com.android.systemui.recents.misc.Utilities;
/* 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 @@ public class TaskStackViewScroller {
TaskStackViewScrollerCallbacks mCb;
float mStackScrollP;
+ float mFlingDownScrollP;
+ int mFlingDownY;
OverScroller mScroller;
ObjectAnimator mScrollAnimator;
@@ -77,11 +84,6 @@ public class TaskStackViewScroller {
}
}
- /** 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 @@ public class TaskStackViewScroller {
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 @@ public class TaskStackViewScroller {
/**** 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 3a1a9876926f..08889c53161c 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 @@ import java.util.List;
/* 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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mVelocityTracker.clear();
}
}
- void initVelocityTrackerIfNotExists() {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- }
void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
@@ -115,175 +112,70 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
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,45 +184,50 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
}
}
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);
+ // 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);
}
- mLastMotionX = x;
- mLastMotionY = y;
- mLastP = layoutAlgorithm.sCurve.xToP(mLastMotionY, layoutAlgorithm.mStackRect);
- mTotalPMotion += Math.abs(deltaP);
+
+ mVelocityTracker.addMovement(ev);
+ 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);
+ }
+ 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 && (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();
+ 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());
@@ -338,24 +235,9 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mActivePointerId = INACTIVE_POINTER_ID;
mIsScrolling = false;
- mTotalPMotion = 0;
recycleVelocityTracker();
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: {
if (mScroller.isScrollOutOfBounds()) {
// Animate the scroll back into bounds
@@ -363,20 +245,19 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
}
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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
@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 0a5ee79b4cb8..aaacc6cf99b5 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 class TaskView extends FrameLayout implements Task.TaskCallbacks,
public void onTaskViewClipStateChanged(TaskView tv);
}
- RecentsConfiguration mConfig;
-
float mTaskProgress;
ObjectAnimator mTaskProgressAnimator;
float mMaxDimScale;
@@ -121,8 +119,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
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 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
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 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
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 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
* 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 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
/** 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 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
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 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
ctx.postAnimationTrigger.increment();
}
}
- startDelay = transitionEnterFromAppDelay;
} else if (launchState.launchedFromHome) {
// Animate the tasks up
@@ -381,7 +381,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
setScaleX(transform.scale);
setScaleY(transform.scale);
- if (!mConfig.fakeShadows) {
+ if (!config.fakeShadows) {
animate().translationZ(transform.translationZ);
}
animate()
@@ -400,7 +400,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
})
.start();
ctx.postAnimationTrigger.increment();
- startDelay = delay;
}
}
@@ -580,8 +579,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
/** 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 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
@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 ec50eb61b558..174ff33e9681 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 @@ import android.view.animation.Interpolator;
/* 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 class TaskViewTransform {
public TaskViewTransform(TaskViewTransform o) {
startDelay = o.startDelay;
+ translationX = o.translationX;
translationY = o.translationY;
translationZ = o.translationZ;
scale = o.scale;
@@ -52,9 +54,12 @@ public class TaskViewTransform {
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 class TaskViewTransform {
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 @@ public class TaskViewTransform {
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 @@ public class TaskViewTransform {
.start();
} else {
// Set the changed properties
+ if (hasTranslationXChangedFrom(v.getTranslationX())) {
+ v.setTranslationX(translationX);
+ }
if (hasTranslationYChangedFrom(v.getTranslationY())) {
v.setTranslationY(translationY);
}
@@ -147,7 +161,8 @@ public class TaskViewTransform {
@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 cfdb01e177ea..f3658eb3c1c9 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.Context;
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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
* 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
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/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
index 1cf7a70f7e4d..673a30b30305 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -125,10 +125,6 @@ public class VolumeDialogController {
return mAudio;
}
- public ZenModeConfig getZenModeConfig() {
- return mNoMan.getZenModeConfig();
- }
-
public void dismiss() {
mCallbacks.onDismissRequested(Events.DISMISS_REASON_VOLUME_CONTROLLER);
}
@@ -348,7 +344,6 @@ public class VolumeDialogController {
updateRingerModeExternalW(mAudio.getRingerMode());
updateZenModeW();
updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
- updateZenModeConfigW();
mCallbacks.onStateChanged(mState);
}
@@ -401,13 +396,6 @@ public class VolumeDialogController {
return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
}
- private boolean updateZenModeConfigW() {
- final ZenModeConfig zenModeConfig = getZenModeConfig();
- if (Objects.equals(mState.zenModeConfig, zenModeConfig)) return false;
- mState.zenModeConfig = zenModeConfig;
- return true;
- }
-
private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) {
if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
mState.effectsSuppressor = effectsSuppressor;
@@ -748,9 +736,6 @@ public class VolumeDialogController {
if (ZEN_MODE_URI.equals(uri)) {
changed = updateZenModeW();
}
- if (ZEN_MODE_CONFIG_URI.equals(uri)) {
- changed = updateZenModeConfigW();
- }
if (changed) {
mCallbacks.onStateChanged(mState);
}
@@ -947,7 +932,6 @@ public class VolumeDialogController {
public int zenMode;
public ComponentName effectsSuppressor;
public String effectsSuppressorName;
- public ZenModeConfig zenModeConfig;
public int activeStream = NO_ACTIVE_STREAM;
public State copy() {
@@ -960,7 +944,6 @@ public class VolumeDialogController {
rt.zenMode = zenMode;
if (effectsSuppressor != null) rt.effectsSuppressor = effectsSuppressor.clone();
rt.effectsSuppressorName = effectsSuppressorName;
- if (zenModeConfig != null) rt.zenModeConfig = zenModeConfig.copy();
rt.activeStream = activeStream;
return rt;
}
@@ -989,7 +972,6 @@ public class VolumeDialogController {
sep(sb, indent); sb.append("zenMode:").append(zenMode);
sep(sb, indent); sb.append("effectsSuppressor:").append(effectsSuppressor);
sep(sb, indent); sb.append("effectsSuppressorName:").append(effectsSuppressorName);
- sep(sb, indent); sb.append("zenModeConfig:").append(zenModeConfig);
sep(sb, indent); sb.append("activeStream:").append(activeStream);
if (indent > 0) sep(sb, indent);
return sb.append('}').toString();
@@ -1005,11 +987,6 @@ public class VolumeDialogController {
sb.append(',');
}
}
-
- public Condition getManualExitCondition() {
- return zenModeConfig != null && zenModeConfig.manualRule != null
- ? zenModeConfig.manualRule.condition : null;
- }
}
public interface Callbacks {
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 4f5fff40d908..ed6fc0057c09 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -95,6 +95,7 @@ class AlarmManagerService extends SystemService {
static final boolean DEBUG_VALIDATE = localLOGV || false;
static final boolean DEBUG_ALARM_CLOCK = localLOGV || false;
static final boolean RECORD_ALARMS_IN_HISTORY = true;
+ static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
static final int ALARM_EVENT = 1;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@@ -144,6 +145,16 @@ class AlarmManagerService extends SystemService {
*/
final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray();
+ final static class IdleDispatchEntry {
+ int uid;
+ String pkg;
+ String tag;
+ String op;
+ long elapsedRealtime;
+ long argRealtime;
+ }
+ final ArrayList<IdleDispatchEntry> mAllowWhileIdleDispatches = new ArrayList();
+
/**
* Broadcast options to use for FLAG_ALLOW_WHILE_IDLE.
*/
@@ -700,6 +711,14 @@ class AlarmManagerService extends SystemService {
}
void restorePendingWhileIdleAlarmsLocked() {
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ IdleDispatchEntry ent = new IdleDispatchEntry();
+ ent.uid = 0;
+ ent.pkg = "FINISH IDLE";
+ ent.elapsedRealtime = SystemClock.elapsedRealtime();
+ mAllowWhileIdleDispatches.add(ent);
+ }
+
// Bring pending alarms back into the main list.
if (mPendingWhileIdleAlarms.size() > 0) {
ArrayList<Alarm> alarms = mPendingWhileIdleAlarms;
@@ -1006,6 +1025,19 @@ class AlarmManagerService extends SystemService {
}
}
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ if ((a.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+ IdleDispatchEntry ent = new IdleDispatchEntry();
+ ent.uid = a.uid;
+ ent.pkg = a.operation.getCreatorPackage();
+ ent.tag = a.operation.getTag("");
+ ent.op = "SET";
+ ent.elapsedRealtime = SystemClock.elapsedRealtime();
+ ent.argRealtime = a.whenElapsed;
+ mAllowWhileIdleDispatches.add(ent);
+ }
+ }
+
int whichBatch = ((a.flags&AlarmManager.FLAG_STANDALONE) != 0)
? -1 : attemptCoalesceLocked(a.whenElapsed, a.maxWhenElapsed);
if (whichBatch < 0) {
@@ -1028,6 +1060,15 @@ class AlarmManagerService extends SystemService {
boolean needRebatch = false;
if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ if (mPendingIdleUntil == null) {
+ IdleDispatchEntry ent = new IdleDispatchEntry();
+ ent.uid = 0;
+ ent.pkg = "START IDLE";
+ ent.elapsedRealtime = SystemClock.elapsedRealtime();
+ mAllowWhileIdleDispatches.add(ent);
+ }
+ }
mPendingIdleUntil = a;
mConstants.updateAllowWhileIdleMinTimeLocked();
needRebatch = true;
@@ -1400,6 +1441,32 @@ class AlarmManagerService extends SystemService {
}
}
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ pw.println();
+ pw.println(" Allow while idle dispatches:");
+ for (int i = 0; i < mAllowWhileIdleDispatches.size(); i++) {
+ IdleDispatchEntry ent = mAllowWhileIdleDispatches.get(i);
+ pw.print(" ");
+ TimeUtils.formatDuration(ent.elapsedRealtime, nowELAPSED, pw);
+ pw.print(": ");
+ UserHandle.formatUid(pw, ent.uid);
+ pw.print(":");
+ pw.println(ent.pkg);
+ if (ent.op != null) {
+ pw.print(" ");
+ pw.print(ent.op);
+ pw.print(" / ");
+ pw.print(ent.tag);
+ if (ent.argRealtime != 0) {
+ pw.print(" (");
+ TimeUtils.formatDuration(ent.argRealtime, nowELAPSED, pw);
+ pw.print(")");
+ }
+ pw.println();
+ }
+ }
+ }
+
if (WAKEUP_STATS) {
pw.println();
pw.println(" Recent Wakeup History:");
@@ -1862,6 +1929,16 @@ class AlarmManagerService extends SystemService {
if (alarm.maxWhenElapsed < minTime) {
alarm.maxWhenElapsed = minTime;
}
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ IdleDispatchEntry ent = new IdleDispatchEntry();
+ ent.uid = alarm.uid;
+ ent.pkg = alarm.operation.getCreatorPackage();
+ ent.tag = alarm.operation.getTag("");
+ ent.op = "RESCHEDULE";
+ ent.elapsedRealtime = nowELAPSED;
+ ent.argRealtime = lastTime;
+ mAllowWhileIdleDispatches.add(ent);
+ }
setImplLocked(alarm, true, false);
continue;
}
@@ -2130,6 +2207,15 @@ class AlarmManagerService extends SystemService {
if (allowWhileIdle) {
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
mLastAllowWhileIdleDispatch.put(alarm.uid, nowELAPSED);
+ if (RECORD_DEVICE_IDLE_ALARMS) {
+ IdleDispatchEntry ent = new IdleDispatchEntry();
+ ent.uid = alarm.uid;
+ ent.pkg = alarm.operation.getCreatorPackage();
+ ent.tag = alarm.operation.getTag("");
+ ent.op = "DELIVER";
+ ent.elapsedRealtime = nowELAPSED;
+ mAllowWhileIdleDispatches.add(ent);
+ }
}
final BroadcastStats bs = inflight.mBroadcastStats;
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 2eeaec96b581..61fe62fb88a4 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -19,6 +19,8 @@ package com.android.server;
import android.database.ContentObserver;
import android.os.BatteryStats;
+import android.os.ResultReceiver;
+import android.os.ShellCommand;
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
@@ -96,7 +98,6 @@ public final class BatteryService extends SystemService {
// discharge stats before the device dies.
private int mCriticalBatteryLevel;
- private static final int DUMP_MAX_LENGTH = 24 * 1024;
private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "--unplugged" };
private static final String DUMPSYS_DATA_PATH = "/data/system/";
@@ -106,6 +107,7 @@ public final class BatteryService extends SystemService {
private final Context mContext;
private final IBatteryStats mBatteryStats;
+ BinderService mBinderService;
private final Handler mHandler;
private final Object mLock = new Object();
@@ -162,7 +164,18 @@ public final class BatteryService extends SystemService {
// watch for invalid charger messages if the invalid_charger switch exists
if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
- mInvalidChargerObserver.startObserving(
+ UEventObserver invalidChargerObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEvent event) {
+ final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
+ synchronized (mLock) {
+ if (mInvalidCharger != invalidCharger) {
+ mInvalidCharger = invalidCharger;
+ }
+ }
+ }
+ };
+ invalidChargerObserver.startObserving(
"DEVPATH=/devices/virtual/switch/invalid_charger");
}
}
@@ -178,7 +191,8 @@ public final class BatteryService extends SystemService {
// Should never happen.
}
- publishBinderService("battery", new BinderService());
+ mBinderService = new BinderService();
+ publishBinderService("battery", mBinderService);
publishLocalService(BatteryManagerInternal.class, new LocalService());
}
@@ -593,7 +607,6 @@ public final class BatteryService extends SystemService {
} catch (NumberFormatException e) {
Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
durationThresholdString + " or " + dischargeThresholdString);
- return;
}
}
}
@@ -616,27 +629,40 @@ public final class BatteryService extends SystemService {
}
}
- private void dumpInternal(PrintWriter pw, String[] args) {
- synchronized (mLock) {
- if (args == null || args.length == 0 || "-a".equals(args[0])) {
- pw.println("Current Battery Service state:");
- if (mUpdatesStopped) {
- pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
- }
- pw.println(" AC powered: " + mBatteryProps.chargerAcOnline);
- pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline);
- pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline);
- pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent);
- pw.println(" status: " + mBatteryProps.batteryStatus);
- pw.println(" health: " + mBatteryProps.batteryHealth);
- pw.println(" present: " + mBatteryProps.batteryPresent);
- pw.println(" level: " + mBatteryProps.batteryLevel);
- pw.println(" scale: " + BATTERY_SCALE);
- pw.println(" voltage: " + mBatteryProps.batteryVoltage);
- pw.println(" temperature: " + mBatteryProps.batteryTemperature);
- pw.println(" technology: " + mBatteryProps.batteryTechnology);
+ class Shell extends ShellCommand {
+ @Override
+ public int onCommand(String cmd) {
+ return onShellCommand(this, cmd);
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ dumpHelp(pw);
+ }
+ }
+
+ static void dumpHelp(PrintWriter pw) {
+ pw.println("Battery service (battery) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" set [ac|usb|wireless|status|level|invalid] <value>");
+ pw.println(" Force a battery property value, freezing battery state.");
+ pw.println(" unplug");
+ pw.println(" Force battery unplugged, freezing battery state.");
+ pw.println(" reset");
+ pw.println(" Unfreeze battery state, returning to current hardware values.");
+ }
- } else if ("unplug".equals(args[0])) {
+ int onShellCommand(Shell shell, String cmd) {
+ if (cmd == null) {
+ return shell.handleDefaultCommands(cmd);
+ }
+ PrintWriter pw = shell.getOutPrintWriter();
+ switch (cmd) {
+ case "unplug": {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
if (!mUpdatesStopped) {
mLastBatteryProps.set(mBatteryProps);
}
@@ -650,30 +676,50 @@ public final class BatteryService extends SystemService {
} finally {
Binder.restoreCallingIdentity(ident);
}
+ } break;
+ case "set": {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
+ final String key = shell.getNextArg();
+ if (key == null) {
+ pw.println("No property specified");
+ return -1;
- } else if (args.length == 3 && "set".equals(args[0])) {
- String key = args[1];
- String value = args[2];
+ }
+ final String value = shell.getNextArg();
+ if (value == null) {
+ pw.println("No value specified");
+ return -1;
+
+ }
try {
if (!mUpdatesStopped) {
mLastBatteryProps.set(mBatteryProps);
}
boolean update = true;
- if ("ac".equals(key)) {
- mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
- } else if ("usb".equals(key)) {
- mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
- } else if ("wireless".equals(key)) {
- mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;
- } else if ("status".equals(key)) {
- mBatteryProps.batteryStatus = Integer.parseInt(value);
- } else if ("level".equals(key)) {
- mBatteryProps.batteryLevel = Integer.parseInt(value);
- } else if ("invalid".equals(key)) {
- mInvalidCharger = Integer.parseInt(value);
- } else {
- pw.println("Unknown set option: " + key);
- update = false;
+ switch (key) {
+ case "ac":
+ mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
+ break;
+ case "usb":
+ mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
+ break;
+ case "wireless":
+ mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;
+ break;
+ case "status":
+ mBatteryProps.batteryStatus = Integer.parseInt(value);
+ break;
+ case "level":
+ mBatteryProps.batteryLevel = Integer.parseInt(value);
+ break;
+ case "invalid":
+ mInvalidCharger = Integer.parseInt(value);
+ break;
+ default:
+ pw.println("Unknown set option: " + key);
+ update = false;
+ break;
}
if (update) {
long ident = Binder.clearCallingIdentity();
@@ -686,9 +732,12 @@ public final class BatteryService extends SystemService {
}
} catch (NumberFormatException ex) {
pw.println("Bad value: " + value);
+ return -1;
}
-
- } else if (args.length == 1 && "reset".equals(args[0])) {
+ } break;
+ case "reset": {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
long ident = Binder.clearCallingIdentity();
try {
if (mUpdatesStopped) {
@@ -699,26 +748,38 @@ public final class BatteryService extends SystemService {
} finally {
Binder.restoreCallingIdentity(ident);
}
- } else {
- pw.println("Dump current battery state, or:");
- pw.println(" set [ac|usb|wireless|status|level|invalid] <value>");
- pw.println(" unplug");
- pw.println(" reset");
- }
+ } break;
+ default:
+ return shell.handleDefaultCommands(cmd);
}
+ return 0;
}
- private final UEventObserver mInvalidChargerObserver = new UEventObserver() {
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
- synchronized (mLock) {
- if (mInvalidCharger != invalidCharger) {
- mInvalidCharger = invalidCharger;
+ private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
+ synchronized (mLock) {
+ if (args == null || args.length == 0 || "-a".equals(args[0])) {
+ pw.println("Current Battery Service state:");
+ if (mUpdatesStopped) {
+ pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
}
+ pw.println(" AC powered: " + mBatteryProps.chargerAcOnline);
+ pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline);
+ pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline);
+ pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent);
+ pw.println(" status: " + mBatteryProps.batteryStatus);
+ pw.println(" health: " + mBatteryProps.batteryHealth);
+ pw.println(" present: " + mBatteryProps.batteryPresent);
+ pw.println(" level: " + mBatteryProps.batteryLevel);
+ pw.println(" scale: " + BATTERY_SCALE);
+ pw.println(" voltage: " + mBatteryProps.batteryVoltage);
+ pw.println(" temperature: " + mBatteryProps.batteryTemperature);
+ pw.println(" technology: " + mBatteryProps.batteryTechnology);
+ } else {
+ Shell shell = new Shell();
+ shell.exec(mBinderService, null, fd, null, args, new ResultReceiver(null));
}
}
- };
+ }
private final class Led {
private final Light mBatteryLight;
@@ -776,8 +837,7 @@ public final class BatteryService extends SystemService {
}
private final class BatteryListener extends IBatteryPropertiesListener.Stub {
- @Override
- public void batteryPropertiesChanged(BatteryProperties props) {
+ @Override public void batteryPropertiesChanged(BatteryProperties props) {
final long identity = Binder.clearCallingIdentity();
try {
BatteryService.this.update(props);
@@ -788,8 +848,7 @@ public final class BatteryService extends SystemService {
}
private final class BinderService extends Binder {
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -799,7 +858,12 @@ public final class BatteryService extends SystemService {
return;
}
- dumpInternal(pw, args);
+ dumpInternal(fd, pw, args);
+ }
+
+ @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ (new Shell()).exec(this, in, out, err, args, resultReceiver);
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 6b346123e94a..1ff13b293add 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 @@ public class AccountManagerService
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/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dfe5751267fd..d5ee5e846277 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -69,6 +69,7 @@ import android.graphics.Rect;
import android.os.BatteryStats;
import android.os.PersistableBundle;
import android.os.PowerManager;
+import android.os.ResultReceiver;
import android.os.Trace;
import android.os.TransactionTooLargeException;
import android.os.WorkSource;
@@ -13083,6 +13084,13 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ (new ActivityManagerShellCommand(this, false)).exec(
+ this, in, out, err, args, resultReceiver);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (checkCallingPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -13119,34 +13127,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
dumpClient = true;
} else if ("-h".equals(opt)) {
- pw.println("Activity manager dump options:");
- pw.println(" [-a] [-c] [-p package] [-h] [cmd] ...");
- pw.println(" cmd may be one of:");
- pw.println(" a[ctivities]: activity stack state");
- pw.println(" r[recents]: recent activities state");
- pw.println(" b[roadcasts] [PACKAGE_NAME] [history [-s]]: broadcast state");
- pw.println(" i[ntents] [PACKAGE_NAME]: pending intent state");
- pw.println(" p[rocesses] [PACKAGE_NAME]: process state");
- pw.println(" o[om]: out of memory management");
- pw.println(" perm[issions]: URI permission grant state");
- pw.println(" prov[iders] [COMP_SPEC ...]: content provider state");
- pw.println(" provider [COMP_SPEC]: provider client-side state");
- pw.println(" s[ervices] [COMP_SPEC ...]: service state");
- pw.println(" as[sociations]: tracked app associations");
- pw.println(" service [COMP_SPEC]: service client-side state");
- pw.println(" package [PACKAGE_NAME]: all state related to given package");
- pw.println(" all: dump all activities");
- pw.println(" top: dump the top activity");
- pw.println(" write: write all pending state to storage");
- pw.println(" track-associations: enable association tracking");
- pw.println(" untrack-associations: disable and clear association tracking");
- pw.println(" cmd may also be a COMP_SPEC to dump activities.");
- pw.println(" COMP_SPEC may be a component name (com.foo/.myApp),");
- pw.println(" a partial substring in a component name, a");
- pw.println(" hex object identifier.");
- pw.println(" -a: include all available server state.");
- pw.println(" -c: include client state.");
- pw.println(" -p: limit output to given package.");
+ ActivityManagerShellCommand.dumpHelp(pw, true);
return;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
@@ -13283,36 +13264,15 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (this) {
mServices.dumpServicesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
}
- } else if ("write".equals(cmd)) {
- mTaskPersister.flush();
- pw.println("All tasks persisted.");
- return;
- } else if ("track-associations".equals(cmd)) {
- synchronized (this) {
- if (!mTrackingAssociations) {
- mTrackingAssociations = true;
- pw.println("Association tracking started.");
- } else {
- pw.println("Association tracking already enabled.");
- }
- }
- return;
- } else if ("untrack-associations".equals(cmd)) {
- synchronized (this) {
- if (mTrackingAssociations) {
- mTrackingAssociations = false;
- mAssociations.clear();
- pw.println("Association tracking stopped.");
- } else {
- pw.println("Association tracking not running.");
- }
- }
- return;
} else {
// Dumping a single activity?
if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll)) {
- pw.println("Bad activity command, or no activities match: " + cmd);
- pw.println("Use -h for help.");
+ ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
+ int res = shell.exec(this, null, fd, null, args, new ResultReceiver(null));
+ if (res < 0) {
+ pw.println("Bad activity command, or no activities match: " + cmd);
+ pw.println("Use -h for help.");
+ }
}
}
if (!more) {
@@ -13563,17 +13523,34 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (mActiveUids.size() > 0) {
- if (needSep) {
- pw.println();
+ boolean printed = false;
+ int whichAppId = -1;
+ if (dumpPackage != null) {
+ try {
+ ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
+ dumpPackage, 0);
+ whichAppId = UserHandle.getAppId(info.uid);
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
}
- pw.println(" UID states:");
for (int i=0; i<mActiveUids.size(); i++) {
UidRecord uidRec = mActiveUids.valueAt(i);
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
+ continue;
+ }
+ if (!printed) {
+ printed = true;
+ if (needSep) {
+ pw.println();
+ }
+ pw.println(" UID states:");
+ needSep = true;
+ printedAnything = true;
+ }
pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
pw.print(": "); pw.println(uidRec);
}
- needSep = true;
- printedAnything = true;
}
if (mLruProcesses.size() > 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
new file mode 100644
index 000000000000..d1e7e85b6d51
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -0,0 +1,211 @@
+/*
+ * 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.server.am;
+
+import android.app.IActivityManager;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+
+class ActivityManagerShellCommand extends ShellCommand {
+ // IPC interface to activity manager -- don't need to do additional security checks.
+ final IActivityManager mInterface;
+
+ // Internal service impl -- must perform security checks before touching.
+ final ActivityManagerService mInternal;
+
+ final boolean mDumping;
+
+ ActivityManagerShellCommand(ActivityManagerService service, boolean dumping) {
+ mInterface = service;
+ mInternal = service;
+ mDumping = dumping;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ PrintWriter pw = getOutPrintWriter();
+ try {
+ switch (cmd) {
+ case "force-stop":
+ return runForceStop(pw);
+ case "kill":
+ return runKill(pw);
+ case "kill-all":
+ return runKillAll(pw);
+ case "write":
+ return runWrite(pw);
+ case "track-associations":
+ return runTrackAssociations(pw);
+ case "untrack-associations":
+ return runUntrackAssociations(pw);
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ int runForceStop(PrintWriter pw) throws RemoteException {
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = parseUserArg(getNextArgRequired());
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+ mInterface.forceStopPackage(getNextArgRequired(), userId);
+ return 0;
+ }
+
+ int runKill(PrintWriter pw) throws RemoteException {
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt=getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = parseUserArg(getNextArgRequired());
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+ mInterface.killBackgroundProcesses(getNextArgRequired(), userId);
+ return 0;
+ }
+
+ int runKillAll(PrintWriter pw) throws RemoteException {
+ mInterface.killAllBackgroundProcesses();
+ return 0;
+ }
+
+ int runWrite(PrintWriter pw) {
+ mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "registerUidObserver()");
+ mInternal.mTaskPersister.flush();
+ pw.println("All tasks persisted.");
+ return 0;
+ }
+
+ int runTrackAssociations(PrintWriter pw) {
+ mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "registerUidObserver()");
+ synchronized (mInternal) {
+ if (!mInternal.mTrackingAssociations) {
+ mInternal.mTrackingAssociations = true;
+ pw.println("Association tracking started.");
+ } else {
+ pw.println("Association tracking already enabled.");
+ }
+ }
+ return 0;
+ }
+
+ int runUntrackAssociations(PrintWriter pw) {
+ mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "registerUidObserver()");
+ synchronized (mInternal) {
+ if (mInternal.mTrackingAssociations) {
+ mInternal.mTrackingAssociations = false;
+ mInternal.mAssociations.clear();
+ pw.println("Association tracking stopped.");
+ } else {
+ pw.println("Association tracking not running.");
+ }
+ }
+ return 0;
+ }
+
+ int parseUserArg(String arg) {
+ int userId;
+ if ("all".equals(arg)) {
+ userId = UserHandle.USER_ALL;
+ } else if ("current".equals(arg) || "cur".equals(arg)) {
+ userId = UserHandle.USER_CURRENT;
+ } else {
+ try {
+ userId = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Bad user number: " + arg);
+ }
+ }
+ return userId;
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ dumpHelp(pw, mDumping);
+ }
+
+ static void dumpHelp(PrintWriter pw, boolean dumping) {
+ if (dumping) {
+ pw.println("Activity manager dump options:");
+ pw.println(" [-a] [-c] [-p PACKAGE] [-h] [WHAT] ...");
+ pw.println(" WHAT may be one of:");
+ pw.println(" a[ctivities]: activity stack state");
+ pw.println(" r[recents]: recent activities state");
+ pw.println(" b[roadcasts] [PACKAGE_NAME] [history [-s]]: broadcast state");
+ pw.println(" i[ntents] [PACKAGE_NAME]: pending intent state");
+ pw.println(" p[rocesses] [PACKAGE_NAME]: process state");
+ pw.println(" o[om]: out of memory management");
+ pw.println(" perm[issions]: URI permission grant state");
+ pw.println(" prov[iders] [COMP_SPEC ...]: content provider state");
+ pw.println(" provider [COMP_SPEC]: provider client-side state");
+ pw.println(" s[ervices] [COMP_SPEC ...]: service state");
+ pw.println(" as[sociations]: tracked app associations");
+ pw.println(" service [COMP_SPEC]: service client-side state");
+ pw.println(" package [PACKAGE_NAME]: all state related to given package");
+ pw.println(" all: dump all activities");
+ pw.println(" top: dump the top activity");
+ pw.println(" WHAT may also be a COMP_SPEC to dump activities.");
+ pw.println(" COMP_SPEC may be a component name (com.foo/.myApp),");
+ pw.println(" a partial substring in a component name, a");
+ pw.println(" hex object identifier.");
+ pw.println(" -a: include all available server state.");
+ pw.println(" -c: include client state.");
+ pw.println(" -p: limit output to given package.");
+ } else {
+ pw.println("Activity manager (activity) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" force-stop [--user <USER_ID> | all | current] <PACKAGE>");
+ pw.println(" Complete stop the given application package.");
+ pw.println(" kill [--user <USER_ID> | all | current] <PACKAGE>");
+ pw.println(" Kill all processes associated with the given application.");
+ pw.println(" kill-all");
+ pw.println(" Kill all processes that are safe to kill (cached, etc)");
+ pw.println(" write");
+ pw.println(" Write all pending state to storage.");
+ pw.println(" track-associations");
+ pw.println(" Enable association tracking.");
+ pw.println(" untrack-associations");
+ pw.println(" Disable and clear association tracking.");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 4671cb0a94e3..8061a9271cb4 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.EventLog;
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 @@ final class ActivityRecord {
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 1cd375800afd..2104830c6666 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -530,26 +530,36 @@ final class ActivityStack {
* @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);
-
- // TODO(multi-display): Needs to also work if focus is moving to the non-home display.
- if (isOnHomeDisplay()) {
- mStackSupervisor.setFocusStack(reason, lastFocusStack);
- }
- if (task != null) {
- insertTaskAtTop(task, null);
- } else {
- task = topTask();
- }
- if (task != null) {
- mWindowManager.moveTaskToTop(task.taskId);
+ if (!isAttached()) {
+ return;
+ }
+
+ 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--;
}
}
+
+ 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);
+ }
}
final boolean isAttached() {
@@ -1304,12 +1314,7 @@ final class ActivityStack {
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 @@ final class ActivityStack {
// 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 @@ final class ActivityStack {
}
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 @@ final class ActivityStack {
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 @@ final class ActivityStack {
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 @@ final class ActivityStack {
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 @@ final class ActivityStack {
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 3bf87d870589..1cd71a1cb61a 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 @@ public final class ActivityStackSupervisor implements DisplayListener {
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 @@ public final class ActivityStackSupervisor implements DisplayListener {
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 @@ public final class ActivityStackSupervisor implements DisplayListener {
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 @@ public final class ActivityStackSupervisor implements DisplayListener {
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 @@ public final class ActivityStackSupervisor implements DisplayListener {
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 @@ public final class ActivityStackSupervisor implements DisplayListener {
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 @@ public final class ActivityStackSupervisor implements DisplayListener {
// 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 @@ public final class ActivityStackSupervisor implements DisplayListener {
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 @@ public final class ActivityStackSupervisor implements DisplayListener {
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 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
// Do targetStack first.
boolean result = false;
- if (isFrontStack(targetStack)) {
+ if (isFocusedStack(targetStack)) {
result = targetStack.resumeTopActivityLocked(target, targetOptions);
}
@@ -2817,7 +2818,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
// Already started above.
continue;
}
- if (isFrontStack(stack)) {
+ if (isFocusedStack(stack)) {
stack.resumeTopActivityLocked(null);
}
}
@@ -3243,7 +3244,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
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 @@ public final class ActivityStackSupervisor implements DisplayListener {
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 @@ public final class ActivityStackSupervisor implements DisplayListener {
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 @@ public final class ActivityStackSupervisor implements DisplayListener {
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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c4b57f1945a1..246ff081177d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -109,6 +109,7 @@ import android.view.accessibility.AccessibilityManager;
import com.android.internal.util.XmlUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
import com.android.server.pm.UserManagerService;
import org.xmlpull.v1.XmlPullParserException;
@@ -564,6 +565,27 @@ public class AudioService extends IAudioService.Stub {
return "card=" + card + ";device=" + device + ";";
}
+ public static final class Lifecycle extends SystemService {
+ private AudioService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ mService = new AudioService(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.AUDIO_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mService.systemReady();
+ }
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
diff --git a/services/core/java/com/android/server/input/InputWindowHandle.java b/services/core/java/com/android/server/input/InputWindowHandle.java
index 9149fcc63b0d..207c05d72bd1 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 @@ public final class InputWindowHandle {
}
@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/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 19d853851820..9441d88f2504 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -17,6 +17,7 @@
package com.android.server.notification;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
@@ -29,6 +30,7 @@ import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -79,7 +81,7 @@ public class ConditionProviders extends ManagedServices {
final Config c = new Config();
c.caption = "condition provider";
c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
- c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
+ c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES;
c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
c.clientLabel = R.string.condition_provider_service_binding_label;
@@ -280,6 +282,26 @@ public class ConditionProviders extends ManagedServices {
}
}
+ @Override
+ protected ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
+ int userId) {
+ final ContentResolver cr = mContext.getContentResolver();
+ String settingValue = Settings.Secure.getStringForUser(
+ cr,
+ settingName,
+ userId);
+ if (TextUtils.isEmpty(settingValue))
+ return null;
+ String[] packages = settingValue.split(ENABLED_SERVICES_SEPARATOR);
+ ArraySet<ComponentName> result = new ArraySet<>(packages.length);
+ for (int i = 0; i < packages.length; i++) {
+ if (!TextUtils.isEmpty(packages[i])) {
+ result.addAll(queryPackageForServices(packages[i], userId));
+ }
+ }
+ return result;
+ }
+
public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) {
synchronized (mMutex) {
final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index a54a61a1c68a..d2a264d775bf 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -70,7 +70,7 @@ abstract public class ManagedServices {
protected final String TAG = getClass().getSimpleName();
protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String ENABLED_SERVICES_SEPARATOR = ":";
+ protected static final String ENABLED_SERVICES_SEPARATOR = ":";
protected final Context mContext;
protected final Object mMutex;
@@ -279,7 +279,8 @@ abstract public class ManagedServices {
}
- private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName, int userId) {
+ protected ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
+ int userId) {
final ContentResolver cr = mContext.getContentResolver();
String settingValue = Settings.Secure.getStringForUser(
cr,
@@ -319,7 +320,6 @@ abstract public class ManagedServices {
userId);
}
-
/**
* Remove access for any services that no longer exist.
*/
@@ -332,18 +332,15 @@ abstract public class ManagedServices {
rebuildRestoredPackages();
}
- private void updateSettingsAccordingToInstalledServices(int userId) {
- boolean restoredChanged = false;
- boolean currentChanged = false;
- Set<ComponentName> restored =
- loadComponentNamesFromSetting(restoredSettingName(mConfig), userId);
- Set<ComponentName> current =
- loadComponentNamesFromSetting(mConfig.secureSettingName, userId);
+ protected Set<ComponentName> queryPackageForServices(String packageName, int userId) {
Set<ComponentName> installed = new ArraySet<>();
-
final PackageManager pm = mContext.getPackageManager();
+ Intent queryIntent = new Intent(mConfig.serviceInterface);
+ if (!TextUtils.isEmpty(packageName)) {
+ queryIntent.setPackage(packageName);
+ }
List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
- new Intent(mConfig.serviceInterface),
+ queryIntent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
userId);
if (DEBUG)
@@ -363,6 +360,18 @@ abstract public class ManagedServices {
}
installed.add(component);
}
+ return installed;
+ }
+
+ private void updateSettingsAccordingToInstalledServices(int userId) {
+ boolean restoredChanged = false;
+ boolean currentChanged = false;
+ Set<ComponentName> restored =
+ loadComponentNamesFromSetting(restoredSettingName(mConfig), userId);
+ Set<ComponentName> current =
+ loadComponentNamesFromSetting(mConfig.secureSettingName, userId);
+ // Load all services for all packages.
+ Set<ComponentName> installed = queryPackageForServices(null, userId);
ArraySet<ComponentName> retained = new ArraySet<>();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 361bbf906d7e..b84811fa8c4d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1579,12 +1579,6 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public boolean setZenModeConfig(ZenModeConfig config, String reason) {
- checkCallerIsSystem();
- return mZenModeHelper.setConfig(config, reason);
- }
-
- @Override
public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
final long identity = Binder.clearCallingIdentity();
@@ -1669,12 +1663,6 @@ public class NotificationManagerService extends SystemService {
});
}
- @Override
- public void requestZenModeConditions(IConditionListener callback, int relevance) {
- enforceSystemOrSystemUIOrVolume("INotificationManager.requestZenModeConditions");
- mZenModeHelper.requestZenModeConditions(callback, relevance);
- }
-
private void enforceSystemOrSystemUIOrVolume(String message) {
if (mAudioManagerInternal != null) {
final int vcuid = mAudioManagerInternal.getVolumeControllerUid();
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 76c64432e54b..a1f8c41619d0 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -121,8 +121,11 @@ public class ZenModeHelper {
public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) {
- return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle, extras,
- validator, contactsTimeoutMs, timeoutAffinity);
+ synchronized (mConfig) {
+ return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle,
+ extras,
+ validator, contactsTimeoutMs, timeoutAffinity);
+ }
}
public boolean isCall(NotificationRecord record) {
@@ -130,7 +133,9 @@ public class ZenModeHelper {
}
public boolean shouldIntercept(NotificationRecord record) {
- return mFiltering.shouldIntercept(mZenMode, mConfig, record);
+ synchronized (mConfig) {
+ return mFiltering.shouldIntercept(mZenMode, mConfig, record);
+ }
}
public void addCallback(Callback callback) {
@@ -175,10 +180,6 @@ public class ZenModeHelper {
mConfigs.remove(user);
}
- public void requestZenModeConditions(IConditionListener callback, int relevance) {
- mConditions.requestConditions(callback, relevance);
- }
-
public int getZenModeListenerInterruptionFilter() {
return NotificationManager.zenModeToInterruptionFilter(mZenMode);
}
@@ -203,18 +204,23 @@ public class ZenModeHelper {
public List<AutomaticZenRule> getAutomaticZenRules() {
List<AutomaticZenRule> rules = new ArrayList<>();
- if (mConfig == null) return rules;
- for(ZenRule rule : mConfig.automaticRules.values()) {
- if (canManageAutomaticZenRule(rule)) {
- rules.add(createAutomaticZenRule(rule));
+ synchronized (mConfig) {
+ if (mConfig == null) return rules;
+ for (ZenRule rule : mConfig.automaticRules.values()) {
+ if (canManageAutomaticZenRule(rule)) {
+ rules.add(createAutomaticZenRule(rule));
+ }
}
}
return rules;
}
public AutomaticZenRule getAutomaticZenRule(String id) {
- if (mConfig == null) return null;
- ZenRule rule = mConfig.automaticRules.get(id);
+ ZenRule rule;
+ synchronized (mConfig) {
+ if (mConfig == null) return null;
+ rule = mConfig.automaticRules.get(id);
+ }
if (rule == null) return null;
if (canManageAutomaticZenRule(rule)) {
return createAutomaticZenRule(rule);
@@ -223,14 +229,18 @@ public class ZenModeHelper {
}
public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
- if (mConfig == null) return null;
- if (DEBUG) {
- Log.d(TAG, "addAutomaticZenRule zenRule= " + automaticZenRule + " reason=" +reason);
- }
- if (!TextUtils.isEmpty(automaticZenRule.getId())) {
- throw new IllegalArgumentException("Rule already exists");
+ ZenModeConfig newConfig;
+ synchronized (mConfig) {
+ if (mConfig == null) return null;
+ if (DEBUG) {
+ Log.d(TAG,
+ "addAutomaticZenRule zenRule= " + automaticZenRule + " reason=" + reason);
+ }
+ if (!TextUtils.isEmpty(automaticZenRule.getId())) {
+ throw new IllegalArgumentException("Rule already exists");
+ }
+ newConfig = mConfig.copy();
}
- final ZenModeConfig newConfig = mConfig.copy();
ZenRule rule = new ZenRule();
populateZenRule(automaticZenRule, rule, true);
newConfig.automaticRules.put(rule.id, rule);
@@ -242,12 +252,15 @@ public class ZenModeHelper {
}
public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
- if (mConfig == null) return false;
- if (DEBUG) {
- Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
- + " reason=" + reason);
+ ZenModeConfig newConfig;
+ synchronized (mConfig) {
+ if (mConfig == null) return false;
+ if (DEBUG) {
+ Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
+ + " reason=" + reason);
+ }
+ newConfig = mConfig.copy();
}
- final ZenModeConfig newConfig = mConfig.copy();
final String ruleId = automaticZenRule.getId();
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
if (ruleId == null) {
@@ -265,8 +278,11 @@ public class ZenModeHelper {
}
public boolean removeAutomaticZenRule(String id, String reason) {
- if (mConfig == null) return false;
- final ZenModeConfig newConfig = mConfig.copy();
+ ZenModeConfig newConfig;
+ synchronized (mConfig) {
+ if (mConfig == null) return false;
+ newConfig = mConfig.copy();
+ }
ZenRule rule = newConfig.automaticRules.get(id);
if (rule == null) return false;
if (canManageAutomaticZenRule(rule)) {
@@ -328,12 +344,15 @@ public class ZenModeHelper {
private void setManualZenMode(int zenMode, Uri conditionId, String reason,
boolean setRingerMode) {
- if (mConfig == null) return;
- if (!Global.isValidZenMode(zenMode)) return;
- if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
- + " conditionId=" + conditionId + " reason=" + reason
- + " setRingerMode=" + setRingerMode);
- final ZenModeConfig newConfig = mConfig.copy();
+ ZenModeConfig newConfig;
+ synchronized (mConfig) {
+ if (mConfig == null) return;
+ if (!Global.isValidZenMode(zenMode)) return;
+ if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
+ + " conditionId=" + conditionId + " reason=" + reason
+ + " setRingerMode=" + setRingerMode);
+ newConfig = mConfig.copy();
+ }
if (zenMode == Global.ZEN_MODE_OFF) {
newConfig.manualRule = null;
for (ZenRule automaticRule : newConfig.automaticRules.values()) {
@@ -360,7 +379,9 @@ public class ZenModeHelper {
dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
}
pw.print(prefix); pw.print("mUser="); pw.println(mUser);
- dump(pw, prefix, "mConfig", mConfig);
+ synchronized (mConfig) {
+ dump(pw, prefix, "mConfig", mConfig);
+ }
pw.print(prefix); pw.print("mEffectsSuppressed="); pw.println(mEffectsSuppressed);
mFiltering.dump(pw, prefix);
mConditions.dump(pw, prefix);
@@ -437,7 +458,9 @@ public class ZenModeHelper {
}
public ZenModeConfig getConfig() {
- return mConfig;
+ synchronized (mConfig) {
+ return mConfig.copy();
+ }
}
public boolean setConfig(ZenModeConfig config, String reason) {
@@ -462,19 +485,21 @@ public class ZenModeHelper {
return true;
}
mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config
- mConfigs.put(config.user, config);
- if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
- ZenLog.traceConfig(reason, mConfig, config);
- final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
- getNotificationPolicy(config));
- mConfig = config;
- if (config.equals(mConfig)) {
- dispatchOnConfigChanged();
- }
- if (policyChanged){
- dispatchOnPolicyChanged();
+ synchronized (mConfig) {
+ mConfigs.put(config.user, config);
+ if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
+ ZenLog.traceConfig(reason, mConfig, config);
+ final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
+ getNotificationPolicy(config));
+ mConfig = config;
+ if (config.equals(mConfig)) {
+ dispatchOnConfigChanged();
+ }
+ if (policyChanged) {
+ dispatchOnPolicyChanged();
+ }
}
- final String val = Integer.toString(mConfig.hashCode());
+ final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
if (!evaluateZenMode(reason, setRingerMode)) {
applyRestrictions(); // evaluateZenMode will also apply restrictions if changed
@@ -529,17 +554,19 @@ public class ZenModeHelper {
}
private int computeZenMode() {
- if (mConfig == null) return Global.ZEN_MODE_OFF;
- if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
- int zen = Global.ZEN_MODE_OFF;
- for (ZenRule automaticRule : mConfig.automaticRules.values()) {
- if (automaticRule.isAutomaticActive()) {
- if (zenSeverity(automaticRule.zenMode) > zenSeverity(zen)) {
- zen = automaticRule.zenMode;
+ synchronized (mConfig) {
+ if (mConfig == null) return Global.ZEN_MODE_OFF;
+ if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
+ int zen = Global.ZEN_MODE_OFF;
+ for (ZenRule automaticRule : mConfig.automaticRules.values()) {
+ if (automaticRule.isAutomaticActive()) {
+ if (zenSeverity(automaticRule.zenMode) > zenSeverity(zen)) {
+ zen = automaticRule.zenMode;
+ }
}
}
+ return zen;
}
- return zen;
}
private void applyRestrictions() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e1496166cfd1..be9f44dcd299 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 @@ public class PackageManagerService extends IPackageManager.Stub {
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 @@ public class PackageManagerService extends IPackageManager.Stub {
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 dc34904d98a9..eb0ca232462b 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.Interpolator;
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
} 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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
}
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);
+ 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);
+ 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);
+ AnimationSet set = new AnimationSet(true);
+ set.addAnimation(clipAnim);
+ set.addAnimation(scaleAnim);
+ set.addAnimation(translateAnim);
- a = set;
- a.setZAdjustment(Animation.ZORDER_TOP);
+ a = set;
+ a.setZAdjustment(Animation.ZORDER_TOP);
+ }
break;
}
default:
@@ -884,27 +888,48 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 @@ public class AppTransition implements Dump {
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 943e3ea736d4..c5bd3a79ac5b 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 @@ class AppWindowToken extends WindowToken {
}
}
+ /**
+ * 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 39479c1a9681..fab8ee55b22c 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 @@ package com.android.server.wm;
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 @@ class DisplayContent {
}
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 2d8712375757..04cba81726ec 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 @@ package com.android.server.wm;
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 @@ public class DockedStackDividerController implements View.OnTouchListener, DimLa
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 6c391ade60cc..5f911c36825e 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 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
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 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
inputWindowHandle.scaleFactor = 1;
}
-
+ if (DEBUG_INPUT) {
+ Slog.d(WindowManagerService.TAG, "addInputWindowHandle: " + inputWindowHandle);
+ }
addInputWindowHandleLw(inputWindowHandle);
}
@@ -428,7 +442,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
* 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 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
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 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
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 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
public void freezeInputDispatchingLw() {
if (! mInputDispatchFrozen) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
}
@@ -497,7 +511,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
public void thawInputDispatchingLw() {
if (mInputDispatchFrozen) {
- if (WindowManagerService.DEBUG_INPUT) {
+ if (DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
}
@@ -508,7 +522,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
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 4d11452a8c0c..ad4419604896 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 @@ class Task implements DimLayer.DimLayerUser {
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 9b3d47823d7a..df664bd271bd 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 @@ public class TaskStack implements DimLayer.DimLayerUser {
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 @@ public class TaskStack implements DimLayer.DimLayerUser {
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 @@ public class TaskStack implements DimLayer.DimLayerUser {
* @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 @@ public class TaskStack implements DimLayer.DimLayerUser {
// 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 47995a7c4099..140fbaf01f58 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 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
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 @@ public class WindowManagerService extends IWindowManager.Stub
// 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 @@ public class WindowManagerService extends IWindowManager.Stub
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 @@ public class WindowManagerService extends IWindowManager.Stub
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 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- 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 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ 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 @@ public class WindowManagerService extends IWindowManager.Stub
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 @@ public class WindowManagerService extends IWindowManager.Stub
} 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 @@ public class WindowManagerService extends IWindowManager.Stub
@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 e9ea3a89ff19..1d2cb7535b97 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;
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 @@ final class WindowState implements WindowManagerPolicy.WindowState {
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 68c39ba432e3..80f1094d61a6 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 @@ class WindowStateAnimator {
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 b267860ce80e..aca0f5b6de39 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 @@ class WindowSurfacePlacer {
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 @@ class WindowSurfacePlacer {
if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
wallpaperDestroyed = true;
}
- if (!win.saveSurfaceIfNeeded()) {
+ if (!win.shouldSaveSurface()) {
win.mWinAnimator.destroySurfaceLocked();
}
} while (i > 0);
@@ -1191,7 +1191,10 @@ class WindowSurfacePlacer {
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 @@ class WindowSurfacePlacer {
}
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/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 89b2a47d73c6..01acdeffb853 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -414,12 +414,9 @@ int JTvInputHal::removeStream(int deviceId, int streamId) {
return NO_ERROR;
}
if (Surface::isValid(connection.mSurface)) {
- connection.mSurface.clear();
- }
- if (connection.mSurface != NULL) {
connection.mSurface->setSidebandStream(NULL);
- connection.mSurface.clear();
}
+ connection.mSurface.clear();
if (connection.mThread != NULL) {
connection.mThread->shutdown();
connection.mThread.clear();
@@ -616,6 +613,9 @@ static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz,
return BAD_VALUE;
}
sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
+ if (!Surface::isValid(surface)) {
+ return BAD_VALUE;
+ }
return tvInputHal->addOrUpdateStream(deviceId, streamId, surface);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1ec1a46f3620..e32af5c92d8d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -438,7 +438,6 @@ public final class SystemServer {
InputManagerService inputManager = null;
TelephonyRegistry telephonyRegistry = null;
ConsumerIrService consumerIr = null;
- AudioService audioService = null;
MmsServiceBroker mmsService = null;
EntropyMixer entropyMixer = null;
@@ -857,12 +856,7 @@ public final class SystemServer {
}
traceBeginAndSlog("StartAudioService");
- try {
- audioService = new AudioService(context);
- ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
- } catch (Throwable e) {
- reportWtf("starting Audio Service", e);
- }
+ mSystemServiceManager.startService(AudioService.Lifecycle.class);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
if (!disableNonCoreServices) {
@@ -1163,7 +1157,6 @@ public final class SystemServer {
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
final MediaRouterService mediaRouterF = mediaRouter;
- final AudioService audioServiceF = audioService;
final MmsServiceBroker mmsServiceF = mmsService;
// We now tell the activity manager it is okay to run third party
@@ -1234,13 +1227,7 @@ public final class SystemServer {
reportWtf("making Connectivity Service ready", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeAudioServiceReady");
- try {
- if (audioServiceF != null) audioServiceF.systemReady();
- } catch (Throwable e) {
- reportWtf("Notifying AudioService running", e);
- }
- Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+
Watchdog.getInstance().start();
// It is now okay to let the various system services start their
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 28cb114ccf6b..c9efc69b5979 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -417,9 +417,10 @@ public class DhcpClient extends BaseDhcpStateMachine {
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 class DhcpClient extends BaseDhcpStateMachine {
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 @@ public class DhcpClient extends BaseDhcpStateMachine {
}
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 701272e28630..129e5377882a 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 @@ public final class UsbAlsaManager {
}
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 @@ public final class UsbAlsaManager {
*/
/* 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 @@ public final class UsbAlsaManager {
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 @@ public final class UsbAlsaManager {
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 @@ public final class UsbAlsaManager {
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 @@ public final class UsbAlsaManager {
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 @@ public final class UsbAlsaManager {
}
}
}
+
+ 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 3160e3961495..e0f95cfa18a0 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 @@ public class UsbDeviceManager {
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;
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index f263b4da7b29..861a37990629 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -178,6 +178,7 @@ public class ImsCallProfile implements Parcelable {
* Codec: Codec info.
* DisplayText: Display text for the call.
* AdditionalCallInfo: Additional call info.
+ * CallRadioTech: The radio tech on which the call is placed.
*/
public static final String EXTRA_OI = "oi";
public static final String EXTRA_CNA = "cna";
@@ -187,6 +188,7 @@ public class ImsCallProfile implements Parcelable {
public static final String EXTRA_CODEC = "Codec";
public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
+ public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
public int mServiceType;
public int mCallType;