Merge "Correct drawLines annotations"
diff --git a/Android.mk b/Android.mk
index 137ef85..51dfa57 100644
--- a/Android.mk
+++ b/Android.mk
@@ -226,6 +226,7 @@
core/java/android/service/carrier/ICarrierMessagingService.aidl \
core/java/android/service/gatekeeper/IGateKeeperService.aidl \
core/java/android/service/notification/INotificationListener.aidl \
+ core/java/android/service/notification/INotificationAssistant.aidl \
core/java/android/service/notification/IStatusBarNotificationHolder.aidl \
core/java/android/service/notification/IConditionListener.aidl \
core/java/android/service/notification/IConditionProvider.aidl \
@@ -308,7 +309,7 @@
core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl \
core/java/com/android/internal/textservice/ITextServicesManager.aidl \
core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl \
- core/java/com/android/internal/view/IDropPermissionHolder.aidl \
+ core/java/com/android/internal/view/IDropPermissions.aidl \
core/java/com/android/internal/view/IInputContext.aidl \
core/java/com/android/internal/view/IInputContextCallback.aidl \
core/java/com/android/internal/view/IInputMethod.aidl \
diff --git a/api/current.txt b/api/current.txt
index 622a8b7..e4962d8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5740,6 +5740,7 @@
method public java.lang.String[] getAccountTypesWithManagementDisabled();
method public java.util.List<android.content.ComponentName> getActiveAdmins();
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
+ method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
method public boolean getAutoTimeRequired();
method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5783,6 +5784,7 @@
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
+ method public boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5790,6 +5792,7 @@
method public boolean isProvisioningAllowed(java.lang.String);
method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
method public void lockNow();
+ method public void reboot(android.content.ComponentName);
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
@@ -5798,6 +5801,7 @@
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
+ method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
method public void setAutoTimeRequired(android.content.ComponentName, boolean);
method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
method public void setCameraDisabled(android.content.ComponentName, boolean);
@@ -30915,6 +30919,7 @@
field public static final java.lang.String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
+ field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS";
field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS";
@@ -33191,13 +33196,20 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
+ public class NotificationAdjustment implements android.os.Parcelable {
+ ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
+ }
+
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
ctor public NotificationAssistantService();
- method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
+ method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
method public final void clearAnnotation(java.lang.String);
method public void onNotificationActionClick(java.lang.String, long, int);
method public void onNotificationClick(java.lang.String, long);
- method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+ method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public void onNotificationRemoved(java.lang.String, long, int);
method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -33214,10 +33226,7 @@
field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
field public static final int REASON_USER_STOPPED = 6; // 0x6
- }
-
- public class NotificationAssistantService.Adjustment {
- ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
public abstract class NotificationListenerService extends android.app.Service {
@@ -39298,11 +39307,11 @@
method public int getAction();
method public android.content.ClipData getClipData();
method public android.content.ClipDescription getClipDescription();
- method public android.view.DropPermissionHolder getDropPermissionHolder();
method public java.lang.Object getLocalState();
method public boolean getResult();
method public float getX();
method public float getY();
+ method public android.view.DropPermissions requestDropPermissions();
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_DRAG_ENDED = 4; // 0x4
field public static final int ACTION_DRAG_ENTERED = 5; // 0x5
@@ -39313,12 +39322,8 @@
field public static final android.os.Parcelable.Creator<android.view.DragEvent> CREATOR;
}
- public class DropPermissionHolder implements android.os.Parcelable {
- method public int describeContents();
- method public void grant();
- method public void revoke();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.view.DropPermissionHolder> CREATOR;
+ public final class DropPermissions {
+ method public void release();
}
public class FocusFinder {
diff --git a/api/system-current.txt b/api/system-current.txt
index e51b7d5..586ea06 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5865,6 +5865,7 @@
method public java.lang.String[] getAccountTypesWithManagementDisabled();
method public java.util.List<android.content.ComponentName> getActiveAdmins();
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
+ method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
method public boolean getAutoTimeRequired();
method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5915,6 +5916,7 @@
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
+ method public boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5923,6 +5925,7 @@
method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
method public void lockNow();
method public void notifyPendingSystemUpdate(long);
+ method public void reboot(android.content.ComponentName);
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
@@ -5932,6 +5935,7 @@
method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
+ method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
method public void setAutoTimeRequired(android.content.ComponentName, boolean);
method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
method public void setCameraDisabled(android.content.ComponentName, boolean);
@@ -9652,7 +9656,6 @@
method public void setAppLabel(java.lang.CharSequence);
method public void setAppPackageName(java.lang.String);
method public void setGrantedRuntimePermissions(java.lang.String[]);
- method public void setInstallFlagsQuick();
method public void setInstallLocation(int);
method public void setOriginatingUid(int);
method public void setOriginatingUri(android.net.Uri);
@@ -23791,6 +23794,7 @@
public class TvStreamConfig implements android.os.Parcelable {
method public int describeContents();
+ method public int getFlags();
method public int getGeneration();
method public int getMaxHeight();
method public int getMaxWidth();
@@ -23798,6 +23802,7 @@
method public int getType();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.media.tv.TvStreamConfig> CREATOR;
+ field public static final int FLAG_MASK_SIGNAL_DETECTION = 1; // 0x1
field public static final int STREAM_TYPE_BUFFER_PRODUCER = 2; // 0x2
field public static final int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1; // 0x1
}
@@ -23805,6 +23810,7 @@
public static final class TvStreamConfig.Builder {
ctor public TvStreamConfig.Builder();
method public android.media.tv.TvStreamConfig build();
+ method public android.media.tv.TvStreamConfig.Builder flags(int);
method public android.media.tv.TvStreamConfig.Builder generation(int);
method public android.media.tv.TvStreamConfig.Builder maxHeight(int);
method public android.media.tv.TvStreamConfig.Builder maxWidth(int);
@@ -33055,6 +33061,7 @@
field public static final java.lang.String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
+ field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS";
field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS";
@@ -35332,13 +35339,20 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
+ public class NotificationAdjustment implements android.os.Parcelable {
+ ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
+ }
+
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
ctor public NotificationAssistantService();
- method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
+ method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
method public final void clearAnnotation(java.lang.String);
method public void onNotificationActionClick(java.lang.String, long, int);
method public void onNotificationClick(java.lang.String, long);
- method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+ method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public void onNotificationRemoved(java.lang.String, long, int);
method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -35355,10 +35369,7 @@
field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
field public static final int REASON_USER_STOPPED = 6; // 0x6
- }
-
- public class NotificationAssistantService.Adjustment {
- ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
public abstract class NotificationListenerService extends android.app.Service {
@@ -41650,11 +41661,11 @@
method public int getAction();
method public android.content.ClipData getClipData();
method public android.content.ClipDescription getClipDescription();
- method public android.view.DropPermissionHolder getDropPermissionHolder();
method public java.lang.Object getLocalState();
method public boolean getResult();
method public float getX();
method public float getY();
+ method public android.view.DropPermissions requestDropPermissions();
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_DRAG_ENDED = 4; // 0x4
field public static final int ACTION_DRAG_ENTERED = 5; // 0x5
@@ -41665,12 +41676,8 @@
field public static final android.os.Parcelable.Creator<android.view.DragEvent> CREATOR;
}
- public class DropPermissionHolder implements android.os.Parcelable {
- method public int describeContents();
- method public void grant();
- method public void revoke();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.view.DropPermissionHolder> CREATOR;
+ public final class DropPermissions {
+ method public void release();
}
public class FocusFinder {
diff --git a/api/test-current.txt b/api/test-current.txt
index 2377a9b..eccd8b0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5740,6 +5740,7 @@
method public java.lang.String[] getAccountTypesWithManagementDisabled();
method public java.util.List<android.content.ComponentName> getActiveAdmins();
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
+ method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
method public boolean getAutoTimeRequired();
method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5783,6 +5784,7 @@
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
+ method public boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5790,6 +5792,7 @@
method public boolean isProvisioningAllowed(java.lang.String);
method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
method public void lockNow();
+ method public void reboot(android.content.ComponentName);
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
@@ -5798,6 +5801,7 @@
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
+ method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
method public void setAutoTimeRequired(android.content.ComponentName, boolean);
method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
method public void setCameraDisabled(android.content.ComponentName, boolean);
@@ -30917,6 +30921,7 @@
field public static final java.lang.String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
+ field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS";
field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS";
@@ -33193,13 +33198,20 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
+ public class NotificationAdjustment implements android.os.Parcelable {
+ ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
+ }
+
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
ctor public NotificationAssistantService();
- method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
+ method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
method public final void clearAnnotation(java.lang.String);
method public void onNotificationActionClick(java.lang.String, long, int);
method public void onNotificationClick(java.lang.String, long);
- method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+ method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public void onNotificationRemoved(java.lang.String, long, int);
method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -33216,10 +33228,7 @@
field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
field public static final int REASON_USER_STOPPED = 6; // 0x6
- }
-
- public class NotificationAssistantService.Adjustment {
- ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
public abstract class NotificationListenerService extends android.app.Service {
@@ -39300,11 +39309,11 @@
method public int getAction();
method public android.content.ClipData getClipData();
method public android.content.ClipDescription getClipDescription();
- method public android.view.DropPermissionHolder getDropPermissionHolder();
method public java.lang.Object getLocalState();
method public boolean getResult();
method public float getX();
method public float getY();
+ method public android.view.DropPermissions requestDropPermissions();
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_DRAG_ENDED = 4; // 0x4
field public static final int ACTION_DRAG_ENTERED = 5; // 0x5
@@ -39315,12 +39324,8 @@
field public static final android.os.Parcelable.Creator<android.view.DragEvent> CREATOR;
}
- public class DropPermissionHolder implements android.os.Parcelable {
- method public int describeContents();
- method public void grant();
- method public void revoke();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.view.DropPermissionHolder> CREATOR;
+ public final class DropPermissions {
+ method public void release();
}
public class FocusFinder {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 571bc6e..4ed2fd7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5300,8 +5300,7 @@
*/
public boolean isTaskRoot() {
try {
- return ActivityManagerNative.getDefault()
- .getTaskForActivity(mToken, true) >= 0;
+ return ActivityManagerNative.getDefault().getTaskForActivity(mToken, true) >= 0;
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c39ee75..681ed5b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -556,14 +556,6 @@
public static boolean isAlwaysOnTop(int stackId) {
return stackId == PINNED_STACK_ID;
}
-
- /**
- * Returns true if the application windows in this stack should be displayed above all
- * other application windows, including during the animation.
- */
- public static boolean shouldIncreaseApplicationWindowLayer(int stackId) {
- return stackId == PINNED_STACK_ID || stackId == DOCKED_STACK_ID;
- }
}
/**
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 136b810..bce5bf3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -54,6 +54,7 @@
int getTopicPriority(String pkg, int uid, in Notification.Topic topic);
void setTopicImportance(String pkg, int uid, in Notification.Topic topic, int importance);
int getTopicImportance(String pkg, int uid, in Notification.Topic topic);
+ void setAppImportance(String pkg, int uid, int importance);
// TODO: Remove this when callers have been migrated to the equivalent
// INotificationListener method.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 660ce3b..5df6ba8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -40,8 +40,8 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
import android.provider.ContactsContract.Directory;
+import android.provider.Settings;
import android.security.Credentials;
import android.service.restrictions.RestrictionsReceiver;
import android.util.Log;
@@ -56,14 +56,14 @@
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
-import java.security.NoSuchAlgorithmException;
+import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -3312,8 +3312,69 @@
}
/**
- * Called by a profile or device owner to set the application restrictions for a given target
- * application running in the profile.
+ * Called by a profile owner or device owner to grant permission to a package to manage
+ * application restrictions for the calling user via {@link #setApplicationRestrictions} and
+ * {@link #getApplicationRestrictions}.
+ * <p>
+ * This permission is persistent until it is later cleared by calling this method with a
+ * {@code null} value or uninstalling the managing package.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageName The package name which will be given access to application restrictions
+ * APIs. If {@code null} is given the current package will be cleared.
+ */
+ public void setApplicationRestrictionsManagingPackage(@NonNull ComponentName admin,
+ @Nullable String packageName) {
+ if (mService != null) {
+ try {
+ mService.setApplicationRestrictionsManagingPackage(admin, packageName);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ }
+
+ /**
+ * Called by a profile owner or device owner to retrieve the application restrictions managing
+ * package for the current user, or {@code null} if none is set.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @return The package name allowed to manage application restrictions on the current user, or
+ * {@code null} if none is set.
+ */
+ public String getApplicationRestrictionsManagingPackage(@NonNull ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getApplicationRestrictionsManagingPackage(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns {@code true} if the calling package has been granted permission via
+ * {@link #setApplicationRestrictionsManagingPackage} to manage application
+ * restrictions for the calling user.
+ */
+ public boolean isCallerApplicationRestrictionsManagingPackage() {
+ if (mService != null) {
+ try {
+ return mService.isCallerApplicationRestrictionsManagingPackage();
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets the application restrictions for a given target application running in the calling user.
+ *
+ * <p>The caller must be a profile or device owner on that user, or the package allowed to
+ * manage application restrictions via {@link #setApplicationRestrictionsManagingPackage};
+ * otherwise a security exception will be thrown.
*
* <p>The provided {@link Bundle} consists of key-value pairs, where the types of values may be:
* <ul>
@@ -3323,24 +3384,25 @@
* <li>From {@link android.os.Build.VERSION_CODES#M}, {@code Bundle} or {@code Bundle[]}
* </ul>
*
- * <p>The application restrictions are only made visible to the target application and the
- * profile or device owner.
- *
* <p>If the restrictions are not available yet, but may be applied in the near future,
- * the admin can notify the target application of that by adding
+ * the caller can notify the target application of that by adding
* {@link UserManager#KEY_RESTRICTIONS_PENDING} to the settings parameter.
*
- * <p>The calling device admin must be a profile or device owner; if it is not, a security
- * exception will be thrown.
+ * <p>The application restrictions are only made visible to the target application via
+ * {@link UserManager#getApplicationRestrictions(String)}, in addition to the profile or
+ * device owner, and the application restrictions managing package via
+ * {@link #getApplicationRestrictions}.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+ * {@code null} if called by the application restrictions managing package.
* @param packageName The name of the package to update restricted settings for.
* @param settings A {@link Bundle} to be parsed by the receiving application, conveying a new
* set of active restrictions.
*
+ * @see #setApplicationRestrictionsManagingPackage
* @see UserManager#KEY_RESTRICTIONS_PENDING
*/
- public void setApplicationRestrictions(@NonNull ComponentName admin, String packageName,
+ public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
Bundle settings) {
if (mService != null) {
try {
@@ -3896,19 +3958,23 @@
}
/**
- * Called by a profile or device owner to get the application restrictions for a given target
- * application running in the profile.
+ * Retrieves the application restrictions for a given target application running in the calling
+ * user.
*
- * <p>The calling device admin must be a profile or device owner; if it is not, a security
- * exception will be thrown.
+ * <p>The caller must be a profile or device owner on that user, or the package allowed to
+ * manage application restrictions via {@link #setApplicationRestrictionsManagingPackage};
+ * otherwise a security exception will be thrown.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+ * {@code null} if called by the application restrictions managing package.
* @param packageName The name of the package to fetch restricted settings of.
* @return {@link Bundle} of settings corresponding to what was set last time
* {@link DevicePolicyManager#setApplicationRestrictions} was called, or an empty {@link Bundle}
* if no restrictions have been set.
+ *
+ * @see {@link #setApplicationRestrictionsManagingPackage}
*/
- public Bundle getApplicationRestrictions(@NonNull ComponentName admin, String packageName) {
+ public Bundle getApplicationRestrictions(@Nullable ComponentName admin, String packageName) {
if (mService != null) {
try {
return mService.getApplicationRestrictions(admin, packageName);
@@ -4721,4 +4787,15 @@
return null;
}
}
+
+ /**
+ * Called by device owner to reboot the device.
+ */
+ public void reboot(@NonNull ComponentName admin) {
+ try {
+ mService.reboot(admin);
+ } catch (RemoteException re) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d8cc2ea..30ce682 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -149,6 +149,9 @@
void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings);
Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
+ void setApplicationRestrictionsManagingPackage(in ComponentName admin, in String packageName);
+ String getApplicationRestrictionsManagingPackage(in ComponentName admin);
+ boolean isCallerApplicationRestrictionsManagingPackage();
void setRestrictionsProvider(in ComponentName who, in ComponentName provider);
ComponentName getRestrictionsProvider(int userHandle);
@@ -241,4 +244,5 @@
boolean isManagedProfile(in ComponentName admin);
boolean isSystemOnlyUser(in ComponentName admin);
String getWifiMacAddress();
+ void reboot(in ComponentName admin);
}
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index c934e8d..0b80f8a 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -36,6 +36,7 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
+import java.util.List;
/**
* Representation of a clipped data on the clipboard.
@@ -914,6 +915,27 @@
}
}
+ /** @hide */
+ public void collectUris(List<Uri> out) {
+ for (int i = 0; i < mItems.size(); ++i) {
+ ClipData.Item item = getItemAt(i);
+
+ if (item.getUri() != null) {
+ out.add(item.getUri());
+ }
+
+ Intent intent = item.getIntent();
+ if (intent != null) {
+ if (intent.getData() != null) {
+ out.add(intent.getData());
+ }
+ if (intent.getClipData() != null) {
+ intent.getClipData().collectUris(out);
+ }
+ }
+ }
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 38a4475..67bdad5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -326,10 +326,30 @@
*/
public static final int BIND_NOT_VISIBLE = 0x40000000;
- /** Return an AssetManager instance for your application's package. */
+ /**
+ * Returns an AssetManager instance for the application's package.
+ * <p>
+ * <strong>Note:</strong> Implementations of this method should return
+ * an AssetManager instance that is consistent with the Resources instance
+ * returned by {@link #getResources()}. For example, they should share the
+ * same {@link Configuration} object.
+ *
+ * @return an AssetManager instance for the application's package
+ * @see #getResources()
+ */
public abstract AssetManager getAssets();
- /** Return a Resources instance for your application's package. */
+ /**
+ * Returns a Resources instance for the application's package.
+ * <p>
+ * <strong>Note:</strong> Implementations of this method should return
+ * a Resources instance that is consistent with the AssetManager instance
+ * returned by {@link #getAssets()}. For example, they should share the
+ * same {@link Configuration} object.
+ *
+ * @return a Resources instance for the application's package
+ * @see #getAssets()
+ */
public abstract Resources getResources();
/** Return PackageManager instance to find global package information. */
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 1a3d262..c99ddc8 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -82,8 +82,7 @@
}
@Override
- public Resources getResources()
- {
+ public Resources getResources() {
return mBase.getResources();
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d6d395b..3283005 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1042,12 +1042,6 @@
}
/** {@hide} */
- @SystemApi
- public void setInstallFlagsQuick() {
- installFlags |= PackageManager.INSTALL_QUICK;
- }
-
- /** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
pw.printHexPair("installFlags", installFlags);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3e7deb9..40bcc7e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -457,19 +457,11 @@
/**
* Flag parameter for {@link #installPackage} to indicate that this package is
- * to be installed quickly.
- *
- * @hide
- */
- public static final int INSTALL_QUICK = 0x00000800;
-
- /**
- * Flag parameter for {@link #installPackage} to indicate that this package is
* to be installed as a lightweight "ephemeral" app.
*
* @hide
*/
- public static final int INSTALL_EPHEMERAL = 0x00001000;
+ public static final int INSTALL_EPHEMERAL = 0x00000800;
/**
* Flag parameter for
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 019ed2b..f445cf8 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -625,9 +625,7 @@
public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
public final static int PARSE_TRUSTED_OVERLAY = 1<<9;
public final static int PARSE_ENFORCE_CODE = 1<<10;
- // TODO: fix b/25118622; remove this entirely once signature processing is quick
- public final static int PARSE_SKIP_VERIFICATION = 1<<11;
- public final static int PARSE_IS_EPHEMERAL = 1<<12;
+ public final static int PARSE_IS_EPHEMERAL = 1<<11;
private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
@@ -1060,8 +1058,7 @@
/**
* Collect certificates from all the APKs described in the given package,
- * populating {@link Package#mSignatures}.
- * <p>Depending upon the parser flags, this may also asserts that all APK
+ * populating {@link Package#mSignatures}. Also asserts that all APK
* contents are signed correctly and consistently.
*/
public void collectCertificates(Package pkg, int parseFlags) throws PackageParserException {
@@ -1084,10 +1081,8 @@
final boolean hasCode = (apkFlags & ApplicationInfo.FLAG_HAS_CODE) != 0;
final boolean requireCode = ((parseFlags & PARSE_ENFORCE_CODE) != 0) && hasCode;
final String apkPath = apkFile.getAbsolutePath();
- final boolean skipVerification = Build.IS_DEBUGGABLE
- && ((parseFlags & PARSE_SKIP_VERIFICATION) != 0);
- boolean codeFound = skipVerification;
+ boolean codeFound = false;
StrictJarFile jarFile = null;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
@@ -1106,7 +1101,7 @@
toVerify.add(manifestEntry);
// If we're parsing an untrusted package, verify all contents
- if (!skipVerification && (parseFlags & PARSE_IS_SYSTEM) == 0) {
+ if ((parseFlags & PARSE_IS_SYSTEM) == 0) {
final Iterator<ZipEntry> i = jarFile.iterator();
while (i.hasNext()) {
final ZipEntry entry = i.next();
@@ -1150,9 +1145,6 @@
for (int i=0; i < entryCerts.length; i++) {
pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
}
- if (skipVerification) {
- break;
- }
} else {
if (!Signature.areExactMatch(pkg.mSignatures, entrySignatures)) {
throw new PackageParserException(
@@ -1218,9 +1210,7 @@
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
// TODO: factor signature related items out of Package object
final Package tempPkg = new Package(null);
- // TODO: fix b/25118622; pass in '0' for parse flags
- collectCertificates(tempPkg, apkFile, 0 /*apkFlags*/,
- flags & PARSE_SKIP_VERIFICATION);
+ collectCertificates(tempPkg, apkFile, 0 /*apkFlags*/, 0 /*flags*/);
signatures = tempPkg.mSignatures;
} else {
signatures = null;
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index cd483b1..ee7bd9a 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -32,7 +32,9 @@
private static final String TAG = "Bundle";
static final boolean DEBUG = false;
+ // Keep in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+
static final Parcel EMPTY_PARCEL;
static {
@@ -1308,6 +1310,8 @@
* @param parcel The parcel to copy this bundle to.
*/
void writeToParcelInner(Parcel parcel, int flags) {
+ // Keep implementation in sync with writeToParcel() in
+ // frameworks/native/libs/binder/PersistableBundle.cpp.
if (mParcelledData != null) {
if (mParcelledData == EMPTY_PARCEL) {
parcel.writeInt(0);
@@ -1345,6 +1349,8 @@
* @param parcel The parcel to overwrite this bundle from.
*/
void readFromParcelInner(Parcel parcel) {
+ // Keep implementation in sync with readFromParcel() in
+ // frameworks/native/libs/binder/PersistableBundle.cpp.
int length = parcel.readInt();
readFromParcelInner(parcel, length);
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 9b68f90..2631247 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -204,6 +204,7 @@
private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE];
+ // Keep in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
private static final int VAL_NULL = -1;
private static final int VAL_STRING = 0;
private static final int VAL_INTEGER = 1;
@@ -704,6 +705,8 @@
writeInt(-1);
return;
}
+ // Keep the format of this Parcel in sync with writeToParcelInner() in
+ // frameworks/native/libs/binder/PersistableBundle.cpp.
final int N = val.size();
writeInt(N);
if (DEBUG_ARRAY_MAP) {
@@ -1370,7 +1373,13 @@
// Must be before Parcelable
writeInt(VAL_BUNDLE);
writeBundle((Bundle) v);
+ } else if (v instanceof PersistableBundle) {
+ writeInt(VAL_PERSISTABLEBUNDLE);
+ writePersistableBundle((PersistableBundle) v);
} else if (v instanceof Parcelable) {
+ // IMPOTANT: cases for classes that implement Parcelable must
+ // come before the Parcelable case, so that their specific VAL_*
+ // types will be written.
writeInt(VAL_PARCELABLE);
writeParcelable((Parcelable) v, 0);
} else if (v instanceof Short) {
@@ -1426,9 +1435,6 @@
} else if (v instanceof Byte) {
writeInt(VAL_BYTE);
writeInt((Byte) v);
- } else if (v instanceof PersistableBundle) {
- writeInt(VAL_PERSISTABLEBUNDLE);
- writePersistableBundle((PersistableBundle) v);
} else if (v instanceof Size) {
writeInt(VAL_SIZE);
writeSize((Size) v);
diff --git a/core/java/android/os/PersistableBundle.aidl b/core/java/android/os/PersistableBundle.aidl
index 5b05873..94e8607 100644
--- a/core/java/android/os/PersistableBundle.aidl
+++ b/core/java/android/os/PersistableBundle.aidl
@@ -2,19 +2,19 @@
**
** Copyright 2014, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** 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
+** 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
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
package android.os;
-parcelable PersistableBundle;
+parcelable PersistableBundle cpp_header "binder/PersistableBundle.h";
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index a0a0060..126824f 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -389,6 +389,13 @@
public static final String REBOOT_RECOVERY = "recovery";
/**
+ * The value to pass as the 'reason' argument to reboot() when device owner requests a reboot on
+ * the device.
+ * @hide
+ */
+ public static final String REBOOT_REQUESTED_BY_DEVICE_OWNER = "deviceowner";
+
+ /**
* The value to pass as the 'reason' argument to android_reboot().
* @hide
*/
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c5adafe..ddd16e2 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -852,9 +852,14 @@
* @param user to retrieve the unlocked state for.
*/
public boolean isUserUnlocked(UserHandle user) {
+ return isUserUnlocked(user.getIdentifier());
+ }
+
+ /** {@hide} */
+ public boolean isUserUnlocked(int userId) {
try {
- return ActivityManagerNative.getDefault().isUserRunning(
- user.getIdentifier(), ActivityManager.FLAG_AND_UNLOCKED);
+ return ActivityManagerNative.getDefault().isUserRunning(userId,
+ ActivityManager.FLAG_AND_UNLOCKED);
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a1e5510..9508077 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1092,6 +1092,22 @@
public static final String ACTION_HOME_SETTINGS
= "android.settings.HOME_SETTINGS";
+
+
+ /**
+ * Activity Action: Show Default apps settings.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_DEFAULT_APPS_SETTINGS
+ = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
+
/**
* Activity Action: Show notification settings.
*
@@ -5617,6 +5633,15 @@
public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled";
/**
+ * Names of the service component that the current user has explicitly allowed to
+ * see and change the importance of all of the user's notifications.
+ *
+ * @hide
+ */
+ public static final String ENABLED_NOTIFICATION_ASSISTANT
+ = "enabled_notification_assistant";
+
+ /**
* Names of the service components that the current user has explicitly allowed to
* see all of the user's notifications, separated by ':'.
*
diff --git a/core/java/android/service/notification/INotificationAssistant.aidl b/core/java/android/service/notification/INotificationAssistant.aidl
new file mode 100644
index 0000000..5c5f358
--- /dev/null
+++ b/core/java/android/service/notification/INotificationAssistant.aidl
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import android.service.notification.NotificationAdjustment;
+import android.service.notification.IStatusBarNotificationHolder;
+import android.service.notification.NotificationRankingUpdate;
+
+/** @hide */
+interface INotificationAssistant
+{
+ void onListenerConnected(in NotificationRankingUpdate update);
+ void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
+ in NotificationRankingUpdate update);
+ void onNotificationRankingUpdate(in NotificationRankingUpdate update);
+ void onListenerHintsChanged(int hints);
+ void onInterruptionFilterChanged(int interruptionFilter);
+ NotificationAdjustment onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
+ void onNotificationVisibilityChanged(String key, long time, boolean visible);
+ void onNotificationClick(String key, long time);
+ void onNotificationActionClick(String key, long time, int actionIndex);
+ void onNotificationRemoved(String key, long time, int reason);
+}
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/core/java/android/service/notification/NotificationAdjustment.aidl
new file mode 100644
index 0000000..805fe2c
--- /dev/null
+++ b/core/java/android/service/notification/NotificationAdjustment.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+parcelable NotificationAdjustment;
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationAdjustment.java b/core/java/android/service/notification/NotificationAdjustment.java
new file mode 100644
index 0000000..c5f0db9
--- /dev/null
+++ b/core/java/android/service/notification/NotificationAdjustment.java
@@ -0,0 +1,57 @@
+package android.service.notification;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class NotificationAdjustment implements Parcelable {
+ int mImportance;
+ CharSequence mExplanation;
+ Uri mReference;
+
+ /**
+ * Create a notification importance adjustment.
+ *
+ * @param importance The final importance of the notification.
+ * @param explanation A human-readable justification for the adjustment.
+ * @param reference A reference to an external object that augments the
+ * explanation, such as a
+ * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
+ * or null.
+ */
+ public NotificationAdjustment(int importance, CharSequence explanation, Uri reference) {
+ mImportance = importance;
+ mExplanation = explanation;
+ mReference = reference;
+ }
+
+ private NotificationAdjustment(Parcel source) {
+ this(source.readInt(), source.readCharSequence(),
+ (Uri) source.readParcelable(NotificationAdjustment.class.getClassLoader()));
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mImportance);
+ dest.writeCharSequence(mExplanation);
+ dest.writeParcelable(mReference, 0);
+ }
+
+ public static final Parcelable.Creator<NotificationAdjustment> CREATOR
+ = new Parcelable.Creator<NotificationAdjustment>() {
+ @Override
+ public NotificationAdjustment createFromParcel(Parcel source) {
+ return new NotificationAdjustment(source);
+ }
+
+ @Override
+ public NotificationAdjustment[] newArray(int size) {
+ return new NotificationAdjustment[size];
+ }
+ };
+}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 5d1317c..7ce5e1f 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -16,8 +16,12 @@
package android.service.notification;
+import android.annotation.SdkConstant;
import android.app.Notification;
+import android.content.Intent;
import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
/**
* A service that helps the user manage notifications by modifying the
@@ -35,6 +39,13 @@
* </service></pre>
*/
public abstract class NotificationAssistantService extends NotificationListenerService {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE
+ = "android.service.notification.NotificationAssistantService";
+
/** Notification was canceled by the status bar reporting a click. */
public static final int REASON_DELEGATE_CLICK = 1;
@@ -74,28 +85,6 @@
/** Notification was canceled because it was an invisible member of a group. */
public static final int REASON_GROUP_OPTIMIZATION = 13;
- public class Adjustment {
- int mImportance;
- CharSequence mExplanation;
- Uri mReference;
-
- /**
- * Create a notification importance adjustment.
- *
- * @param importance The final importance of the notification.
- * @param explanation A human-readable justification for the adjustment.
- * @param reference A reference to an external object that augments the
- * explanation, such as a
- * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
- * or null.
- */
- public Adjustment(int importance, CharSequence explanation, Uri reference) {
- mImportance = importance;
- mExplanation = explanation;
- mReference = reference;
- }
- }
-
/**
* A notification was posted by an app. Called before alert.
*
@@ -104,7 +93,7 @@
* @param user true if the initial importance reflects an explicit user preference.
* @return an adjustment or null to take no action, within 100ms.
*/
- abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
+ abstract public NotificationAdjustment onNotificationEnqueued(StatusBarNotification sbn,
int importance, boolean user);
/**
@@ -161,7 +150,7 @@
* @param key the notification key
* @param adjustment the new importance with an explanation
*/
- public final void adjustImportance(String key, Adjustment adjustment)
+ public final void adjustImportance(String key, NotificationAdjustment adjustment)
{
// TODO: pack up the adjustment and send it to the NotificationManager.
}
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index fd2d5b0..d8787b4 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -77,6 +77,15 @@
private Tile mTile;
private IBinder mToken;
+ @Override
+ public void onDestroy() {
+ if (mListening) {
+ onStopListening();
+ mListening = false;
+ }
+ super.onDestroy();
+ }
+
/**
* Called when the user adds this tile to Quick Settings.
* <p/>
@@ -197,10 +206,10 @@
mTile = (Tile) msg.obj;
break;
case MSG_TILE_ADDED:
- TileService.this.onTileRemoved();
+ TileService.this.onTileAdded();
break;
case MSG_TILE_REMOVED:
- TileService.this.onTileAdded();
+ TileService.this.onTileRemoved();
break;
case MSG_STOP_LISTENING:
if (mListening) {
diff --git a/core/java/android/util/StateSet.java b/core/java/android/util/StateSet.java
index c2a6a7a..051de8a 100644
--- a/core/java/android/util/StateSet.java
+++ b/core/java/android/util/StateSet.java
@@ -260,6 +260,12 @@
case R.attr.state_enabled:
sb.append("E ");
break;
+ case R.attr.state_checked:
+ sb.append("C ");
+ break;
+ case R.attr.state_activated:
+ sb.append("A ");
+ break;
}
}
diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java
index ea0873d..4888877 100644
--- a/core/java/android/view/ContextThemeWrapper.java
+++ b/core/java/android/view/ContextThemeWrapper.java
@@ -104,11 +104,15 @@
@Override
public AssetManager getAssets() {
// Ensure we're returning assets with the correct configuration.
- return getResources().getAssets();
+ return getResourcesInternal().getAssets();
}
@Override
public Resources getResources() {
+ return getResourcesInternal();
+ }
+
+ private Resources getResourcesInternal() {
if (mResources == null) {
if (mOverrideConfiguration == null) {
mResources = super.getResources();
@@ -117,7 +121,6 @@
mResources = resContext.getResources();
}
}
-
return mResources;
}
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index 34835f4..5903d4a 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -21,6 +21,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.view.IDropPermissions;
+
//TODO: Improve Javadoc
/**
* Represents an event that is sent out by the system at various times during a drag and drop
@@ -128,7 +130,7 @@
float mX, mY;
ClipDescription mClipDescription;
ClipData mClipData;
- DropPermissionHolder mDropPermissionHolder;
+ IDropPermissions mDropPermissions;
Object mLocalState;
boolean mDragResult;
@@ -146,7 +148,7 @@
* Action constant returned by {@link #getAction()}: Signals the start of a
* drag and drop operation. The View should return {@code true} from its
* {@link View#onDragEvent(DragEvent) onDragEvent()} handler method or
- * {@link View.View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()} listener
+ * {@link View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()} listener
* if it can accept a drop. The onDragEvent() or onDrag() methods usually inspect the metadata
* from {@link #getClipDescription()} to determine if they can accept the data contained in
* this drag. For an operation that doesn't represent data transfer, these methods may
@@ -190,7 +192,7 @@
* within the View object's bounding box.
* <p>
* The View should return {@code true} from its {@link View#onDragEvent(DragEvent)}
- * handler or {@link View.View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()}
+ * handler or {@link View.OnDragListener#onDrag(View,DragEvent) OnDragListener.onDrag()}
* listener if it accepted the drop, and {@code false} if it ignored the drop.
* </p>
* <p>
@@ -255,13 +257,13 @@
}
private void init(int action, float x, float y, ClipDescription description, ClipData data,
- DropPermissionHolder dropPermissionHolder, Object localState, boolean result) {
+ IDropPermissions dropPermissions, Object localState, boolean result) {
mAction = action;
mX = x;
mY = y;
mClipDescription = description;
mClipData = data;
- mDropPermissionHolder = dropPermissionHolder;
+ mDropPermissions = dropPermissions;
mLocalState = localState;
mDragResult = result;
}
@@ -272,13 +274,13 @@
/** @hide */
public static DragEvent obtain(int action, float x, float y, Object localState,
- ClipDescription description, ClipData data, DropPermissionHolder dropPermissionHolder,
+ ClipDescription description, ClipData data, IDropPermissions dropPermissions,
boolean result) {
final DragEvent ev;
synchronized (gRecyclerLock) {
if (gRecyclerTop == null) {
ev = new DragEvent();
- ev.init(action, x, y, description, data, dropPermissionHolder, localState, result);
+ ev.init(action, x, y, description, data, dropPermissions, localState, result);
return ev;
}
ev = gRecyclerTop;
@@ -289,7 +291,7 @@
ev.mRecycled = false;
ev.mNext = null;
- ev.init(action, x, y, description, data, dropPermissionHolder, localState, result);
+ ev.init(action, x, y, description, data, dropPermissions, localState, result);
return ev;
}
@@ -297,7 +299,7 @@
/** @hide */
public static DragEvent obtain(DragEvent source) {
return obtain(source.mAction, source.mX, source.mY, source.mLocalState,
- source.mClipDescription, source.mClipData, source.mDropPermissionHolder,
+ source.mClipDescription, source.mClipData, source.mDropPermissions,
source.mDragResult);
}
@@ -363,14 +365,19 @@
}
/**
- * Returns the {@link android.view.DropPermissionHolder} object that can be used by the drag
- * listener to request and release the permissions for the content URIs contained in the
- * {@link android.content.ClipData} object associated with this event.
+ * Requests the permissions for the content URIs contained in {@link android.content.ClipData}
+ * object associated with this event. Which permissions will be granted is defined by the set of
+ * flags passed to {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)}.
+ * Returns the {@link DropPermissions} object that can be used by the receiving app to release
+ * the permissions for the content URIs when they are no longer needed.
* This method only returns valid data if the event action is {@link #ACTION_DROP}.
- * @return The DropPermissionHolder object used to handle content URI permissions.
+ * @return The DropPermissions object used to control access to the content URIs.
*/
- public DropPermissionHolder getDropPermissionHolder() {
- return mDropPermissionHolder;
+ public DropPermissions requestDropPermissions() {
+ if (mDropPermissions == null) {
+ return null;
+ }
+ return new DropPermissions(mDropPermissions);
}
/**
@@ -493,11 +500,11 @@
dest.writeInt(1);
mClipDescription.writeToParcel(dest, flags);
}
- if (mDropPermissionHolder == null) {
+ if (mDropPermissions == null) {
dest.writeInt(0);
} else {
dest.writeInt(1);
- mDropPermissionHolder.writeToParcel(dest, flags);
+ dest.writeStrongBinder(mDropPermissions.asBinder());
}
}
@@ -519,7 +526,7 @@
event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in);
}
if (in.readInt() != 0) {
- event.mDropPermissionHolder = DropPermissionHolder.CREATOR.createFromParcel(in);
+ event.mDropPermissions = IDropPermissions.Stub.asInterface(in.readStrongBinder());;
}
return event;
}
diff --git a/core/java/android/view/DropPermissionHolder.java b/core/java/android/view/DropPermissionHolder.java
deleted file mode 100644
index 993e67a..0000000
--- a/core/java/android/view/DropPermissionHolder.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package android.view;
-
-import android.app.IActivityManager;
-import android.content.ClipData;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import com.android.internal.view.IDropPermissionHolder;
-
-import java.util.ArrayList;
-
-public class DropPermissionHolder implements Parcelable {
-
- IDropPermissionHolder mDropPermissionHolder;
-
- /**
- * Create a new DropPermissionHolder to be passed to the client with a DragEvent.
- *
- * @hide
- */
- public DropPermissionHolder(ClipData clipData, IActivityManager activityManager,
- int sourceUid, String targetPackage, int mode, int sourceUserId, int targetUserId) {
- mDropPermissionHolder = new LocalDropPermissionHolder(clipData, activityManager,
- sourceUid, targetPackage, mode, sourceUserId, targetUserId);
- }
-
- private class LocalDropPermissionHolder extends IDropPermissionHolder.Stub {
-
- private final IActivityManager mActivityManager;
- private final int mSourceUid;
- private final String mTargetPackage;
- private final int mMode;
- private final int mSourceUserId;
- private final int mTargetUserId;
-
- IBinder mPermissionOwner = null;
-
- final private ArrayList<Uri> mUris = new ArrayList<Uri>();
-
- LocalDropPermissionHolder(ClipData clipData, IActivityManager activityManager,
- int sourceUid, String targetPackage, int mode, int sourceUserId, int targetUserId) {
- mActivityManager = activityManager;
- mSourceUid = sourceUid;
- mTargetPackage = targetPackage;
- mMode = mode;
- mSourceUserId = sourceUserId;
- mTargetUserId = targetUserId;
-
- int N = clipData.getItemCount();
- for (int i = 0; i != N; ++i) {
- ClipData.Item item = clipData.getItemAt(i);
-
- if (item.getUri() != null) {
- mUris.add(item.getUri());
- }
-
- Intent intent = item.getIntent();
- if (intent != null && intent.getData() != null) {
- mUris.add(intent.getData());
- }
- }
- }
-
- @Override
- public void grant() throws RemoteException {
- if (mPermissionOwner != null) {
- return;
- }
-
- mPermissionOwner = mActivityManager.newUriPermissionOwner("drop");
-
- long origId = Binder.clearCallingIdentity();
- try {
- for (Uri mUri : mUris) {
- mActivityManager.grantUriPermissionFromOwner(
- mPermissionOwner, mSourceUid, mTargetPackage, mUri, mMode,
- mSourceUserId, mTargetUserId);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
-
- }
-
- @Override
- public void revoke() throws RemoteException {
- if (mPermissionOwner == null) {
- return;
- }
-
- for (Uri mUri : mUris) {
- mActivityManager.revokeUriPermissionFromOwner(
- mPermissionOwner, mUri, mMode, mSourceUserId);
- }
-
- mPermissionOwner = null;
- }
- }
-
- /**
- * Request permissions granted by the activity which started the drag.
- */
- public void grant() {
- try {
- mDropPermissionHolder.grant();
- } catch (RemoteException e) {
- }
- }
-
- /**
- * Revoke permissions granted by the {@link #grant()} call.
- */
- public void revoke() {
- try {
- mDropPermissionHolder.revoke();
- } catch (RemoteException e) {
- }
- }
-
- /**
- * Returns information about the {@link android.os.Parcel} representation of this
- * DropPermissionHolder object.
- * @return Information about the {@link android.os.Parcel} representation.
- */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Creates a {@link android.os.Parcel} object from this DropPermissionHolder object.
- * @param dest A {@link android.os.Parcel} object in which to put the DropPermissionHolder
- * object.
- * @param flags Flags to store in the Parcel.
- */
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeStrongBinder(mDropPermissionHolder.asBinder());
- }
-
- DropPermissionHolder(Parcel in) {
- mDropPermissionHolder = IDropPermissionHolder.Stub.asInterface(in.readStrongBinder());
- }
-
- /**
- * A container for creating a DropPermissionHolder from a Parcel.
- */
- public static final Parcelable.Creator<DropPermissionHolder> CREATOR
- = new Parcelable.Creator<DropPermissionHolder>() {
- public DropPermissionHolder createFromParcel(Parcel in) {
- return new DropPermissionHolder(in);
- }
- public DropPermissionHolder[] newArray(int size) {
- return new DropPermissionHolder[size];
- }
- };
-}
diff --git a/core/java/android/view/DropPermissions.java b/core/java/android/view/DropPermissions.java
new file mode 100644
index 0000000..780461f
--- /dev/null
+++ b/core/java/android/view/DropPermissions.java
@@ -0,0 +1,66 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.view;
+
+import android.os.RemoteException;
+import com.android.internal.view.IDropPermissions;
+import dalvik.system.CloseGuard;
+
+
+public final class DropPermissions {
+
+ private final IDropPermissions mDropPermissions;
+
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ /**
+ * Create a new DropPermissions object to be passed to the client with a DragEvent.
+ *
+ * @hide
+ */
+ DropPermissions(IDropPermissions dropPermissions) {
+ mDropPermissions = dropPermissions;
+ try {
+ mDropPermissions.take();
+ } catch (RemoteException e) {
+ }
+ mCloseGuard.open("release");
+ }
+
+ /**
+ * Revoke permissions taken by {@link DragEvent#requestDropPermissions()}.
+ */
+ public void release() {
+ try {
+ mDropPermissions.release();
+ } catch (RemoteException e) {
+ }
+ mCloseGuard.close();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ release();
+ } finally {
+ super.finalize();
+ }
+ }
+}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 01d1566..6aa5e2f 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -134,6 +134,25 @@
private static String TAG_SIGNATURE = "signature";
/**
+ * Reads all signatures at the current depth (within the current provider) from the XML parser.
+ */
+ private static String[] readSignatures(XmlResourceParser parser) throws IOException,
+ XmlPullParserException {
+ List<String> signatures = new ArrayList<String>();
+ int outerDepth = parser.getDepth();
+ while(XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (parser.getName().equals(TAG_SIGNATURE)) {
+ // Parse the value within the signature tag
+ String signature = parser.nextText();
+ signatures.add(signature);
+ } else {
+ Log.e(LOGTAG, "Found an element in a webview provider that is not a signature");
+ }
+ }
+ return signatures.toArray(new String[signatures.size()]);
+ }
+
+ /**
* Returns all packages declared in the framework resources as potential WebView providers.
* @hide
* */
@@ -161,9 +180,9 @@
throw new MissingWebViewPackageException(
"WebView provider in framework resources missing description");
}
- String signature = parser.getAttributeValue(null, TAG_SIGNATURE);
webViewProviders.add(
- new WebViewProviderInfo(packageName, description, signature));
+ new WebViewProviderInfo(packageName, description,
+ readSignatures(parser)));
}
else {
Log.e(LOGTAG, "Found an element that is not a webview provider");
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index d5e3a230..7bad652 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -40,10 +40,10 @@
public WebViewPackageNotFoundException(Exception e) { super(e); }
}
- public WebViewProviderInfo(String packageName, String description, String signature) {
+ public WebViewProviderInfo(String packageName, String description, String[] signatures) {
this.packageName = packageName;
this.description = description;
- this.signature = signature;
+ this.signatures = signatures;
}
private boolean hasValidSignature() {
@@ -53,7 +53,7 @@
try {
// If no signature is declared, instead check whether the package is included in the
// system.
- if (signature == null)
+ if (signatures == null || signatures.length == 0)
return getPackageInfo().applicationInfo.isSystemApp();
packageSignatures = getPackageInfo().signatures;
@@ -62,8 +62,15 @@
}
if (packageSignatures.length != 1)
return false;
- final byte[] releaseSignature = Base64.decode(signature, Base64.DEFAULT);
- return Arrays.equals(releaseSignature, packageSignatures[0].toByteArray());
+
+ final byte[] packageSignature = packageSignatures[0].toByteArray();
+ // Return whether the package signature matches any of the valid signatures
+ for (String signature : signatures) {
+ final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
+ if (Arrays.equals(packageSignature, validSignature))
+ return true;
+ }
+ return false;
}
/**
@@ -109,7 +116,7 @@
private WebViewProviderInfo(Parcel in) {
packageName = in.readString();
description = in.readString();
- signature = in.readString();
+ signatures = in.createStringArray();
packageInfo = null;
}
@@ -122,14 +129,14 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(packageName);
out.writeString(description);
- out.writeString(signature);
+ out.writeStringArray(signatures);
}
// fields read from framework resource
public String packageName;
public String description;
- private String signature;
+ private String[] signatures;
private PackageInfo packageInfo;
// flags declaring we want extra info from the package manager
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 2d1f855..15cea77 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -72,6 +72,7 @@
import android.util.SparseArray;
import android.view.ActionMode;
import android.view.ActionMode.Callback;
+import android.view.ContextMenu;
import android.view.DisplayListCanvas;
import android.view.DragEvent;
import android.view.Gravity;
@@ -135,13 +136,16 @@
// Tag used when the Editor maintains its own separate UndoManager.
private static final String UNDO_OWNER_TAG = "Editor";
- // Ordering constants used to place the Action Mode items in their menu.
- private static final int MENU_ITEM_ORDER_CUT = 1;
- private static final int MENU_ITEM_ORDER_COPY = 2;
- private static final int MENU_ITEM_ORDER_PASTE = 3;
- private static final int MENU_ITEM_ORDER_SHARE = 4;
- private static final int MENU_ITEM_ORDER_SELECT_ALL = 5;
- private static final int MENU_ITEM_ORDER_REPLACE = 6;
+ // Ordering constants used to place the Action Mode or context menu items in their menu.
+ private static final int MENU_ITEM_ORDER_UNDO = 1;
+ private static final int MENU_ITEM_ORDER_REDO = 2;
+ private static final int MENU_ITEM_ORDER_CUT = 3;
+ private static final int MENU_ITEM_ORDER_COPY = 4;
+ private static final int MENU_ITEM_ORDER_PASTE = 5;
+ private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 6;
+ private static final int MENU_ITEM_ORDER_SHARE = 7;
+ private static final int MENU_ITEM_ORDER_SELECT_ALL = 8;
+ private static final int MENU_ITEM_ORDER_REPLACE = 9;
private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 10;
// Each Editor manages its own undo stack.
@@ -184,6 +188,7 @@
boolean mDiscardNextActionUp;
boolean mIgnoreActionUpEvent;
+ private boolean mIgnoreNextMouseActionUpOrDown;
long mShowCursor;
Blink mBlink;
@@ -209,6 +214,8 @@
boolean mPreserveDetachedSelection;
boolean mTemporaryDetach;
+ boolean mIsBeingLongClicked;
+
SuggestionsPopupWindow mSuggestionsPopupWindow;
SuggestionRangeSpan mSuggestionRangeSpan;
Runnable mShowSuggestionRunnable;
@@ -224,6 +231,7 @@
private PositionListener mPositionListener;
float mLastDownPositionX, mLastDownPositionY;
+ private float mContextMenuAnchorX, mContextMenuAnchorY;
Callback mCustomSelectionActionModeCallback;
Callback mCustomInsertionActionModeCallback;
@@ -239,6 +247,9 @@
// Only for mouse input.
private static final int TAP_STATE_TRIPLE_CLICK = 3;
+ // The button state as of the last time #onTouchEvent is called.
+ private int mLastButtonState;
+
private Runnable mInsertionActionModeRunnable;
// The span controller helps monitoring the changes to which the Editor needs to react:
@@ -1314,7 +1325,33 @@
}
}
+ private boolean shouldFilterOutTouchEvent(MotionEvent event) {
+ if (!event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ return false;
+ }
+ final boolean primaryButtonStateChanged =
+ ((mLastButtonState ^ event.getButtonState()) & MotionEvent.BUTTON_PRIMARY) != 0;
+ final int action = event.getActionMasked();
+ if ((action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP)
+ && !primaryButtonStateChanged) {
+ return true;
+ }
+ if (action == MotionEvent.ACTION_MOVE
+ && !event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+ return true;
+ }
+ return false;
+ }
+
void onTouchEvent(MotionEvent event) {
+ final boolean filterOutEvent = shouldFilterOutTouchEvent(event);
+ mLastButtonState = event.getButtonState();
+ if (filterOutEvent) {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mDiscardNextActionUp = true;
+ }
+ return;
+ }
updateTapState(event);
updateFloatingToolbarVisibility(event);
@@ -2318,6 +2355,84 @@
text.setSpan(mSpanController, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
+ void setContextMenuAnchor(float x, float y) {
+ mContextMenuAnchorX = x;
+ mContextMenuAnchorY = y;
+ }
+
+ void onCreateContextMenu(ContextMenu menu) {
+ if (mIsBeingLongClicked || Float.isNaN(mContextMenuAnchorX)
+ || Float.isNaN(mContextMenuAnchorY)) {
+ return;
+ }
+ final int offset = mTextView.getOffsetForPosition(mContextMenuAnchorX, mContextMenuAnchorY);
+ if (offset == -1) {
+ return;
+ }
+ final boolean isOnSelection = mTextView.hasSelection()
+ && offset >= mTextView.getSelectionStart() && offset <= mTextView.getSelectionEnd();
+ if (!isOnSelection) {
+ // Right clicked position is not on the selection. Remove the selection and move the
+ // cursor to the right clicked position.
+ stopTextActionMode();
+ Selection.setSelection((Spannable) mTextView.getText(), offset);
+ }
+
+ // TODO: Add suggestions in the context menu.
+
+ menu.add(Menu.NONE, TextView.ID_UNDO, MENU_ITEM_ORDER_UNDO,
+ com.android.internal.R.string.undo)
+ .setAlphabeticShortcut('z')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setEnabled(mTextView.canUndo());
+ menu.add(Menu.NONE, TextView.ID_REDO, MENU_ITEM_ORDER_REDO,
+ com.android.internal.R.string.redo)
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setEnabled(mTextView.canRedo());
+
+ menu.add(Menu.NONE, TextView.ID_CUT, MENU_ITEM_ORDER_CUT,
+ com.android.internal.R.string.cut)
+ .setAlphabeticShortcut('x')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setEnabled(mTextView.canCut());
+ menu.add(Menu.NONE, TextView.ID_COPY, MENU_ITEM_ORDER_COPY,
+ com.android.internal.R.string.copy)
+ .setAlphabeticShortcut('c')
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+ .setEnabled(mTextView.canCopy());
+ menu.add(Menu.NONE, TextView.ID_PASTE, MENU_ITEM_ORDER_PASTE,
+ com.android.internal.R.string.paste)
+ .setAlphabeticShortcut('v')
+ .setEnabled(mTextView.canPaste())
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ menu.add(Menu.NONE, TextView.ID_PASTE, MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT,
+ com.android.internal.R.string.paste_as_plain_text)
+ .setEnabled(mTextView.canPaste())
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ menu.add(Menu.NONE, TextView.ID_SHARE, MENU_ITEM_ORDER_SHARE,
+ com.android.internal.R.string.share)
+ .setEnabled(mTextView.canShare())
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+ menu.add(Menu.NONE, TextView.ID_SELECT_ALL, MENU_ITEM_ORDER_SELECT_ALL,
+ com.android.internal.R.string.selectAll)
+ .setAlphabeticShortcut('a')
+ .setEnabled(mTextView.canSelectAllText())
+ .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+
+ mPreserveDetachedSelection = true;
+ }
+
+ private final MenuItem.OnMenuItemClickListener mOnContextMenuItemClickListener =
+ new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
+ return true;
+ }
+ return mTextView.onTextContextMenuItem(item.getItemId());
+ }
+ };
+
/**
* Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
* pop-up should be displayed.
@@ -2710,6 +2825,9 @@
}
public void hide() {
+ if (!isShowing()) {
+ return;
+ }
mPopupWindow.dismiss();
getPositionListener().removeSubscriber(this);
}
@@ -2759,8 +2877,10 @@
@Override
public void dismiss() {
+ if (!isShowing()) {
+ return;
+ }
super.dismiss();
-
getPositionListener().removeSubscriber(SuggestionsPopupWindow.this);
// Safe cast since show() checks that mTextView.getText() is an Editable
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index ad939be..f6e6186 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -359,7 +359,6 @@
final int count = getVirtualChildCount();
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
-
if (child != null && child.getVisibility() != GONE) {
if (hasDividerBeforeChildAt(i)) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
@@ -388,7 +387,7 @@
*/
private View getLastNonGoneChild() {
for (int i = getVirtualChildCount() - 1; i >= 0; i--) {
- View child = getVirtualChildAt(i);
+ final View child = getVirtualChildAt(i);
if (child != null && child.getVisibility() != GONE) {
return child;
}
@@ -401,7 +400,6 @@
final boolean isLayoutRtl = isLayoutRtl();
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
-
if (child != null && child.getVisibility() != GONE) {
if (hasDividerBeforeChildAt(i)) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
@@ -588,8 +586,9 @@
* for an example.</p>
*
* @param index the child's index
- * @return the child at the specified index
+ * @return the child at the specified index, may be {@code null}
*/
+ @Nullable
View getVirtualChildAt(int index) {
return getChildAt(index);
}
@@ -670,7 +669,7 @@
*/
private boolean allViewsAreGoneBefore(int childIndex) {
for (int i = childIndex - 1; i >= 0; i--) {
- View child = getVirtualChildAt(i);
+ final View child = getVirtualChildAt(i);
if (child != null && child.getVisibility() != GONE) {
return false;
}
@@ -715,7 +714,6 @@
// See how tall everyone is. Also remember max width.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
-
if (child == null) {
mTotalLength += measureNullChild(i);
continue;
@@ -837,7 +835,6 @@
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
-
if (child == null) {
mTotalLength += measureNullChild(i);
continue;
@@ -943,7 +940,6 @@
if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
-
if (child == null || child.getVisibility() == View.GONE) {
continue;
}
@@ -986,7 +982,7 @@
MeasureSpec.EXACTLY);
for (int i = 0; i< count; ++i) {
final View child = getVirtualChildAt(i);
- if (child.getVisibility() != GONE) {
+ if (child != null && child.getVisibility() != GONE) {
LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams());
if (lp.width == LayoutParams.MATCH_PARENT) {
@@ -1053,7 +1049,6 @@
// See how wide everyone is. Also remember max height.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
-
if (child == null) {
mTotalLength += measureNullChild(i);
continue;
@@ -1211,7 +1206,6 @@
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
-
if (child == null) {
mTotalLength += measureNullChild(i);
continue;
@@ -1357,7 +1351,6 @@
if (useLargestChild && widthMode != MeasureSpec.EXACTLY) {
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
-
if (child == null || child.getVisibility() == View.GONE) {
continue;
}
@@ -1402,7 +1395,7 @@
MeasureSpec.EXACTLY);
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
- if (child.getVisibility() != GONE) {
+ if (child != null && child.getVisibility() != GONE) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
if (lp.height == LayoutParams.MATCH_PARENT) {
@@ -1662,9 +1655,8 @@
}
for (int i = 0; i < count; i++) {
- int childIndex = start + dir * i;
+ final int childIndex = start + dir * i;
final View child = getVirtualChildAt(childIndex);
-
if (child == null) {
childLeft += measureNullChild(childIndex);
} else if (child.getVisibility() != GONE) {
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index f7f9c91..22931fc 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -98,7 +98,7 @@
* {@hide}
*/
void setColumnCollapsed(int columnIndex, boolean collapsed) {
- View child = getVirtualChildAt(columnIndex);
+ final View child = getVirtualChildAt(columnIndex);
if (child != null) {
child.setVisibility(collapsed ? GONE : VISIBLE);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 17c803f..d46c6f9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -111,6 +111,7 @@
import android.view.AccessibilityIterators.TextSegmentIterator;
import android.view.ActionMode;
import android.view.Choreographer;
+import android.view.ContextMenu;
import android.view.DragEvent;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -393,7 +394,17 @@
mOverride = false;
}
- public void resolveWithLayoutDirection(int layoutDirection) {
+ /**
+ * Updates the list of displayed drawables to account for the current
+ * layout direction.
+ *
+ * @param layoutDirection the current layout direction
+ * @return {@code true} if the displayed drawables changed
+ */
+ public boolean resolveWithLayoutDirection(int layoutDirection) {
+ final Drawable previousLeft = mShowing[Drawables.LEFT];
+ final Drawable previousRight = mShowing[Drawables.RIGHT];
+
// First reset "left" and "right" drawables to their initial values
mShowing[Drawables.LEFT] = mDrawableLeftInitial;
mShowing[Drawables.RIGHT] = mDrawableRightInitial;
@@ -441,16 +452,11 @@
break;
}
}
- applyErrorDrawableIfNeeded(layoutDirection);
- updateDrawablesLayoutDirection(layoutDirection);
- }
- private void updateDrawablesLayoutDirection(int layoutDirection) {
- for (Drawable dr : mShowing) {
- if (dr != null) {
- dr.setLayoutDirection(layoutDirection);
- }
- }
+ applyErrorDrawableIfNeeded(layoutDirection);
+
+ return mShowing[Drawables.LEFT] != previousLeft
+ || mShowing[Drawables.RIGHT] != previousRight;
}
public void setErrorDrawable(Drawable dr, TextView tv) {
@@ -5957,6 +5963,14 @@
@Override
public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
+ if (mText instanceof Spannable && mLinksClickable) {
+ final int offset = getOffsetForPosition(x, y);
+ final ClickableSpan[] clickables = ((Spannable) mText).getSpans(offset, offset,
+ ClickableSpan.class);
+ if (clickables.length > 0) {
+ return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_HAND);
+ }
+ }
if (isTextSelectable() || isTextEditable()) {
return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_TEXT);
}
@@ -8489,6 +8503,29 @@
return super.onGenericMotionEvent(event);
}
+ @Override
+ protected void onCreateContextMenu(ContextMenu menu) {
+ if (mEditor != null) {
+ mEditor.onCreateContextMenu(menu);
+ }
+ }
+
+ @Override
+ public boolean showContextMenu() {
+ if (mEditor != null) {
+ mEditor.setContextMenuAnchor(Float.NaN, Float.NaN);
+ }
+ return super.showContextMenu();
+ }
+
+ @Override
+ public boolean showContextMenu(float x, float y) {
+ if (mEditor != null) {
+ mEditor.setContextMenuAnchor(x, y);
+ }
+ return super.showContextMenu(x, y);
+ }
+
/**
* @return True iff this TextView contains a text that can be edited, or if this is
* a selectable TextView.
@@ -9390,12 +9427,17 @@
public boolean performLongClick() {
boolean handled = false;
+ if (mEditor != null) {
+ mEditor.mIsBeingLongClicked = true;
+ }
+
if (super.performLongClick()) {
handled = true;
}
if (mEditor != null) {
handled |= mEditor.performLongClick(handled);
+ mEditor.mIsBeingLongClicked = false;
}
if (handled) {
@@ -9809,7 +9851,30 @@
// Resolve drawables
if (mDrawables != null) {
- mDrawables.resolveWithLayoutDirection(layoutDirection);
+ if (mDrawables.resolveWithLayoutDirection(layoutDirection)) {
+ prepareDrawableForDisplay(mDrawables.mShowing[Drawables.LEFT]);
+ prepareDrawableForDisplay(mDrawables.mShowing[Drawables.RIGHT]);
+ applyCompoundDrawableTint();
+ }
+ }
+ }
+
+ /**
+ * Prepares a drawable for display by propagating layout direction and
+ * drawable state.
+ *
+ * @param dr the drawable to prepare
+ */
+ private void prepareDrawableForDisplay(@Nullable Drawable dr) {
+ if (dr == null) {
+ return;
+ }
+
+ dr.setLayoutDirection(getLayoutDirection());
+
+ if (dr.isStateful()) {
+ dr.setState(getDrawableState());
+ dr.jumpToCurrentState();
}
}
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 66374a6..27c3b72 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -378,6 +378,7 @@
if (mIconView != null) {
if (icon != null) {
+ mIconView.setVisibility(View.VISIBLE);
mIconView.setImageDrawable(icon);
} else {
mIconView.setVisibility(View.GONE);
diff --git a/core/java/com/android/internal/view/IDropPermissionHolder.aidl b/core/java/com/android/internal/view/IDropPermissions.aidl
similarity index 91%
rename from core/java/com/android/internal/view/IDropPermissionHolder.aidl
rename to core/java/com/android/internal/view/IDropPermissions.aidl
index e60ab0e..86d27e7 100644
--- a/core/java/com/android/internal/view/IDropPermissionHolder.aidl
+++ b/core/java/com/android/internal/view/IDropPermissions.aidl
@@ -20,7 +20,7 @@
* Interface to allow a drop receiver to request permissions for URIs passed along with ClipData
* contained in DragEvent.
*/
-interface IDropPermissionHolder {
- void grant();
- void revoke();
+interface IDropPermissions {
+ void take();
+ void release();
}
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 88b3769..4a0f3fc 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -265,7 +265,7 @@
if (options != NULL) {
jstring mimeType = encodedFormatToString(env, codec->getEncodedFormat());
if (env->ExceptionCheck()) {
- return nullObjectReturn("OOM in getEncodedFormat()");
+ return nullObjectReturn("OOM in encodedFormatToString()");
}
env->SetIntField(options, gOptions_widthFieldID, size.width());
env->SetIntField(options, gOptions_heightFieldID, size.height());
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 492d766..a1ba42e 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -136,9 +136,6 @@
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
- if (kAlpha_8_SkColorType == colorType) {
- colorType = kGray_8_SkColorType;
- }
requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
// The Java options of ditherMode and preferQualityOverSpeed are deprecated. We will
@@ -189,6 +186,9 @@
env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
env->SetObjectField(options, gOptions_mimeFieldID,
encodedFormatToString(env, brd->getEncodedFormat()));
+ if (env->ExceptionCheck()) {
+ return nullObjectReturn("OOM in encodedFormatToString()");
+ }
}
// If we may have reused a bitmap, we need to indicate that the pixels have changed.
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 9b41eb3..d2c99fd 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -27,7 +27,13 @@
#include <SkBitmap.h>
#include <SkRegion.h>
+#if HWUI_NEW_OPS
+#include <RecordingCanvas.h>
+typedef android::uirenderer::RecordingCanvas canvas_t;
+#else
#include <DisplayListCanvas.h>
+typedef android::uirenderer::DisplayListCanvas canvas_t;
+#endif
#include <Rect.h>
#include <RenderNode.h>
#include <CanvasProperty.h>
@@ -46,7 +52,7 @@
static void android_view_DisplayListCanvas_insertReorderBarrier(JNIEnv* env, jobject clazz,
jlong canvasPtr, jboolean reorderEnable) {
- DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+ canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
canvas->insertReorderBarrier(reorderEnable);
}
@@ -56,7 +62,7 @@
static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
jlong canvasPtr, jlong functorPtr) {
- DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+ canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
Functor* functor = reinterpret_cast<Functor*>(functorPtr);
canvas->callDrawGLFunction(functor);
}
@@ -80,7 +86,7 @@
static void android_view_DisplayListCanvas_drawRoundRectProps(JNIEnv* env, jobject clazz,
jlong canvasPtr, jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr,
jlong bottomPropPtr, jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) {
- DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+ canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
CanvasPropertyPrimitive* leftProp = reinterpret_cast<CanvasPropertyPrimitive*>(leftPropPtr);
CanvasPropertyPrimitive* topProp = reinterpret_cast<CanvasPropertyPrimitive*>(topPropPtr);
CanvasPropertyPrimitive* rightProp = reinterpret_cast<CanvasPropertyPrimitive*>(rightPropPtr);
@@ -93,7 +99,7 @@
static void android_view_DisplayListCanvas_drawCircleProps(JNIEnv* env, jobject clazz,
jlong canvasPtr, jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) {
- DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+ canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr);
@@ -107,25 +113,25 @@
static jlong android_view_DisplayListCanvas_finishRecording(JNIEnv* env,
jobject clazz, jlong canvasPtr) {
- DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+ canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
return reinterpret_cast<jlong>(canvas->finishRecording());
}
static jlong android_view_DisplayListCanvas_createDisplayListCanvas(JNIEnv* env, jobject clazz,
jint width, jint height) {
- return reinterpret_cast<jlong>(new DisplayListCanvas(width, height));
+ return reinterpret_cast<jlong>(new canvas_t(width, height));
}
static void android_view_DisplayListCanvas_resetDisplayListCanvas(JNIEnv* env, jobject clazz,
jlong canvasPtr, jint width, jint height) {
- DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+ canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
canvas->reset(width, height);
}
static void android_view_DisplayListCanvas_drawRenderNode(JNIEnv* env,
jobject clazz, jlong canvasPtr, jlong renderNodePtr) {
- DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+ canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
canvas->drawRenderNode(renderNode);
}
@@ -136,7 +142,7 @@
static void android_view_DisplayListCanvas_drawLayer(JNIEnv* env, jobject clazz,
jlong canvasPtr, jlong layerPtr) {
- DisplayListCanvas* canvas = reinterpret_cast<DisplayListCanvas*>(canvasPtr);
+ canvas_t* canvas = reinterpret_cast<canvas_t*>(canvasPtr);
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
canvas->drawLayer(layer);
}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4d967ff..4843879 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2453,6 +2453,9 @@
<!-- Item on EditText context menu. This action is used to paste from the clipboard into the eidt field -->
<string name="paste">Paste</string>
+ <!-- Item on EditText context menu. This action is used to paste from the clipboard into the eidt field without formatting -->
+ <string name="paste_as_plain_text">Paste as plain text</string>
+
<!-- Item on EditText context menu. This action is used to replace the current word by other suggested words, suggested by the IME or the spell checker -->
<string name="replace">Replace\u2026</string>
@@ -2465,6 +2468,12 @@
<!-- Item on EditText context menu. Added only when the context menu is not empty, it enable selection context mode. [CHAR LIMIT=20] -->
<string name="selectTextMode">Select text</string>
+ <!-- Item on EditText context menu. This action is used to undo a text edit operation. -->
+ <string name="undo">Undo</string>
+
+ <!-- Item on EditText context menu. This action is used to redo a text edit operation. -->
+ <string name="redo">Redo</string>
+
<!-- Text selection contextual mode title, displayed in the CAB. [CHAR LIMIT=20] -->
<string name="textSelectionCABTitle">Text selection</string>
@@ -3069,6 +3078,9 @@
<string name="notification_listener_binding_label">Notification listener</string>
<!-- Label to show for a service that is running because it is providing conditions. -->
<string name="condition_provider_service_binding_label">Condition provider</string>
+ <!-- Label to show for a service that is running because it is observing and modifying the
+ importance of the user's notifications. -->
+ <string name="notification_assistant_binding_label">Notification assistant</string>
<!-- Do Not Translate: Alternate eri.xml -->
<string name="alternate_eri_file">/data/eri.xml</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 9a4016b..937d83d 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1235,7 +1235,7 @@
<item name="subtitleTextAppearance">@style/TextAppearance.Widget.Toolbar.Subtitle</item>
<item name="minHeight">?attr/actionBarSize</item>
<item name="titleMargin">4dp</item>
- <item name="maxButtonHeight">56dp</item>
+ <item name="maxButtonHeight">@dimen/action_bar_default_height_material</item>
<item name="buttonGravity">top</item>
<item name="navigationButtonStyle">@style/Widget.Toolbar.Button.Navigation</item>
<item name="collapseIcon">?attr/homeAsUpIndicator</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 517bb75..f257f14 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -456,7 +456,10 @@
<java-symbol type="string" name="notification_title" />
<java-symbol type="string" name="permission_request_notification_with_subtitle" />
<java-symbol type="string" name="prepend_shortcut_label" />
+ <java-symbol type="string" name="paste_as_plain_text" />
<java-symbol type="string" name="replace" />
+ <java-symbol type="string" name="undo" />
+ <java-symbol type="string" name="redo" />
<java-symbol type="string" name="textSelectionCABTitle" />
<java-symbol type="string" name="BaMmi" />
<java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
@@ -1807,6 +1810,7 @@
<java-symbol type="string" name="low_internal_storage_view_title" />
<java-symbol type="string" name="notification_listener_binding_label" />
<java-symbol type="string" name="condition_provider_service_binding_label" />
+ <java-symbol type="string" name="notification_assistant_binding_label" />
<java-symbol type="string" name="report" />
<java-symbol type="string" name="select_input_method" />
<java-symbol type="string" name="select_keyboard_layout_notification_title" />
diff --git a/core/res/res/xml/config_webview_packages.xml b/core/res/res/xml/config_webview_packages.xml
index 6f9c58d..fd443c1 100644
--- a/core/res/res/xml/config_webview_packages.xml
+++ b/core/res/res/xml/config_webview_packages.xml
@@ -16,5 +16,6 @@
<webviewproviders>
<!-- The default WebView implementation -->
- <webviewprovider description="Android WebView" packageName="com.android.webview" />
+ <webviewprovider description="Android WebView" packageName="com.android.webview">
+ </webviewprovider>
</webviewproviders>
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index 83a9e01..afd0bc4 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -26,8 +26,11 @@
import static android.widget.espresso.TextViewActions.mouseLongClickAndDragOnText;
import static android.widget.espresso.TextViewActions.mouseTripleClickAndDragOnText;
import static android.widget.espresso.TextViewActions.mouseTripleClickOnTextAtIndex;
+import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
import static android.widget.espresso.TextViewAssertions.hasSelection;
+
import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.replaceText;
import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
@@ -37,8 +40,11 @@
import com.android.frameworks.coretests.R;
+import android.support.test.espresso.Espresso;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
+import android.view.MotionEvent;
+import android.widget.espresso.ContextMenuUtils;
/**
* Tests mouse interaction of the TextView widget from an Activity
@@ -49,10 +55,13 @@
super(TextViewActivity.class);
}
+ @Override
+ public void setUp() {
+ getActivity();
+ }
+
@SmallTest
public void testSelectTextByDrag() throws Exception {
- getActivity();
-
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -77,8 +86,6 @@
@SmallTest
public void testSelectTextByDrag_reverse() throws Exception {
- getActivity();
-
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -89,9 +96,56 @@
}
@SmallTest
- public void testSelectTextByLongClick() throws Exception {
- getActivity();
+ public void testContextMenu() throws Exception {
+ final String text = "abc def ghi.";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+ ContextMenuUtils.assertContextMenuIsNotDisplayed();
+
+ onView(withId(R.id.textview)).perform(
+ mouseClickOnTextAtIndex(text.indexOf("d"), MotionEvent.BUTTON_SECONDARY));
+
+ ContextMenuUtils.assertContextMenuContainsItemDisabled(
+ getActivity().getString(com.android.internal.R.string.copy));
+ ContextMenuUtils.assertContextMenuContainsItemEnabled(
+ getActivity().getString(com.android.internal.R.string.undo));
+
+ // Hide context menu.
+ pressBack();
+ ContextMenuUtils.assertContextMenuIsNotDisplayed();
+
+ onView(withId(R.id.textview)).perform(
+ mouseDragOnText(text.indexOf("c"), text.indexOf("h")));
+ onView(withId(R.id.textview)).perform(
+ mouseClickOnTextAtIndex(text.indexOf("d"), MotionEvent.BUTTON_SECONDARY));
+
+ ContextMenuUtils.assertContextMenuContainsItemEnabled(
+ getActivity().getString(com.android.internal.R.string.copy));
+ ContextMenuUtils.assertContextMenuContainsItemEnabled(
+ getActivity().getString(com.android.internal.R.string.undo));
+
+ // Hide context menu.
+ pressBack();
+
+ onView(withId(R.id.textview)).check(hasSelection("c def g"));
+
+ onView(withId(R.id.textview)).perform(
+ mouseClickOnTextAtIndex(text.indexOf("i"), MotionEvent.BUTTON_SECONDARY));
+ ContextMenuUtils.assertContextMenuContainsItemDisabled(
+ getActivity().getString(com.android.internal.R.string.copy));
+ ContextMenuUtils.assertContextMenuContainsItemEnabled(
+ getActivity().getString(com.android.internal.R.string.undo));
+
+ // Hide context menu.
+ pressBack();
+
+ onView(withId(R.id.textview)).check(hasSelection(""));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("i")));
+ }
+
+ @SmallTest
+ public void testSelectTextByLongClick() throws Exception {
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -117,8 +171,6 @@
@SmallTest
public void testSelectTextByDoubleClick() throws Exception {
- getActivity();
-
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -144,8 +196,6 @@
@SmallTest
public void testSelectTextByDoubleClickAndDrag() throws Exception {
- getActivity();
-
final String text = "abcd efg hijk lmn";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
@@ -157,8 +207,6 @@
@SmallTest
public void testSelectTextByDoubleClickAndDrag_reverse() throws Exception {
- getActivity();
-
final String text = "abcd efg hijk lmn";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
@@ -170,8 +218,6 @@
@SmallTest
public void testSelectTextByLongPressAndDrag() throws Exception {
- getActivity();
-
final String text = "abcd efg hijk lmn";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
@@ -183,8 +229,6 @@
@SmallTest
public void testSelectTextByLongPressAndDrag_reverse() throws Exception {
- getActivity();
-
final String text = "abcd efg hijk lmn";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
@@ -196,8 +240,6 @@
@SmallTest
public void testSelectTextByTripleClick() throws Exception {
- getActivity();
-
final StringBuilder builder = new StringBuilder();
builder.append("First paragraph.\n");
builder.append("Second paragraph.");
@@ -232,8 +274,6 @@
@SmallTest
public void testSelectTextByTripleClickAndDrag() throws Exception {
- getActivity();
-
final StringBuilder builder = new StringBuilder();
builder.append("First paragraph.\n");
builder.append("Second paragraph.");
@@ -263,8 +303,6 @@
@SmallTest
public void testSelectTextByTripleClickAndDrag_reverse() throws Exception {
- getActivity();
-
final StringBuilder builder = new StringBuilder();
builder.append("First paragraph.\n");
builder.append("Second paragraph.");
diff --git a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
new file mode 100644
index 0000000..c8218aa
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.hasFocus;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+
+import com.android.internal.view.menu.ListMenuItemView;
+
+import android.support.test.espresso.NoMatchingRootException;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewInteraction;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.widget.MenuPopupWindow.MenuDropDownListView;
+
+/**
+ * Espresso utility methods for the context menu.
+ */
+public final class ContextMenuUtils {
+ private ContextMenuUtils() {}
+
+ private static ViewInteraction onContextMenu() {
+ // TODO: Have more reliable way to get context menu.
+ return onView(ViewMatchers.isAssignableFrom(MenuDropDownListView.class))
+ .inRoot(withDecorView(hasFocus()));
+ }
+
+ /**
+ * Asserts that the context menu is displayed
+ *
+ * @throws AssertionError if the assertion fails
+ */
+ private static void assertContextMenuIsDisplayed() {
+ onContextMenu().check(matches(isDisplayed()));
+ }
+
+ /**
+ * Asserts that the context menu is not displayed
+ *
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertContextMenuIsNotDisplayed() {
+ try {
+ assertContextMenuIsDisplayed();
+ } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
+ return;
+ }
+ throw new AssertionError("Context menu is displayed");
+ }
+
+ /**
+ * Asserts that the context menu contains the specified item and the item has specified enabled
+ * state.
+ *
+ * @param itemLabel label of the item.
+ * @param enabled enabled state of the item.
+ * @throws AssertionError if the assertion fails
+ */
+ private static void asssertContextMenuContainsItemWithEnabledState(String itemLabel,
+ boolean enabled) {
+ onContextMenu().check(matches(
+ hasDescendant(allOf(
+ isAssignableFrom(ListMenuItemView.class),
+ enabled ? isEnabled() : not(isEnabled()),
+ hasDescendant(withText(itemLabel))))));
+ }
+
+ /**
+ * Asserts that the context menu contains the specified item and the item is enabled.
+ *
+ * @param itemLabel label of the item.
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertContextMenuContainsItemEnabled(String itemLabel) {
+ asssertContextMenuContainsItemWithEnabledState(itemLabel, true);
+ }
+
+ /**
+ * Asserts that the context menu contains the specified item and the item is disabled.
+ *
+ * @param itemLabel label of the item.
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertContextMenuContainsItemDisabled(String itemLabel) {
+ asssertContextMenuContainsItemWithEnabledState(itemLabel, false);
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
index e51f2785..b8ea2de 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
@@ -24,9 +24,9 @@
import android.support.test.espresso.action.GeneralClickAction;
import android.support.test.espresso.action.MotionEvents;
import android.support.test.espresso.action.MotionEvents.DownResultHolder;
-import android.support.test.espresso.action.PrecisionDescriber;
import android.support.test.espresso.action.Press;
import android.support.test.espresso.action.Tapper;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -35,6 +35,8 @@
*/
public final class MouseClickAction implements ViewAction {
private final GeneralClickAction mGeneralClickAction;
+ @MouseUiController.MouseButton
+ private final int mButton;
public enum CLICK implements Tapper {
TRIPLE {
@@ -86,8 +88,20 @@
};
public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider) {
- mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider,
- Press.PINPOINT);
+ this(tapper, coordinatesProvider, MotionEvent.BUTTON_PRIMARY);
+ }
+
+ /**
+ * Constructs MouseClickAction
+ *
+ * @param tapper the tapper
+ * @param coordinatesProvider the provider of the event coordinates
+ * @param button the mouse button used to send motion events
+ */
+ public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider,
+ @MouseUiController.MouseButton int button) {
+ mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider, Press.PINPOINT);
+ mButton = button;
}
@Override
@@ -102,7 +116,7 @@
@Override
public void perform(UiController uiController, View view) {
- mGeneralClickAction.perform(new MouseUiController(uiController), view);
+ mGeneralClickAction.perform(new MouseUiController(uiController, mButton), view);
long doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
if (0 < doubleTapTimeout) {
// Wait to avoid false gesture detection. Without this wait, consecutive clicks can be
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
index f1387f8..022be76 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
@@ -16,6 +16,12 @@
package android.widget.espresso;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import android.annotation.IntDef;
import android.support.test.espresso.InjectEventSecurityException;
import android.support.test.espresso.UiController;
import android.view.InputDevice;
@@ -26,11 +32,28 @@
* Class to wrap an UiController to overwrite source of motion events to SOURCE_MOUSE.
* Note that this doesn't change the tool type.
*/
-public class MouseUiController implements UiController {
+public final class MouseUiController implements UiController {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({MotionEvent.BUTTON_PRIMARY, MotionEvent.BUTTON_SECONDARY, MotionEvent.BUTTON_TERTIARY})
+ public @interface MouseButton {}
+
private final UiController mUiController;
+ @MouseButton
+ private final int mButton;
public MouseUiController(UiController uiController) {
- mUiController = uiController;
+ this(uiController, MotionEvent.BUTTON_PRIMARY);
+ }
+
+ /**
+ * Constructs MouseUiController.
+ *
+ * @param uiController the uiController to wrap
+ * @param button the button to be used for generating input events.
+ */
+ public MouseUiController(UiController uiController, @MouseButton int button) {
+ mUiController = checkNotNull(uiController);
+ mButton = button;
}
@Override
@@ -40,9 +63,11 @@
@Override
public boolean injectMotionEvent(MotionEvent event) throws InjectEventSecurityException {
- // Modify the event to mimic mouse primary button event.
+ // Modify the event to mimic mouse event.
event.setSource(InputDevice.SOURCE_MOUSE);
- event.setButtonState(MotionEvent.BUTTON_PRIMARY);
+ if (event.getActionMasked() != MotionEvent.ACTION_UP) {
+ event.setButtonState(mButton);
+ }
return mUiController.injectMotionEvent(event);
}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index 54d5823..1dd6e17 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -26,6 +26,7 @@
import android.support.test.espresso.action.Tap;
import android.support.test.espresso.util.HumanReadables;
import android.text.Layout;
+import android.view.MotionEvent;
import android.view.View;
import android.widget.Editor;
import android.widget.TextView;
@@ -63,8 +64,24 @@
* @param index The index of the TextView's text to click on.
*/
public static ViewAction mouseClickOnTextAtIndex(int index) {
+ return mouseClickOnTextAtIndex(index, MotionEvent.BUTTON_PRIMARY);
+ }
+
+ /**
+ * Returns an action that clicks by mouse on text at an index on the TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param index The index of the TextView's text to click on.
+ * @param button the mouse button to use.
+ */
+ public static ViewAction mouseClickOnTextAtIndex(int index,
+ @MouseUiController.MouseButton int button) {
return actionWithAssertions(
- new MouseClickAction(Tap.SINGLE, new TextCoordinates(index)));
+ new MouseClickAction(Tap.SINGLE, new TextCoordinates(index), button));
}
/**
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 48cc91a..470f9ec 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -39,7 +39,7 @@
class DeferredLayerUpdater;
struct RecordedOp;
-class RecordingCanvas: public Canvas, public CanvasStateClient {
+class ANDROID_API RecordingCanvas: public Canvas, public CanvasStateClient {
enum class DeferredBarrierType {
None,
InOrder,
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index e9c94c0..6a13f82 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -129,9 +129,8 @@
* The TV input is connected.
*
* <p>This state indicates that a source device is connected to the input port and is in the
- * normal operation mode. It is mostly relevant to hardware inputs such as HDMI input. This is
- * the default state for any hardware inputs where their states are unknown. Non-hardware inputs
- * are considered connected all the time.
+ * normal operation mode. It is mostly relevant to hardware inputs such as HDMI input.
+ * Non-hardware inputs are considered connected all the time.
*
* @see #getInputState
* @see TvInputManager.TvInputCallback#onInputStateChanged
@@ -141,7 +140,8 @@
* The TV input is connected but in standby mode.
*
* <p>This state indicates that a source device is connected to the input port but is in standby
- * mode. It is mostly relevant to hardware inputs such as HDMI input.
+ * or low power mode. It is mostly relevant to hardware inputs such as HDMI inputs and Component
+ * inputs.
*
* @see #getInputState
* @see TvInputManager.TvInputCallback#onInputStateChanged
diff --git a/media/java/android/media/tv/TvStreamConfig.java b/media/java/android/media/tv/TvStreamConfig.java
index 0c2f3fe..eae83cf 100644
--- a/media/java/android/media/tv/TvStreamConfig.java
+++ b/media/java/android/media/tv/TvStreamConfig.java
@@ -28,8 +28,15 @@
public class TvStreamConfig implements Parcelable {
static final String TAG = TvStreamConfig.class.getSimpleName();
+ // Must be in sync with tv_input.h
public final static int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1;
public final static int STREAM_TYPE_BUFFER_PRODUCER = 2;
+ /**
+ * A flag indicating whether the HAL is sure about signal at this stream. Note that
+ * value of 0 here does not necessarily mean no signal. It just means that it may not have
+ * signal and the underlying layer is not sure.
+ */
+ public static final int FLAG_MASK_SIGNAL_DETECTION = 0x1;
private int mStreamId;
private int mType;
@@ -41,6 +48,10 @@
* via tv_input_device::get_stream_configurations().
*/
private int mGeneration;
+ /**
+ * Flags for stream status. See FLAG_MASK_* for details.
+ */
+ private int mFlags;
public static final Parcelable.Creator<TvStreamConfig> CREATOR =
new Parcelable.Creator<TvStreamConfig>() {
@@ -52,7 +63,8 @@
type(source.readInt()).
maxWidth(source.readInt()).
maxHeight(source.readInt()).
- generation(source.readInt()).build();
+ generation(source.readInt()).
+ flags(source.readInt()).build();
} catch (Exception e) {
Log.e(TAG, "Exception creating TvStreamConfig from parcel", e);
return null;
@@ -87,6 +99,10 @@
return mGeneration;
}
+ public int getFlags() {
+ return mFlags;
+ }
+
@Override
public String toString() {
return "TvStreamConfig {mStreamId=" + mStreamId + ";" + "mType=" + mType + ";mGeneration="
@@ -106,6 +122,7 @@
dest.writeInt(mMaxWidth);
dest.writeInt(mMaxHeight);
dest.writeInt(mGeneration);
+ dest.writeInt(mFlags);
}
/**
@@ -117,6 +134,7 @@
private Integer mMaxWidth;
private Integer mMaxHeight;
private Integer mGeneration;
+ private int mFlags = 0;
public Builder() {
}
@@ -146,6 +164,11 @@
return this;
}
+ public Builder flags(int flag) {
+ mFlags = flag;
+ return this;
+ }
+
public TvStreamConfig build() {
if (mStreamId == null || mType == null || mMaxWidth == null || mMaxHeight == null
|| mGeneration == null) {
@@ -158,6 +181,7 @@
config.mMaxWidth = mMaxWidth;
config.mMaxHeight = mMaxHeight;
config.mGeneration = mGeneration;
+ config.mFlags = mFlags;
return config;
}
}
@@ -172,6 +196,7 @@
&& config.mStreamId == mStreamId
&& config.mType == mType
&& config.mMaxWidth == mMaxWidth
- && config.mMaxHeight == mMaxHeight;
+ && config.mMaxHeight == mMaxHeight
+ && config.mFlags == mFlags;
}
}
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 8cc79a4c..5e634a4 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -40,7 +40,7 @@
<activity
android:name=".DownloadsActivity"
- android:theme="@style/DocumentsFullScreenTheme"
+ android:theme="@style/DocumentsTheme"
android:label="@string/downloads_label"
android:icon="@drawable/ic_doc_text">
<intent-filter>
@@ -64,7 +64,7 @@
<activity
android:name=".FilesActivity"
- android:theme="@style/DocumentsFullScreenTheme"
+ android:theme="@style/DocumentsTheme"
android:icon="@drawable/ic_files_app"
android:label="@string/files_label"
android:documentLaunchMode="intoExisting">
diff --git a/packages/DocumentsUI/res/values-sw720dp/dimens.xml b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
index f393d88..83ceb55 100644
--- a/packages/DocumentsUI/res/values-sw720dp/dimens.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
@@ -15,10 +15,6 @@
-->
<resources>
- <bool name="show_as_dialog">true</bool>
-
- <item type="dimen" name="dialog_width">85%</item>
-
<dimen name="grid_padding_horiz">16dp</dimen>
<dimen name="grid_padding_vert">16dp</dimen>
diff --git a/packages/DocumentsUI/res/values-sw720dp/layouts.xml b/packages/DocumentsUI/res/values-sw720dp/layouts.xml
deleted file mode 100644
index 7d28f9c..0000000
--- a/packages/DocumentsUI/res/values-sw720dp/layouts.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
- <item name="docs_activity" type="layout">@layout/fixed_layout</item>
-</resources>
diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml
deleted file mode 100644
index a8dcbb0..0000000
--- a/packages/DocumentsUI/res/values-sw720dp/styles.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-
- <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>
- <item name="*android:windowFixedHeightMinor">90%</item>
- </style>
-
-</resources>
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/packages/DocumentsUI/res/values/attrs.xml
index 0afc3a2..9e13001 100644
--- a/packages/DocumentsUI/res/values/attrs.xml
+++ b/packages/DocumentsUI/res/values/attrs.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<resources>
- <declare-styleable name="DocumentsBaseTheme">
+ <declare-styleable name="DocumentsTheme">
<attr name="colorActionMode" format="color"/>
</declare-styleable>
</resources>
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
index f94a00e..060871d 100644
--- a/packages/DocumentsUI/res/values/dimens.xml
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -35,7 +35,6 @@
<dimen name="list_divider_inset">72dp</dimen>
<bool name="list_divider_inset_left">true</bool>
- <bool name="show_as_dialog">false</bool>
<bool name="always_show_summary">false</bool>
<dimen name="dir_elevation">8dp</dimen>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index 6712e2d..d14631d 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -16,31 +16,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="DocumentsBaseTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar" />
<style name="ActionBarTheme" parent="@*android:style/ThemeOverlay.Material.Dark.ActionBar" />
<style name="ActionBarPopupTheme" parent="@*android:style/ThemeOverlay.Material.Light" />
- <style name="DocumentsTheme" parent="@style/DocumentsBaseTheme">
- <item name="actionBarWidgetTheme">@null</item>
- <item name="actionBarTheme">@style/ActionBarTheme</item>
- <item name="actionBarPopupTheme">@style/ActionBarPopupTheme</item>
-
- <item name="android:windowBackground">@color/window_background</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>
-
- <item name="android:windowActionBar">false</item>
- <item name="android:windowActionModeOverlay">true</item>
- <item name="android:windowNoTitle">true</item>
-
- <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
- </style>
-
- <style name="DocumentsFullScreenTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
+ <style name="DocumentsTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
<item name="actionBarWidgetTheme">@null</item>
<item name="actionBarTheme">@style/ActionBarTheme</item>
<item name="actionBarPopupTheme">@style/ActionBarPopupTheme</item>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 6d947d1..6a5911bb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -45,6 +45,7 @@
import android.support.design.widget.Snackbar;
import android.text.format.DateUtils;
import android.util.Log;
+import android.webkit.MimeTypeMap;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
@@ -463,9 +464,10 @@
* @param srcInfo DocumentInfos for the documents to copy.
* @param dstDirInfo The destination directory.
* @param mode The transfer mode (copy or move).
+ * @return True on success, false on failure.
* @throws RemoteException
*/
- private void copy(DocumentInfo srcInfo, DocumentInfo dstDirInfo, int mode)
+ private boolean copy(DocumentInfo srcInfo, DocumentInfo dstDirInfo, int mode)
throws RemoteException {
// When copying within the same provider, try to use optimized copying and moving.
// If not supported, then fallback to byte-by-byte copy/move.
@@ -477,7 +479,7 @@
dstDirInfo.derivedUri) == null) {
mFailedFiles.add(srcInfo);
}
- return;
+ return false;
}
break;
case TRANSFER_MODE_MOVE:
@@ -486,7 +488,7 @@
dstDirInfo.derivedUri) == null) {
mFailedFiles.add(srcInfo);
}
- return;
+ return false;
}
break;
default:
@@ -494,47 +496,83 @@
}
}
+ final String dstMimeType;
+ final String dstDisplayName;
+
+ // If the file is virtual, but can be converted to another format, then try to copy it
+ // as such format. Also, append an extension for the target mime type (if known).
+ if (srcInfo.isVirtualDocument()) {
+ if (!srcInfo.isTypedDocument()) {
+ // Impossible to copy a file which is virtual, but not typed.
+ mFailedFiles.add(srcInfo);
+ return false;
+ }
+ final String[] streamTypes = getContentResolver().getStreamTypes(
+ srcInfo.derivedUri, "*/*");
+ if (streamTypes != null && streamTypes.length > 0) {
+ dstMimeType = streamTypes[0];
+ final String extension = MimeTypeMap.getSingleton().
+ getExtensionFromMimeType(dstMimeType);
+ dstDisplayName = srcInfo.displayName +
+ (extension != null ? "." + extension : srcInfo.displayName);
+ } else {
+ // The provider says that it supports typed documents, but doesn't say
+ // anything about available formats.
+ // TODO: Log failures. b/26192412
+ mFailedFiles.add(srcInfo);
+ return false;
+ }
+ } else {
+ dstMimeType = srcInfo.mimeType;
+ dstDisplayName = srcInfo.displayName;
+ }
+
// Create the target document (either a file or a directory), then copy recursively the
// contents (bytes or children).
- final Uri dstUri = DocumentsContract.createDocument(mDstClient, dstDirInfo.derivedUri,
- srcInfo.mimeType, srcInfo.displayName);
+ final Uri dstUri = DocumentsContract.createDocument(mDstClient,
+ dstDirInfo.derivedUri, dstMimeType, dstDisplayName);
if (dstUri == null) {
// If this is a directory, the entire subdir will not be copied over.
mFailedFiles.add(srcInfo);
- return;
+ return false;
}
DocumentInfo dstInfo = null;
try {
- final DocumentInfo dstDocInfo = DocumentInfo.fromUri(getContentResolver(), dstUri);
+ dstInfo = DocumentInfo.fromUri(getContentResolver(), dstUri);
} catch (FileNotFoundException e) {
mFailedFiles.add(srcInfo);
- return;
+ return false;
}
+ final boolean success;
if (Document.MIME_TYPE_DIR.equals(srcInfo.mimeType)) {
- copyDirectoryHelper(srcInfo, dstInfo, mode);
+ success = copyDirectoryHelper(srcInfo, dstInfo, mode);
} else {
- copyFileHelper(srcInfo, dstInfo, mode);
+ success = copyFileHelper(srcInfo, dstInfo, dstMimeType, mode);
}
- if (mode == TRANSFER_MODE_MOVE) {
+ if (mode == TRANSFER_MODE_MOVE && success) {
+ // This is racey. We should make sure that we never delete a directory after
+ // it changed, so we don't remove a file which had not been copied earlier
+ // to the target location.
try {
DocumentsContract.deleteDocument(mSrcClient, srcInfo.derivedUri);
} catch (RemoteException e) {
- // RemoteExceptions usually signal that the connection is dead, so there's no
- // point attempting to continue. Propagate the exception up so the copy job is
- // cancelled.
- Log.w(TAG, "Failed to clean up after move: " + srcInfo.derivedUri, e);
+ Log.w(TAG, "Failed to delete source after moving: " + srcInfo.derivedUri, e);
throw e;
}
}
+
+ return success;
}
/**
* Returns true if {@code doc} is a descendant of {@code parentDoc}.
+ * @throws RemoteException
*/
- boolean isDescendentOf(DocumentInfo doc, DocumentInfo parentDoc) throws RemoteException {
+ boolean isDescendentOf(DocumentInfo doc, DocumentInfo parentDoc)
+ throws RemoteException {
if (parentDoc.isDirectory() && doc.authority.equals(parentDoc.authority)) {
return DocumentsContract.isChildDocument(
mDstClient, doc.derivedUri, parentDoc.derivedUri);
@@ -549,9 +587,11 @@
* @param srcDirInfo Info of the directory to copy from. The routine will copy the directory's
* contents, not the directory itself.
* @param dstDirInfo Info of the directory to copy to. Must be created beforehand.
+ * @return True on success, false if some of the children failed to copy.
* @throws RemoteException
*/
- private void copyDirectoryHelper(DocumentInfo srcDirInfo, DocumentInfo dstDirInfo, int mode)
+ private boolean copyDirectoryHelper(
+ DocumentInfo srcDirInfo, DocumentInfo dstDirInfo, int mode)
throws RemoteException {
// Recurse into directories. Copy children into the new subdirectory.
final String queryColumns[] = new String[] {
@@ -562,17 +602,22 @@
Document.COLUMN_FLAGS
};
Cursor cursor = null;
+ boolean success = true;
try {
// Iterate over srcs in the directory; copy to the destination directory.
+ final Uri queryUri = DocumentsContract.buildChildDocumentsUri(srcDirInfo.authority,
+ srcDirInfo.documentId);
+ cursor = mSrcClient.query(queryUri, queryColumns, null, null, null);
DocumentInfo srcInfo;
- cursor = mSrcClient.query(srcDirInfo.derivedUri, queryColumns, null, null, null);
while (cursor.moveToNext()) {
srcInfo = DocumentInfo.fromCursor(cursor, srcDirInfo.authority);
- copy(srcInfo, dstDirInfo, mode);
+ success &= copy(srcInfo, dstDirInfo, mode);
}
} finally {
IoUtils.closeQuietly(cursor);
}
+
+ return success;
}
/**
@@ -580,10 +625,12 @@
*
* @param srcUriInfo Info of the file to copy from.
* @param dstUriInfo Info of the *file* to copy to. Must be created beforehand.
+ * @param mimeType Mime type for the target. Can be different than source for virtual files.
+ * @return True on success, false on error.
* @throws RemoteException
*/
- private void copyFileHelper(DocumentInfo srcInfo, DocumentInfo dstInfo, int mode)
- throws RemoteException {
+ private boolean copyFileHelper(DocumentInfo srcInfo, DocumentInfo dstInfo, String mimeType,
+ int mode) throws RemoteException {
// Copy an individual file.
CancellationSignal canceller = new CancellationSignal();
ParcelFileDescriptor srcFile = null;
@@ -591,45 +638,43 @@
InputStream src = null;
OutputStream dst = null;
- IOException copyError = null;
+ boolean success = true;
try {
// If the file is virtual, but can be converted to another format, then try to copy it
// as such format.
if (srcInfo.isVirtualDocument() && srcInfo.isTypedDocument()) {
- final String[] streamTypes = mSrcClient.getStreamTypes(srcInfo.derivedUri, "*/*");
- if (streamTypes.length > 0) {
- // Pick the first streamable format.
- final AssetFileDescriptor srcFileAsAsset =
- mSrcClient.openTypedAssetFileDescriptor(
- srcInfo.derivedUri, streamTypes[0], null, canceller);
- srcFile = srcFileAsAsset.getParcelFileDescriptor();
- src = new AssetFileDescriptor.AutoCloseInputStream(srcFileAsAsset);
- } else {
- // TODO: Log failures. b/26192412
- mFailedFiles.add(srcInfo);
- }
+ final AssetFileDescriptor srcFileAsAsset =
+ mSrcClient.openTypedAssetFileDescriptor(
+ srcInfo.derivedUri, mimeType, null, canceller);
+ srcFile = srcFileAsAsset.getParcelFileDescriptor();
+ src = new AssetFileDescriptor.AutoCloseInputStream(srcFileAsAsset);
} else {
srcFile = mSrcClient.openFile(srcInfo.derivedUri, "r", canceller);
src = new ParcelFileDescriptor.AutoCloseInputStream(srcFile);
}
+
dstFile = mDstClient.openFile(dstInfo.derivedUri, "w", canceller);
dst = new ParcelFileDescriptor.AutoCloseOutputStream(dstFile);
byte[] buffer = new byte[8192];
int len;
- while (!mIsCancelled && ((len = src.read(buffer)) != -1)) {
+ while ((len = src.read(buffer)) != -1) {
+ if (mIsCancelled) {
+ success = false;
+ break;
+ }
dst.write(buffer, 0, len);
makeProgress(len);
}
srcFile.checkError();
} catch (IOException e) {
- copyError = e;
+ success = false;
mFailedFiles.add(srcInfo);
if (dstFile != null) {
try {
- dstFile.closeWithError(copyError.getMessage());
+ dstFile.closeWithError(e.getMessage());
} catch (IOException closeError) {
Log.e(TAG, "Error closing destination", closeError);
}
@@ -640,18 +685,21 @@
IoUtils.closeQuietly(dst);
}
- if (copyError != null || mIsCancelled) {
+ if (!success) {
// Clean up half-copied files.
canceller.cancel();
try {
DocumentsContract.deleteDocument(mDstClient, dstInfo.derivedUri);
} catch (RemoteException e) {
- Log.w(TAG, "Failed to clean up after copy error: " + dstInfo.derivedUri, e);
- // RemoteExceptions usually signal that the connection is dead, so there's no point
- // attempting to continue. Propagate the exception up so the copy job is cancelled.
+ // RemoteExceptions usually signal that the connection is dead, so there's no
+ // point attempting to continue. Propagate the exception up so the copy job is
+ // cancelled.
+ Log.w(TAG, "Failed to cleanup after copy error: " + srcInfo.derivedUri, e);
throw e;
}
}
+
+ return success;
}
/**
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 313d303..8ca2cfb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -34,7 +34,6 @@
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
-import android.graphics.Point;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -45,7 +44,6 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.Spinner;
import android.widget.Toolbar;
@@ -64,8 +62,6 @@
private static final int CODE_FORWARD = 42;
private static final String TAG = "DocumentsActivity";
- private boolean mShowAsDialog;
-
private Toolbar mToolbar;
private Spinner mToolbarStack;
@@ -83,29 +79,8 @@
super.onCreate(icicle);
final Resources res = getResources();
- mShowAsDialog = res.getBoolean(R.bool.show_as_dialog);
- if (!mShowAsDialog) {
- setTheme(R.style.DocumentsFullScreenTheme);
- }
-
- if (mShowAsDialog) {
- mDrawer = DrawerController.createDummy();
-
- // Strongly define our horizontal dimension; we leave vertical as
- // WRAP_CONTENT so that system resizes us when IME is showing.
- final WindowManager.LayoutParams a = getWindow().getAttributes();
-
- final Point size = new Point();
- getWindowManager().getDefaultDisplay().getSize(size);
- a.width = (int) res.getFraction(R.dimen.dialog_width, size.x, size.x);
-
- getWindow().setAttributes(a);
-
- } else {
- mDrawer = DrawerController.create(this);
- }
-
+ mDrawer = DrawerController.create(this);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mStackAdapter = new StackAdapter();
@@ -267,15 +242,16 @@
}
}
- if (!mShowAsDialog && mDrawer.isUnlocked()) {
+ if (mDrawer.isUnlocked()) {
mToolbar.setNavigationIcon(R.drawable.ic_hamburger);
mToolbar.setNavigationContentDescription(R.string.drawer_open);
- mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- setRootsDrawerOpen(true);
- }
- });
+ mToolbar.setNavigationOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setRootsDrawerOpen(true);
+ }
+ });
} else {
mToolbar.setNavigationIcon(null);
mToolbar.setNavigationContentDescription(R.string.drawer_open);
@@ -306,10 +282,7 @@
public boolean onCreateOptionsMenu(Menu menu) {
boolean showMenu = super.onCreateOptionsMenu(menu);
- // Most actions are visible when showing as dialog
- if (mShowAsDialog) {
- expandMenus(menu);
- }
+ expandMenus(menu);
return showMenu;
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
index 079d599..24a8113 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
@@ -97,7 +97,7 @@
public void testCopyFile() throws Exception {
String srcPath = "/test0.txt";
- Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
+ Uri testFile = mStorage.createRegularFile(SRC_ROOT, srcPath, "text/plain",
"The five boxing wizards jump quickly".getBytes());
startService(createCopyIntent(Lists.newArrayList(testFile)));
@@ -110,10 +110,33 @@
assertCopied(srcPath);
}
+ public void testCopyVirtualTypedFile() throws Exception {
+ String srcPath = "/virtual.sth";
+ String expectedDstPath = "/virtual.sth.pdf";
+ ArrayList<String> streamTypes = new ArrayList<>();
+ streamTypes.add("application/pdf");
+ streamTypes.add("text/html");
+ String testContent = "I love fruit cakes!";
+ Uri testFile = mStorage.createVirtualFile(SRC_ROOT, srcPath, "virtual/mime-type",
+ streamTypes, testContent.getBytes());
+
+ startService(createCopyIntent(Lists.newArrayList(testFile)));
+
+ // 2 operations: file creation, then writing data.
+ mResolver.waitForChanges(2);
+
+ // Verify that one file was copied.
+ assertDestFileCount(1);
+
+ byte[] dstContent = readFile(DST_ROOT, expectedDstPath);
+ MoreAsserts.assertEquals("Moved file contents differ", testContent.getBytes(), dstContent);
+ }
+
public void testMoveFile() throws Exception {
String srcPath = "/test0.txt";
String testContent = "The five boxing wizards jump quickly";
- Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain", testContent.getBytes());
+ Uri testFile = mStorage.createRegularFile(SRC_ROOT, srcPath, "text/plain",
+ testContent.getBytes());
Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile));
moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
@@ -142,9 +165,12 @@
"/test2.txt"
};
List<Uri> testFiles = Lists.newArrayList(
- mStorage.createFile(SRC_ROOT, srcPaths[0], "text/plain", testContent[0].getBytes()),
- mStorage.createFile(SRC_ROOT, srcPaths[1], "text/plain", testContent[1].getBytes()),
- mStorage.createFile(SRC_ROOT, srcPaths[2], "text/plain", testContent[2].getBytes()));
+ mStorage.createRegularFile(SRC_ROOT, srcPaths[0], "text/plain",
+ testContent[0].getBytes()),
+ mStorage.createRegularFile(SRC_ROOT, srcPaths[1], "text/plain",
+ testContent[1].getBytes()),
+ mStorage.createRegularFile(SRC_ROOT, srcPaths[2], "text/plain",
+ testContent[2].getBytes()));
// Copy all the test files.
startService(createCopyIntent(testFiles));
@@ -195,7 +221,6 @@
Intent intent = createCopyIntent(Lists.newArrayList(testDir), descDir);
startService(intent);
-
getService().addFinishedListener(mListener);
mListener.waitForFinished();
@@ -240,9 +265,9 @@
};
// Create test dir; put some files in it.
Uri testDir = createTestDirectory(srcDir);
- mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
- mStorage.createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
- mStorage.createFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
+ mStorage.createRegularFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
+ mStorage.createRegularFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
+ mStorage.createRegularFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
@@ -270,7 +295,7 @@
public void testCopyFileWithReadErrors() throws Exception {
String srcPath = "/test0.txt";
- Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
+ Uri testFile = mStorage.createRegularFile(SRC_ROOT, srcPath, "text/plain",
"The five boxing wizards jump quickly".getBytes());
mStorage.simulateReadErrorsForFile(testFile);
@@ -284,9 +309,26 @@
assertDestFileCount(0);
}
+ public void testCopyVirtualNonTypedFile() throws Exception {
+ String srcPath = "/non-typed.sth";
+ // Empty stream types causes the FLAG_SUPPORTS_TYPED_DOCUMENT to be not set.
+ ArrayList<String> streamTypes = new ArrayList<>();
+ Uri testFile = mStorage.createVirtualFile(SRC_ROOT, srcPath, "virtual/mime-type",
+ streamTypes, "I love Tokyo!".getBytes());
+
+ Intent intent = createCopyIntent(Lists.newArrayList(testFile));
+ startService(intent);
+ getService().addFinishedListener(mListener);
+
+ mListener.waitForFinished();
+ mListener.assertFailedCount(1);
+ mListener.assertFileFailed("non-typed.sth");
+ assertDestFileCount(0);
+ }
+
public void testMoveFileWithReadErrors() throws Exception {
String srcPath = "/test0.txt";
- Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
+ Uri testFile = mStorage.createRegularFile(SRC_ROOT, srcPath, "text/plain",
"The five boxing wizards jump quickly".getBytes());
mStorage.simulateReadErrorsForFile(testFile);
@@ -326,10 +368,10 @@
};
// Create test dir; put some files in it.
Uri testDir = createTestDirectory(srcDir);
- mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
+ mStorage.createRegularFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
Uri errFile = mStorage
- .createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
- mStorage.createFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
+ .createRegularFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
+ mStorage.createRegularFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
mStorage.simulateReadErrorsForFile(errFile);
@@ -363,7 +405,7 @@
}
private Uri createTestDirectory(String dir) throws IOException {
- return mStorage.createFile(
+ return mStorage.createRegularFile(
SRC_ROOT, dir, DocumentsContract.Document.MIME_TYPE_DIR, null);
}
@@ -473,6 +515,7 @@
final CountDownLatch latch = new CountDownLatch(1);
final List<DocumentInfo> failedDocs = new ArrayList<>();
+
@Override
public void onFinished(List<DocumentInfo> failed) {
failedDocs.addAll(failed);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index 7a75503e..63a519f 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -44,9 +44,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
public class StubProvider extends DocumentsProvider {
@@ -59,7 +61,7 @@
private static final String EXTRA_SIZE = "com.android.documentsui.stubprovider.SIZE";
private static final String EXTRA_ROOT = "com.android.documentsui.stubprovider.ROOT";
private static final String STORAGE_SIZE_KEY = "documentsui.stubprovider.size";
- private static int DEFAULT_ROOT_SIZE = 1024 * 1024 * 100; // 100 MB.
+ private static int DEFAULT_ROOT_SIZE = 1024 * 1024 * 100; // 100 MB.
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
@@ -105,7 +107,13 @@
mRoots.clear();
for (String rootId : rootIds) {
- final RootInfo rootInfo = new RootInfo(rootId, getSize(rootId));
+ // Make a subdir in the cache dir for each root.
+ final File file = new File(getContext().getCacheDir(), rootId);
+ if (file.mkdir()) {
+ Log.i(TAG, "Created new root directory @ " + file.getPath());
+ }
+ final RootInfo rootInfo = new RootInfo(file, getSize(rootId));
+ mStorage.put(rootInfo.document.documentId, rootInfo.document);
mRoots.put(rootId, rootInfo);
}
}
@@ -188,7 +196,7 @@
created = file.createNewFile();
} catch (IOException e) {
// We'll throw an FNF exception later :)
- Log.e(TAG, "createnewFile operation failed for file: " + file, e);
+ Log.e(TAG, "createNewFile operation failed for file: " + file, e);
}
if (!created) {
throw new FileNotFoundException(
@@ -197,7 +205,8 @@
Log.i(TAG, "Created new file: " + file);
}
- final StubDocument document = new StubDocument(file, mimeType, parent);
+ final StubDocument document = StubDocument.createRegularDocument(file, mimeType, parent);
+ mStorage.put(document.documentId, document);
Log.d(TAG, "Created document " + document.documentId);
notifyParentChanged(document.parentId);
getContext().getContentResolver().notifyChange(
@@ -264,14 +273,18 @@
public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
throws FileNotFoundException {
final StubDocument document = mStorage.get(docId);
- if (document == null || !document.file.isFile())
+ if (document == null || !document.file.isFile()) {
throw new FileNotFoundException();
+ }
+ if ((document.flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0) {
+ throw new IllegalStateException("Tried to open a virtual file.");
+ }
if ("r".equals(mode)) {
- ParcelFileDescriptor pfd = ParcelFileDescriptor.open(document.file,
- ParcelFileDescriptor.MODE_READ_ONLY);
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(document.file,
+ ParcelFileDescriptor.MODE_READ_ONLY);
if (docId.equals(mSimulateReadErrors)) {
- pfd = new ParcelFileDescriptor(pfd) {
+ return new ParcelFileDescriptor(pfd) {
@Override
public void checkError() throws IOException {
throw new IOException("Test error");
@@ -298,6 +311,54 @@
throw new FileNotFoundException();
}
+ @Override
+ public AssetFileDescriptor openTypedDocument(
+ String documentId, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
+ throws FileNotFoundException {
+ final StubDocument document = mStorage.get(documentId);
+ if (document == null || !document.file.isFile()) {
+ throw new FileNotFoundException();
+ }
+ if ((document.flags & Document.FLAG_SUPPORTS_TYPED_DOCUMENT) == 0) {
+ throw new IllegalStateException("Tried to open a non-typed document as typed.");
+ }
+ for (final String mimeType : document.streamTypes) {
+ // Strict compare won't accept wildcards, but that's OK for tests, as DocumentsUI
+ // doesn't use them for getStreamTypes nor openTypedDocument.
+ if (mimeType.equals(mimeTypeFilter)) {
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+ document.file, ParcelFileDescriptor.MODE_READ_ONLY);
+ if (documentId.equals(mSimulateReadErrors)) {
+ pfd = new ParcelFileDescriptor(pfd) {
+ @Override
+ public void checkError() throws IOException {
+ throw new IOException("Test error");
+ }
+ };
+ }
+ return new AssetFileDescriptor(pfd, 0, document.file.length());
+ }
+ }
+ throw new IllegalArgumentException("Invalid MIME type filter for openTypedDocument().");
+ }
+
+ @Override
+ public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+ final StubDocument document = mStorage.get(DocumentsContract.getDocumentId(uri));
+ if (document == null) {
+ throw new IllegalArgumentException(
+ "The provided Uri is incorrect, or the file is gone.");
+ }
+ if ((document.flags & Document.FLAG_SUPPORTS_TYPED_DOCUMENT) == 0) {
+ return null;
+ }
+ if (!"*/*".equals(mimeTypeFilter)) {
+ // Not used by DocumentsUI, so don't bother implementing it.
+ throw new UnsupportedOperationException();
+ }
+ return document.streamTypes.toArray(new String[document.streamTypes.size()]);
+ }
+
private ParcelFileDescriptor startWrite(final StubDocument document)
throws FileNotFoundException {
ParcelFileDescriptor[] pipe;
@@ -398,14 +459,7 @@
row.add(Document.COLUMN_DISPLAY_NAME, document.file.getName());
row.add(Document.COLUMN_SIZE, document.file.length());
row.add(Document.COLUMN_MIME_TYPE, document.mimeType);
- int flags = Document.FLAG_SUPPORTS_DELETE;
- // TODO: Add support for renaming.
- if (document.file.isDirectory()) {
- flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
- } else {
- flags |= Document.FLAG_SUPPORTS_WRITE;
- }
- row.add(Document.COLUMN_FLAGS, flags);
+ row.add(Document.COLUMN_FLAGS, document.flags);
row.add(Document.COLUMN_LAST_MODIFIED, document.file.lastModified());
}
@@ -439,23 +493,14 @@
}
@VisibleForTesting
- public Uri createFile(String rootId, String path, String mimeType, byte[] content)
+ public File createFile(String rootId, String path, String mimeType, byte[] content)
throws FileNotFoundException, IOException {
Log.d(TAG, "Creating test file " + rootId + ":" + path);
StubDocument root = mRoots.get(rootId).document;
if (root == null) {
throw new FileNotFoundException("No roots with the ID " + rootId + " were found");
}
- File file = new File(root.file, path.substring(1));
- StubDocument parent = mStorage.get(getDocumentIdForFile(file.getParentFile()));
- if (parent == null) {
- parent = mStorage.get(createFile(rootId, file.getParentFile().getPath(),
- DocumentsContract.Document.MIME_TYPE_DIR, null));
- Log.d(TAG, "Created parent " + parent.documentId);
- } else {
- Log.d(TAG, "Found parent " + parent.documentId);
- }
-
+ final File file = new File(root.file, path.substring(1));
if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) {
if (!file.mkdirs()) {
throw new FileNotFoundException("Couldn't create directory " + file.getPath());
@@ -464,12 +509,38 @@
if (!file.createNewFile()) {
throw new FileNotFoundException("Couldn't create file " + file.getPath());
}
- // Add content to the file.
- FileOutputStream fout = new FileOutputStream(file);
- fout.write(content);
- fout.close();
+ try (final FileOutputStream fout = new FileOutputStream(file)) {
+ fout.write(content);
+ }
}
- final StubDocument document = new StubDocument(file, mimeType, parent);
+ return file;
+ }
+
+ @VisibleForTesting
+ public Uri createRegularFile(String rootId, String path, String mimeType, byte[] content)
+ throws FileNotFoundException, IOException {
+ final File file = createFile(rootId, path, mimeType, content);
+ final StubDocument parent = mStorage.get(getDocumentIdForFile(file.getParentFile()));
+ if (parent == null) {
+ throw new FileNotFoundException("Parent not found.");
+ }
+ final StubDocument document = StubDocument.createRegularDocument(file, mimeType, parent);
+ mStorage.put(document.documentId, document);
+ return DocumentsContract.buildDocumentUri(mAuthority, document.documentId);
+ }
+
+ @VisibleForTesting
+ public Uri createVirtualFile(
+ String rootId, String path, String mimeType, List<String> streamTypes, byte[] content)
+ throws FileNotFoundException, IOException {
+ final File file = createFile(rootId, path, mimeType, content);
+ final StubDocument parent = mStorage.get(getDocumentIdForFile(file.getParentFile()));
+ if (parent == null) {
+ throw new FileNotFoundException("Parent not found.");
+ }
+ final StubDocument document = StubDocument.createVirtualDocument(
+ file, mimeType, streamTypes, parent);
+ mStorage.put(document.documentId, document);
return DocumentsContract.buildDocumentUri(mAuthority, document.documentId);
}
@@ -489,21 +560,16 @@
return found.file;
}
- final class RootInfo {
+ final static class RootInfo {
public final String name;
public final StubDocument document;
public long capacity;
public long size;
- RootInfo(String name, long capacity) {
- this.name = name;
+ RootInfo(File file, long capacity) {
+ this.name = file.getName();
this.capacity = 1024 * 1024;
- // Make a subdir in the cache dir for each root.
- File file = new File(getContext().getCacheDir(), name);
- if (file.mkdir()) {
- Log.i(TAG, "Created new root directory @ " + file.getPath());
- }
- this.document = new StubDocument(file, Document.MIME_TYPE_DIR, this);
+ this.document = StubDocument.createRootDocument(file, this);
this.capacity = capacity;
this.size = 0;
}
@@ -513,38 +579,72 @@
}
}
- final class StubDocument {
+ final static class StubDocument {
public final File file;
- public final String mimeType;
public final String documentId;
+ public final String mimeType;
+ public final List<String> streamTypes;
+ public final int flags;
public final String parentId;
public final RootInfo rootInfo;
- StubDocument(File file, String mimeType, StubDocument parent) {
+ private StubDocument(
+ File file, String mimeType, List<String> streamTypes, int flags,
+ StubDocument parent) {
this.file = file;
- this.mimeType = mimeType;
this.documentId = getDocumentIdForFile(file);
+ this.mimeType = mimeType;
+ this.streamTypes = streamTypes;
+ this.flags = flags;
this.parentId = parent.documentId;
this.rootInfo = parent.rootInfo;
- mStorage.put(this.documentId, this);
}
- StubDocument(File file, String mimeType, RootInfo rootInfo) {
+ private StubDocument(File file, RootInfo rootInfo) {
this.file = file;
- this.mimeType = mimeType;
this.documentId = getDocumentIdForFile(file);
+ this.mimeType = Document.MIME_TYPE_DIR;
+ this.streamTypes = new ArrayList<String>();
+ this.flags = Document.FLAG_DIR_SUPPORTS_CREATE;
this.parentId = null;
this.rootInfo = rootInfo;
- mStorage.put(this.documentId, this);
}
+
+ public static StubDocument createRootDocument(File file, RootInfo rootInfo) {
+ return new StubDocument(file, rootInfo);
+ }
+
+ public static StubDocument createRegularDocument(
+ File file, String mimeType, StubDocument parent) {
+ int flags = Document.FLAG_SUPPORTS_DELETE;
+ if (file.isDirectory()) {
+ flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
+ } else {
+ flags |= Document.FLAG_SUPPORTS_WRITE;
+ }
+ return new StubDocument(file, mimeType, new ArrayList<String>(), flags, parent);
+ }
+
+ public static StubDocument createVirtualDocument(
+ File file, String mimeType, List<String> streamTypes, StubDocument parent) {
+ int flags = Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE
+ | Document.FLAG_VIRTUAL_DOCUMENT;
+ if (streamTypes.size() > 0) {
+ flags |= Document.FLAG_SUPPORTS_TYPED_DOCUMENT;
+ }
+ return new StubDocument(file, mimeType, streamTypes, flags, parent);
+ }
+
@Override
public String toString() {
return "StubDocument{"
+ "path:" + file.getPath()
- + ", mimeType:" + mimeType
- + ", rootInfo:" + rootInfo
+ ", documentId:" + documentId
+ + ", mimeType:" + mimeType
+ + ", streamTypes:" + streamTypes.toString()
+ + ", flags:" + flags
+ ", parentId:" + parentId
+ + ", rootInfo:" + rootInfo
+ "}";
}
}
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index c9dbc79..f15c97e 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -24,35 +24,23 @@
android:clickable="true"
android:gravity="top|start"
android:orientation="vertical"
- android:paddingEnd="8dp"
+ android:paddingStart="@*android:dimen/notification_content_margin_start"
+ android:paddingEnd="@*android:dimen/notification_content_margin_end"
android:background="@color/notification_guts_text_color" >
<!-- header -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="@*android:dimen/notification_content_margin_start"
- android:paddingTop="8dp"
- android:paddingBottom="16dp" >
+ android:paddingBottom="8dp" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/notification_guts_header"
- android:orientation="vertical"
+ android:orientation="horizontal"
android:layout_gravity="center_vertical|start"
android:layout_marginEnd="52dp">
-
- <LinearLayout
- android:id="@+id/notification_guts_app_details"
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:layout_gravity="start|top"
- android:gravity="center_vertical"
- >
-
<ImageView
android:id="@android:id/icon"
android:layout_width="18dp"
@@ -76,22 +64,12 @@
android:layout_gravity="bottom|start"
android:visibility="gone"
android:textColor="#ffffff" />
- </LinearLayout>
-
- <TextView
- android:id="@+id/topic_details"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAppearance="@android:style/TextAppearance.Material.Notification.Title"
- android:textColor="@color/notification_guts_text_color"
- android:layout_alignParentBottom="true"
- android:layout_alignParentStart="true" />
- </LinearLayout>
+ </LinearLayout>
<ImageButton style="@android:style/Widget.Material.Light.Button.Borderless.Small"
android:id="@+id/notification_inspect_item"
android:layout_width="52dp"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center"
android:layout_gravity="center_vertical|end"
@@ -103,7 +81,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
- android:paddingStart="@*android:dimen/notification_content_margin_start"
android:orientation="vertical"
android:clickable="false"
android:focusable="false"
@@ -116,8 +93,7 @@
android:textAppearance="@android:style/TextAppearance.Material.Subhead"
android:textColor="@color/notification_guts_text_color"
android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:text="@*android:string/notification_importance_title"/>
+ android:fadingEdge="horizontal"/>
<TextView
android:id="@+id/summary"
@@ -133,7 +109,7 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="6dp" >
+ android:paddingTop="8dp" >
<ImageView
android:id="@+id/low_importance"
@@ -162,5 +138,21 @@
android:layout_height="24dp"/>
</FrameLayout>
+
+ <RadioGroup android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp">
+ <RadioButton android:id="@+id/apply_to_topic"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/notification_guts_text_color"
+ android:visibility="gone"/>
+ <RadioButton android:id="@+id/apply_to_app"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/apply_to_app"
+ android:textColor="@color/notification_guts_text_color"
+ android:visibility="gone"/>
+ </RadioGroup>
</LinearLayout>
</com.android.systemui.statusbar.NotificationGuts>
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index b8caf23..04f18c5 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -20,7 +20,7 @@
android:layout_height="@dimen/recents_task_bar_height"
android:layout_gravity="top|center_horizontal">
<com.android.systemui.recents.views.FixedSizeImageView
- android:id="@+id/application_icon"
+ android:id="@+id/icon"
android:contentDescription="@string/recents_app_info_button_label"
android:layout_width="@dimen/recents_task_view_application_icon_size"
android:layout_height="@dimen/recents_task_view_application_icon_size"
@@ -29,7 +29,7 @@
android:padding="8dp"
android:background="@drawable/recents_button_bg" />
<TextView
- android:id="@+id/activity_description"
+ android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 76c08f6..0ccc236 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -69,7 +69,7 @@
<!-- The lock to task button foreground color. -->
<color name="recents_task_view_lock_to_app_button_color">#ff666666</color>
<!-- The background color for the freeform workspace. -->
- <color name="recents_freeform_workspace_bg_color">#66000000</color>
+ <color name="recents_freeform_workspace_bg_color">#33FFFFFF</color>
<color name="keyguard_affordance">#ffffffff</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f71a71a..50e0661 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1201,4 +1201,34 @@
<!-- Bluetooth enablement ok text [CHAR LIMIT=40] -->
<string name="enable_bluetooth_confirmation_ok">Turn on</string>
+ <!-- Apply notification importance setting to a topic [CHAR LIMIT=NONE] -->
+ <string name="apply_to_topic">Apply to <xliff:g id="topic_name" example="Friend Request">%1$s</xliff:g> notifications</string>
+ <!-- Apply notification importance setting to an app [CHAR LIMIT=NONE] -->
+ <string name="apply_to_app">Apply to all notifications from this app</string>
+ <!-- Notification importance title, blocked status-->
+ <string name="blocked_importance">Blocked</string>
+ <!-- Notification importance title, low status-->
+ <string name="low_importance">Low importance</string>
+ <!-- Notification importance title, normal status-->
+ <string name="default_importance">Normal importance</string>
+ <!-- Notification importance title, high status-->
+ <string name="high_importance">High importance</string>
+ <!-- Notification importance title, max status-->
+ <string name="max_importance">Urgent importance</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance slider: blocked importance level description -->
+ <string name="notification_importance_blocked">Never show these notifications</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance slider: low importance level description -->
+ <string name="notification_importance_low">Silently show at the bottom of the notification list</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance slider: normal importance level description -->
+ <string name="notification_importance_default">Silently show these notifications</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance slider: high importance level description -->
+ <string name="notification_importance_high">Show at the top of the notifications list and make sound</string>
+
+ <!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description -->
+ <string name="notification_importance_max">Peek onto the screen and make sound</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b3ce4a6..ffcc805 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -246,10 +246,7 @@
boolean dismissHistory() {
// Try and hide the history view first
if (mHistoryView != null && mHistoryView.isVisible()) {
- ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
- t.increment();
- EventBus.getDefault().send(new HideHistoryEvent(true /* animate */, t));
- t.decrement();
+ EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
return true;
}
return false;
@@ -301,8 +298,8 @@
*/
void dismissRecentsToHome(boolean animated) {
if (animated) {
- ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
- null, mFinishLaunchHomeRunnable, null);
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(null,
+ mFinishLaunchHomeRunnable, null);
mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
} else {
@@ -439,10 +436,7 @@
// Reset some states
mIgnoreAltTabRelease = false;
if (mHistoryView != null) {
- ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
- t.increment();
- EventBus.getDefault().send(new HideHistoryEvent(false /* animate */, t));
- t.decrement();
+ EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
}
// Notify that recents is now hidden
@@ -511,11 +505,7 @@
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState.getBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, false)) {
- ReferenceCountedTrigger postHideStackAnimationTrigger =
- new ReferenceCountedTrigger(this);
- postHideStackAnimationTrigger.increment();
- EventBus.getDefault().send(new ShowHistoryEvent(postHideStackAnimationTrigger));
- postHideStackAnimationTrigger.decrement();
+ EventBus.getDefault().send(new ShowHistoryEvent());
}
}
@@ -644,16 +634,14 @@
} else if (event.triggeredFromHomeKey) {
// Otherwise, dismiss Recents to Home
if (mHistoryView != null && mHistoryView.isVisible()) {
- ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
- t.increment();
- t.addLastDecrementRunnable(new Runnable() {
+ HideHistoryEvent hideEvent = new HideHistoryEvent(true /* animate */);
+ hideEvent.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
dismissRecentsToHome(true /* animated */);
}
});
- EventBus.getDefault().send(new HideHistoryEvent(true, t));
- t.decrement();
+ EventBus.getDefault().send(hideEvent);
} else {
dismissRecentsToHome(true /* animated */);
@@ -665,7 +653,7 @@
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
// Try and start the enter animation (or restart it on configuration changed)
- ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
+ ReferenceCountedTrigger t = new ReferenceCountedTrigger();
ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
ctx.postAnimationTrigger.increment();
if (mSearchWidgetInfo != null) {
@@ -784,11 +772,11 @@
// provided.
mHistoryView.setSystemInsets(mRecentsView.getSystemInsets());
}
- mHistoryView.show(mRecentsView.getTaskStack(), event.postHideStackAnimationTrigger);
+ mHistoryView.show(mRecentsView.getTaskStack(), event.getAnimationTrigger());
}
public final void onBusEvent(HideHistoryEvent event) {
- mHistoryView.hide(event.animate, event.postHideHistoryAnimationTrigger);
+ mHistoryView.hide(event.animate, event.getAnimationTrigger());
}
private void refreshSearchWidgetView() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 8a0a043..949fb86 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -207,7 +207,7 @@
RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
loader.preloadTasks(plan, true /* isTopTaskHome */);
RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
+ launchOpts.numVisibleTasks = loader.getIconCacheSize();
launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
launchOpts.onlyLoadForCache = true;
loader.loadTasks(mContext, plan, launchOpts);
@@ -452,7 +452,7 @@
}
// Launch the task
- ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts);
+ ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts);
}
/**
@@ -524,7 +524,7 @@
MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
// Launch the task
- ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.activityLabel, launchOpts);
+ ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts);
}
public void showNextAffiliatedTask() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index d72218f..5c49ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -28,6 +28,8 @@
import android.util.Log;
import android.util.MutableBoolean;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -220,6 +222,20 @@
// Only accessible from derived events
protected Event() {}
+ /**
+ * Called by the EventBus prior to dispatching this event to any subscriber of this event.
+ */
+ void onPreDispatch() {
+ // Do nothing
+ }
+
+ /**
+ * Called by the EventBus after dispatching this event to every subscriber of this event.
+ */
+ void onPostDispatch() {
+ // Do nothing
+ }
+
@Override
protected Object clone() throws CloneNotSupportedException {
Event evt = (Event) super.clone();
@@ -230,6 +246,51 @@
}
/**
+ * An event that represents an animated state change, which allows subscribers to coordinate
+ * callbacks which happen after the animation has taken place.
+ *
+ * Internally, it is guaranteed that increment() and decrement() will be called before and the
+ * after the event is dispatched.
+ */
+ public static class AnimatedEvent extends Event {
+
+ private final ReferenceCountedTrigger mTrigger = new ReferenceCountedTrigger();
+
+ // Only accessible from derived events
+ protected AnimatedEvent() {}
+
+ /**
+ * Returns the reference counted trigger that coordinates the animations for this event.
+ */
+ public ReferenceCountedTrigger getAnimationTrigger() {
+ return mTrigger;
+ }
+
+ /**
+ * Adds a callback that is guaranteed to be called after the state has changed regardless of
+ * whether an actual animation took place.
+ */
+ public void addPostAnimationCallback(Runnable r) {
+ mTrigger.addLastDecrementRunnable(r);
+ }
+
+ @Override
+ void onPreDispatch() {
+ mTrigger.increment();
+ }
+
+ @Override
+ void onPostDispatch() {
+ mTrigger.decrement();
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ throw new CloneNotSupportedException();
+ }
+ }
+
+ /**
* An inter-process event super class that allows us to track user state across subscriber
* invocations.
*/
@@ -706,6 +767,11 @@
if (eventHandlers == null) {
return;
}
+
+ // Prepare this event
+ boolean hasPostedEvent = false;
+ event.onPreDispatch();
+
// We need to clone the list in case a subscriber unregisters itself during traversal
eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
for (final EventHandler eventHandler : eventHandlers) {
@@ -717,11 +783,24 @@
processEvent(eventHandler, event);
}
});
+ hasPostedEvent = true;
} else {
processEvent(eventHandler, event);
}
}
}
+
+ // Clean up after this event, deferring until all subscribers have been called
+ if (hasPostedEvent) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ event.onPostDispatch();
+ }
+ });
+ } else {
+ event.onPostDispatch();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
index 3412852..e85dea3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
@@ -22,17 +22,11 @@
/**
* This is sent when the history view will be closed.
*/
-public class HideHistoryEvent extends EventBus.Event {
+public class HideHistoryEvent extends EventBus.AnimatedEvent {
public final boolean animate;
- public final ReferenceCountedTrigger postHideHistoryAnimationTrigger;
- /**
- * @param postHideHistoryAnimationTrigger the trigger that gets called when all the history animations are finished
- * when transitioning from the history view
- */
- public HideHistoryEvent(boolean animate, ReferenceCountedTrigger postHideHistoryAnimationTrigger) {
+ public HideHistoryEvent(boolean animate) {
this.animate = animate;
- this.postHideHistoryAnimationTrigger = postHideHistoryAnimationTrigger;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
index c91752e..94e5a97 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
@@ -22,15 +22,8 @@
/**
* This is sent when the history view button is clicked.
*/
-public class ShowHistoryEvent extends EventBus.Event {
+public class ShowHistoryEvent extends EventBus.AnimatedEvent {
- public final ReferenceCountedTrigger postHideStackAnimationTrigger;
+ // Simple event
- /**
- * @param postHideStackAnimationTrigger the trigger that gets called when all the task animations are finished when
- * transitioning to the history view
- */
- public ShowHistoryEvent(ReferenceCountedTrigger postHideStackAnimationTrigger) {
- this.postHideStackAnimationTrigger = postHideStackAnimationTrigger;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
index 3deeb47..8aa4631 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
@@ -25,18 +25,15 @@
/**
* This event is sent whenever a drag ends.
*/
-public class DragEndEvent extends EventBus.Event {
+public class DragEndEvent extends EventBus.AnimatedEvent {
public final Task task;
public final TaskView taskView;
public final DropTarget dropTarget;
- public final ReferenceCountedTrigger postAnimationTrigger;
- public DragEndEvent(Task task, TaskView taskView, DropTarget dropTarget,
- ReferenceCountedTrigger postAnimationTrigger) {
+ public DragEndEvent(Task task, TaskView taskView, DropTarget dropTarget) {
this.task = task;
this.taskView = taskView;
this.dropTarget = dropTarget;
- this.postAnimationTrigger = postAnimationTrigger;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
index 76439c0..72ec7b7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
@@ -31,7 +31,6 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
import com.android.systemui.recents.events.activity.HideHistoryEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
@@ -72,7 +71,7 @@
public void onTaskDataLoaded(Task task) {
// This callback is only made for TaskRow view holders
ImageView iv = (ImageView) content.findViewById(R.id.icon);
- iv.setImageDrawable(task.applicationIcon);
+ iv.setImageDrawable(task.icon);
}
@Override
@@ -128,7 +127,7 @@
@Override
public void onClick(View v) {
SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startActivityFromRecents(v.getContext(), task.key.id, task.activityLabel,
+ ssp.startActivityFromRecents(v.getContext(), task.key.id, task.title,
ActivityOptions.makeBasic());
}
@@ -240,7 +239,7 @@
TaskRow taskRow = (TaskRow) row;
taskRow.task.addCallback(holder);
TextView tv = (TextView) holder.content.findViewById(R.id.description);
- tv.setText(taskRow.task.activityLabel);
+ tv.setText(taskRow.task.title);
holder.content.setOnClickListener(taskRow);
loader.loadTaskData(taskRow.task);
break;
@@ -313,10 +312,7 @@
* Dismisses history back to the stack view.
*/
private void dismissHistory() {
- ReferenceCountedTrigger t = new ReferenceCountedTrigger(mContext);
- t.increment();
- EventBus.getDefault().send(new HideHistoryEvent(true /* animate */, t));
- t.decrement();
+ EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
EventBus.getDefault().send(new HideHistoryButtonEvent());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
index b06539a..367f2e2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -29,9 +29,6 @@
*/
public class ReferenceCountedTrigger {
- private static final String TAG = "ReferenceCountedTrigger";
-
- Context mContext;
int mCount;
ArrayList<Runnable> mFirstIncRunnables = new ArrayList<Runnable>();
ArrayList<Runnable> mLastDecRunnables = new ArrayList<Runnable>();
@@ -51,13 +48,12 @@
}
};
- public ReferenceCountedTrigger(Context context) {
- this(context, null, null, null);
+ public ReferenceCountedTrigger() {
+ this(null, null, null);
}
- public ReferenceCountedTrigger(Context context, Runnable firstIncRunnable,
- Runnable lastDecRunnable, Runnable errorRunanable) {
- mContext = context;
+ public ReferenceCountedTrigger(Runnable firstIncRunnable, Runnable lastDecRunnable,
+ Runnable errorRunanable) {
if (firstIncRunnable != null) mFirstIncRunnables.add(firstIncRunnable);
if (lastDecRunnable != null) mLastDecRunnables.add(lastDecRunnable);
mErrorRunnable = errorRunanable;
@@ -81,22 +77,14 @@
/** Adds a runnable to the last-decrement runnables list. */
public void addLastDecrementRunnable(Runnable r) {
- // To ensure that the last decrement always calls, we increment and decrement after setting
- // the last decrement runnable
- boolean ensureLastDecrement = (mCount == 0);
- if (ensureLastDecrement) increment();
mLastDecRunnables.add(r);
- if (ensureLastDecrement) decrement();
}
/** Decrements the ref count */
public void decrement() {
mCount--;
- if (mCount == 0 && !mLastDecRunnables.isEmpty()) {
- int numRunnables = mLastDecRunnables.size();
- for (int i = 0; i < numRunnables; i++) {
- mLastDecRunnables.get(i).run();
- }
+ if (mCount == 0) {
+ flushLastDecrementRunnables();
} else if (mCount < 0) {
if (mErrorRunnable != null) {
mErrorRunnable.run();
@@ -106,6 +94,19 @@
}
}
+ /**
+ * Runs and clears all the last-decrement runnables now.
+ */
+ public void flushLastDecrementRunnables() {
+ if (!mLastDecRunnables.isEmpty()) {
+ int numRunnables = mLastDecRunnables.size();
+ for (int i = 0; i < numRunnables; i++) {
+ mLastDecRunnables.get(i).run();
+ }
+ }
+ mLastDecRunnables.clear();
+ }
+
/** Convenience method to decrement this trigger as a runnable. */
public Runnable decrementAsRunnable() {
return mDecrementRunnable;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 5888b30..6a9268a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -43,6 +43,7 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -572,7 +573,7 @@
* Returns the activity icon for the ActivityInfo for a user, badging if
* necessary.
*/
- public Drawable getActivityIcon(ActivityInfo info, int userId) {
+ public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
if (mPm == null) return null;
// If we are mocking, then return a mock label
@@ -585,9 +586,31 @@
}
/**
+ * Returns the task description icon, loading and badging it if it necessary.
+ */
+ public Drawable getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription,
+ int userId, Resources res) {
+
+ // If we are mocking, then return a mock label
+ if (RecentsDebugFlags.Static.EnableSystemServicesProxy) {
+ return new ColorDrawable(0xFF666666);
+ }
+
+ Bitmap tdIcon = taskDescription.getInMemoryIcon();
+ if (tdIcon == null) {
+ tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
+ taskDescription.getIconFilename(), userId);
+ }
+ if (tdIcon != null) {
+ return getBadgedIcon(new BitmapDrawable(res, tdIcon), userId);
+ }
+ return null;
+ }
+
+ /**
* Returns the given icon for a user, badging if necessary.
*/
- public Drawable getBadgedIcon(Drawable icon, int userId) {
+ private Drawable getBadgedIcon(Drawable icon, int userId) {
if (userId != UserHandle.myUserId()) {
icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
}
@@ -597,7 +620,7 @@
/**
* Returns the given label for a user, badging if necessary.
*/
- public String getBadgedLabel(String label, int userId) {
+ private String getBadgedLabel(String label, int userId) {
if (userId != UserHandle.myUserId()) {
label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
}
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 1d18087..7a92b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -29,7 +29,6 @@
import com.android.systemui.Prefs;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.misc.SystemServicesProxy;
import java.util.ArrayList;
@@ -47,9 +46,6 @@
*/
public class RecentsTaskLoadPlan {
- private static String TAG = "RecentsTaskLoadPlan";
- private static boolean DEBUG = false;
-
private static int MIN_NUM_TASKS = 5;
private static int SESSION_BEGIN_TIME = 1000 /* ms/s */ * 60 /* s/min */ * 60 /* min/hr */ *
6 /* hrs */;
@@ -107,13 +103,6 @@
// 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());
- for (ActivityManager.RecentTaskInfo info : mRawTasks) {
- Log.d(TAG, " " + info.baseIntent + ", " + info.lastActiveTime);
- }
- }
}
/**
@@ -126,8 +115,6 @@
* - 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();
@@ -149,38 +136,26 @@
// This task is only shown in the stack if it statisfies the historical time or min
// number of tasks constraints. Freeform tasks are also always shown.
- boolean isStackTask = true;
boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
- isStackTask = isFreeformTask || (!isHistoricalTask(t) ||
- (t.lastActiveTime >= lastStackActiveTime &&
- i >= (taskCount - MIN_NUM_TASKS)));
+ boolean isStackTask = isFreeformTask || (!isHistoricalTask(t) ||
+ (t.lastActiveTime >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS)));
if (isStackTask && newLastStackActiveTime < 0) {
newLastStackActiveTime = t.lastActiveTime;
}
- // Load the label, icon, and color
- String activityLabel = loader.getAndUpdateActivityLabel(taskKey, t.taskDescription,
- ssp);
- String contentDescription = loader.getAndUpdateContentDescription(taskKey,
- activityLabel, ssp, res);
- Drawable activityIcon = isStackTask
- ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, ssp, res, false)
+ // Load the title, icon, and color
+ String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
+ String contentDescription = loader.getAndUpdateContentDescription(taskKey, title, res);
+ Drawable icon = isStackTask
+ ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
: null;
+ Bitmap thumbnail = loader.getAndUpdateThumbnail(taskKey, false);
int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
- Bitmap icon = t.taskDescription != null
- ? t.taskDescription.getInMemoryIcon() : null;
- String iconFilename = t.taskDescription != null
- ? t.taskDescription.getIconFilename() : null;
-
// Add the task to the stack
- Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel,
- contentDescription, activityIcon, activityColor, (i == (taskCount - 1)),
- config.lockToAppEnabled, !isStackTask, icon, iconFilename, t.bounds);
- task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, false);
- if (DEBUG) {
- Log.d(TAG, activityLabel + " bounds: " + t.bounds);
- }
+ Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
+ thumbnail, title, contentDescription, activityColor, !isStackTask,
+ (i == (taskCount - 1)), config.lockToAppEnabled, t.bounds, t.taskDescription);
allTasks.add(task);
}
@@ -200,19 +175,13 @@
*/
public synchronized void executePlan(Options opts, RecentsTaskLoader loader,
TaskResourceLoadQueue loadQueue) {
- if (DEBUG) Log.d(TAG, "executePlan, # tasks: " + opts.numVisibleTasks +
- ", # thumbnails: " + opts.numVisibleTaskThumbnails +
- ", running task id: " + opts.runningTaskId);
-
RecentsConfiguration config = Recents.getConfiguration();
- SystemServicesProxy ssp = Recents.getSystemServices();
Resources res = mContext.getResources();
// Iterate through each of the tasks and load them according to the load conditions.
ArrayList<Task> tasks = mStack.getStackTasks();
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
- ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
Task task = tasks.get(i);
Task.TaskKey taskKey = task.key;
@@ -226,17 +195,15 @@
}
if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
- if (task.activityIcon == null) {
- if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
- task.activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
- ssp, res, true);
+ if (task.icon == null) {
+ task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
+ res, true);
}
}
if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
if (task.thumbnail == null || isRunningTask) {
- if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
if (config.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
- task.thumbnail = loader.getAndUpdateThumbnail(taskKey, ssp, true);
+ task.thumbnail = loader.getAndUpdateThumbnail(taskKey, true);
} 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 c72d166..28338d83 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -93,23 +93,23 @@
Handler mMainThreadHandler;
TaskResourceLoadQueue mLoadQueue;
- TaskKeyLruCache<Drawable> mApplicationIconCache;
+ TaskKeyLruCache<Drawable> mIconCache;
TaskKeyLruCache<Bitmap> mThumbnailCache;
Bitmap mDefaultThumbnail;
- BitmapDrawable mDefaultApplicationIcon;
+ BitmapDrawable mDefaultIcon;
boolean mCancelled;
boolean mWaitingOnLoadQueue;
/** Constructor, creates a new loading thread that loads task resources in the background */
public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
- TaskKeyLruCache<Drawable> applicationIconCache, TaskKeyLruCache<Bitmap> thumbnailCache,
- Bitmap defaultThumbnail, BitmapDrawable defaultApplicationIcon) {
+ TaskKeyLruCache<Drawable> iconCache, TaskKeyLruCache<Bitmap> thumbnailCache,
+ Bitmap defaultThumbnail, BitmapDrawable defaultIcon) {
mLoadQueue = loadQueue;
- mApplicationIconCache = applicationIconCache;
+ mIconCache = iconCache;
mThumbnailCache = thumbnailCache;
mDefaultThumbnail = defaultThumbnail;
- mDefaultApplicationIcon = defaultApplicationIcon;
+ mDefaultIcon = defaultIcon;
mMainThreadHandler = new Handler();
mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
android.os.Process.THREAD_PRIORITY_BACKGROUND);
@@ -163,30 +163,30 @@
// Load the next item from the queue
final Task t = mLoadQueue.nextTask();
if (t != null) {
- Drawable cachedIcon = mApplicationIconCache.get(t.key);
+ Drawable cachedIcon = mIconCache.get(t.key);
Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
- // Load the application icon if it is stale or we haven't cached one yet
+ // Load the icon if it is stale or we haven't cached one yet
if (cachedIcon == null) {
- cachedIcon = getTaskDescriptionIcon(t.key, t.icon, t.iconFilename, ssp,
- mContext.getResources());
+ cachedIcon = ssp.getBadgedTaskDescriptionIcon(t.taskDescription,
+ t.key.userId, mContext.getResources());
if (cachedIcon == null) {
ActivityInfo info = ssp.getActivityInfo(
t.key.getComponent(), t.key.userId);
if (info != null) {
if (DEBUG) Log.d(TAG, "Loading icon: " + t.key);
- cachedIcon = ssp.getActivityIcon(info, t.key.userId);
+ cachedIcon = ssp.getBadgedActivityIcon(info, t.key.userId);
}
}
if (cachedIcon == null) {
- cachedIcon = mDefaultApplicationIcon;
+ cachedIcon = mDefaultIcon;
}
// At this point, even if we can't load the icon, we will set the
// default icon.
- mApplicationIconCache.put(t.key, cachedIcon);
+ mIconCache.put(t.key, cachedIcon);
}
// Load the thumbnail if it is stale or we haven't cached one yet
if (cachedThumbnail == null) {
@@ -234,25 +234,6 @@
}
}
}
-
- Drawable getTaskDescriptionIcon(Task.TaskKey taskKey, Bitmap iconBitmap, String iconFilename,
- SystemServicesProxy ssp, Resources res) {
- Bitmap tdIcon = null;
- if (iconBitmap != null) {
- tdIcon = iconBitmap;
- } else {
- try {
- tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(iconFilename,
- taskKey.userId);
- } catch (Exception e) {
- // TODO: Investigate for b/26221779
- }
- }
- if (tdIcon != null) {
- return ssp.getBadgedIcon(new BitmapDrawable(res, tdIcon), taskKey.userId);
- }
- return null;
- }
}
/**
@@ -269,7 +250,7 @@
// active time. Instead, we rely on the RecentsPackageMonitor to keep us informed whenever a
// package in the cache has been updated, so that we may remove it.
private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
- private final TaskKeyLruCache<Drawable> mApplicationIconCache;
+ private final TaskKeyLruCache<Drawable> mIconCache;
private final TaskKeyLruCache<Bitmap> mThumbnailCache;
private final TaskKeyLruCache<String> mActivityLabelCache;
private final TaskKeyLruCache<String> mContentDescriptionCache;
@@ -282,7 +263,7 @@
private int mNumVisibleThumbnailsLoaded;
int mDefaultTaskBarBackgroundColor;
- BitmapDrawable mDefaultApplicationIcon;
+ BitmapDrawable mDefaultIcon;
Bitmap mDefaultThumbnail;
public RecentsTaskLoader(Context context) {
@@ -302,22 +283,22 @@
mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
mDefaultThumbnail.setHasAlpha(false);
mDefaultThumbnail.eraseColor(0xFFffffff);
- mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
+ mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
// Initialize the proxy, cache and loaders
int numRecentTasks = ActivityManager.getMaxRecentTasksStatic();
mLoadQueue = new TaskResourceLoadQueue();
- mApplicationIconCache = new TaskKeyLruCache<>(iconCacheSize);
+ mIconCache = new TaskKeyLruCache<>(iconCacheSize);
mThumbnailCache = new TaskKeyLruCache<>(thumbnailCacheSize);
mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks);
mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks);
mActivityInfoCache = new LruCache(numRecentTasks);
- mLoader = new BackgroundTaskLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
- mDefaultThumbnail, mDefaultApplicationIcon);
+ mLoader = new BackgroundTaskLoader(mLoadQueue, mIconCache, mThumbnailCache,
+ mDefaultThumbnail, mDefaultIcon);
}
/** Returns the size of the app icon cache. */
- public int getApplicationIconCacheSize() {
+ public int getIconCacheSize() {
return mMaxIconCacheSize;
}
@@ -355,33 +336,33 @@
/** Acquires the task resource data directly from the pool. */
public void loadTaskData(Task t) {
- Drawable applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(t.key);
+ Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
// Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
// use the default assets in their place until they load
- boolean requiresLoad = (applicationIcon == null) || (thumbnail == null);
- applicationIcon = applicationIcon != null ? applicationIcon : mDefaultApplicationIcon;
+ boolean requiresLoad = (icon == null) || (thumbnail == null);
+ icon = icon != null ? icon : mDefaultIcon;
if (requiresLoad) {
mLoadQueue.addTask(t);
}
- t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, applicationIcon);
+ t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon);
}
/** Releases the task resource data back into the pool. */
public void unloadTaskData(Task t) {
mLoadQueue.removeTask(t);
- t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
+ t.notifyTaskDataUnloaded(null, mDefaultIcon);
}
/** Completely removes the resource data from the pool. */
public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
mLoadQueue.removeTask(t);
mThumbnailCache.remove(t.key);
- mApplicationIconCache.remove(t.key);
+ mIconCache.remove(t.key);
mActivityInfoCache.remove(t.key.getComponent());
if (notifyTaskDataUnloaded) {
- t.notifyTaskDataUnloaded(null, mDefaultApplicationIcon);
+ t.notifyTaskDataUnloaded(null, mDefaultIcon);
}
}
@@ -403,14 +384,14 @@
} else if (config.svelteLevel >= RecentsConfiguration.SVELTE_DISABLE_CACHE) {
mThumbnailCache.evictAll();
}
- mApplicationIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
+ mIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
mMaxIconCacheSize / 2));
break;
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
// We are leaving recents, so trim the data a bit
mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 2));
- mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
+ mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
mActivityInfoCache.trimToSize(Math.max(1,
ActivityManager.getMaxRecentTasksStatic() / 2));
break;
@@ -418,7 +399,7 @@
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
// We are going to be low on memory
mThumbnailCache.trimToSize(Math.max(1, mMaxThumbnailCacheSize / 4));
- mApplicationIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
+ mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
mActivityInfoCache.trimToSize(Math.max(1,
ActivityManager.getMaxRecentTasksStatic() / 4));
break;
@@ -426,7 +407,7 @@
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
// We are low on memory, so release everything
mThumbnailCache.evictAll();
- mApplicationIconCache.evictAll();
+ mIconCache.evictAll();
mActivityInfoCache.evictAll();
// The cache is small, only clear the label cache when we are critical
mActivityLabelCache.evictAll();
@@ -440,8 +421,9 @@
/**
* Returns the cached task label if the task key is not expired, updating the cache if it is.
*/
- String getAndUpdateActivityLabel(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
- SystemServicesProxy ssp) {
+ String getAndUpdateActivityTitle(Task.TaskKey taskKey, ActivityManager.TaskDescription td) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
// Return the task description label if it exists
if (td != null && td.getLabel() != null) {
return td.getLabel();
@@ -452,7 +434,7 @@
return label;
}
// All short paths failed, load the label from the activity info and cache it
- ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey, ssp);
+ ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
if (activityInfo != null) {
label = ssp.getActivityLabel(activityInfo);
mActivityLabelCache.put(taskKey, label);
@@ -468,7 +450,9 @@
* cache if it is.
*/
String getAndUpdateContentDescription(Task.TaskKey taskKey, String activityLabel,
- SystemServicesProxy ssp, Resources res) {
+ Resources res) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
// Return the cached content description if it exists
String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
if (label != null) {
@@ -493,28 +477,29 @@
* Returns the cached task icon if the task key is not expired, updating the cache if it is.
*/
Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
- SystemServicesProxy ssp, Resources res, boolean loadIfNotCached) {
+ Resources res, boolean loadIfNotCached) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
// Return the cached activity icon if it exists
- Drawable icon = mApplicationIconCache.getAndInvalidateIfModified(taskKey);
+ Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey);
if (icon != null) {
return icon;
}
if (loadIfNotCached) {
// Return and cache the task description icon if it exists
- icon = mLoader.getTaskDescriptionIcon(taskKey, td.getInMemoryIcon(),
- td.getIconFilename(), ssp, res);
+ icon = ssp.getBadgedTaskDescriptionIcon(td, taskKey.userId, res);
if (icon != null) {
- mApplicationIconCache.put(taskKey, icon);
+ mIconCache.put(taskKey, icon);
return icon;
}
// Load the icon from the activity info and cache it
- ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey, ssp);
+ ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
if (activityInfo != null) {
- icon = ssp.getActivityIcon(activityInfo, taskKey.userId);
+ icon = ssp.getBadgedActivityIcon(activityInfo, taskKey.userId);
if (icon != null) {
- mApplicationIconCache.put(taskKey, icon);
+ mIconCache.put(taskKey, icon);
return icon;
}
}
@@ -526,8 +511,9 @@
/**
* Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
*/
- Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, SystemServicesProxy ssp,
- boolean loadIfNotCached) {
+ Bitmap getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+
// Return the cached thumbnail if it exists
Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
if (thumbnail != null) {
@@ -550,7 +536,8 @@
}
/**
- * Returns the task's primary color.
+ * Returns the task's primary color if possible, defaulting to the default color if there is
+ * no specified primary color.
*/
int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
if (td != null && td.getPrimaryColor() != 0) {
@@ -563,7 +550,8 @@
* Returns the activity info for the given task key, retrieving one from the system if the
* task key is expired.
*/
- private ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey, SystemServicesProxy ssp) {
+ private ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
ComponentName cn = taskKey.getComponent();
ActivityInfo activityInfo = mActivityInfoCache.get(cn);
if (activityInfo == null) {
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 73c0adb..34a0e52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents.model;
+import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -92,24 +93,46 @@
}
public TaskKey key;
+
+ /**
+ * The group will be computed separately from the initialization of the task
+ */
public TaskGrouping group;
- // The taskAffiliationId is the task id of the parent task or itself if it is not affiliated with any task
- public int taskAffiliationId;
- public int taskAffiliationColor;
- public boolean isLaunchTarget;
- public Drawable applicationIcon;
- public Drawable activityIcon;
+ /**
+ * The affiliationTaskId is the task id of the parent task or itself if it is not affiliated
+ * with any task.
+ */
+ public int affiliationTaskId;
+ public int affiliationColor;
+
+ /**
+ * The icon is the task description icon (if provided), which falls back to the activity icon,
+ * which can then fall back to the application icon.
+ */
+ public Drawable icon;
+ public Bitmap thumbnail;
+ public String title;
public String contentDescription;
- public String activityLabel;
public int colorPrimary;
public boolean useLightOnPrimaryColor;
- public Bitmap thumbnail;
+
+ /**
+ * The bounds of the task, used only if it is a freeform task.
+ */
+ public Rect bounds;
+
+ /**
+ * The task description for this task, only used to reload task icons.
+ */
+ public ActivityManager.TaskDescription taskDescription;
+
+ /**
+ * The state isLaunchTarget will be set for the correct task upon launching Recents.
+ */
+ public boolean isLaunchTarget;
+ public boolean isHistorical;
public boolean lockToThisTask;
public boolean lockToTaskEnabled;
- public boolean isHistorical;
- public Bitmap icon;
- public String iconFilename;
- public Rect bounds;
private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
@@ -117,45 +140,46 @@
// Do nothing
}
- public Task(TaskKey key, int taskAffiliation, int taskAffiliationColor,
- String activityTitle, String contentDescription, Drawable activityIcon,
- int colorPrimary, boolean lockToThisTask, boolean lockToTaskEnabled,
- boolean isHistorical, Bitmap icon, String iconFilename, Rect bounds) {
- boolean isInAffiliationGroup = (taskAffiliation != key.id);
- boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0);
+ public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
+ Bitmap thumbnail, String title, String contentDescription, int colorPrimary,
+ boolean isHistorical, boolean lockToThisTask, boolean lockToTaskEnabled,
+ Rect bounds, ActivityManager.TaskDescription taskDescription) {
+ boolean isInAffiliationGroup = (affiliationTaskId != key.id);
+ boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
this.key = key;
- this.taskAffiliationId = taskAffiliation;
- this.taskAffiliationColor = taskAffiliationColor;
- this.activityLabel = activityTitle;
+ this.affiliationTaskId = affiliationTaskId;
+ this.affiliationColor = affiliationColor;
+ this.icon = icon;
+ this.thumbnail = thumbnail;
+ this.title = title;
this.contentDescription = contentDescription;
- this.activityIcon = activityIcon;
- this.colorPrimary = hasAffiliationGroupColor ? taskAffiliationColor : colorPrimary;
+ this.colorPrimary = hasAffiliationGroupColor ? affiliationColor : colorPrimary;
this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
Color.WHITE) > 3f;
+ this.bounds = bounds;
+ this.taskDescription = taskDescription;
+ this.isHistorical = isHistorical;
this.lockToThisTask = lockToTaskEnabled && lockToThisTask;
this.lockToTaskEnabled = lockToTaskEnabled;
- this.isHistorical = isHistorical;
- this.icon = icon;
- this.iconFilename = iconFilename;
- this.bounds = bounds;
}
/** Copies the other task. */
public void copyFrom(Task o) {
this.key = o.key;
- this.taskAffiliationId = o.taskAffiliationId;
- this.taskAffiliationColor = o.taskAffiliationColor;
- this.activityLabel = o.activityLabel;
+ this.group = o.group;
+ this.affiliationTaskId = o.affiliationTaskId;
+ this.affiliationColor = o.affiliationColor;
+ this.icon = o.icon;
+ this.thumbnail = o.thumbnail;
+ this.title = o.title;
this.contentDescription = o.contentDescription;
- this.activityIcon = o.activityIcon;
this.colorPrimary = o.colorPrimary;
this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
+ this.bounds = o.bounds;
+ this.isLaunchTarget = o.isLaunchTarget;
+ this.isHistorical = o.isHistorical;
this.lockToThisTask = o.lockToThisTask;
this.lockToTaskEnabled = o.lockToTaskEnabled;
- this.isHistorical = o.isHistorical;
- this.icon = o.icon;
- this.iconFilename = o.iconFilename;
- this.bounds = o.bounds;
}
/**
@@ -200,7 +224,7 @@
/** Notifies the callback listeners that this task has been loaded */
public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
- this.applicationIcon = applicationIcon;
+ this.icon = applicationIcon;
this.thumbnail = thumbnail;
int callbackCount = mCallbacks.size();
for (int i = 0; i < callbackCount; i++) {
@@ -210,7 +234,7 @@
/** Notifies the callback listeners that this task has been unloaded */
public void notifyTaskDataUnloaded(Bitmap defaultThumbnail, Drawable defaultApplicationIcon) {
- applicationIcon = defaultApplicationIcon;
+ icon = defaultApplicationIcon;
thumbnail = defaultThumbnail;
int callbackCount = mCallbacks.size();
for (int i = 0; i < callbackCount; i++) {
@@ -222,7 +246,7 @@
* Returns whether this task is affiliated with another task.
*/
public boolean isAffiliatedTask() {
- return key.id != taskAffiliationId;
+ return key.id != affiliationTaskId;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 3484c38..d06012e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -232,16 +232,16 @@
public static final DockState NONE = new DockState(-1, 96, null, null);
public static final DockState LEFT = new DockState(
DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
- new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.25f, 1));
+ new RectF(0, 0, 0.15f, 1), new RectF(0, 0, 0.15f, 1));
public static final DockState TOP = new DockState(
DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
- new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.25f));
+ new RectF(0, 0, 1, 0.15f), new RectF(0, 0, 1, 0.15f));
public static final DockState RIGHT = new DockState(
DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
- new RectF(0.75f, 0, 1, 1), new RectF(0.75f, 0, 1, 1));
+ new RectF(0.85f, 0, 1, 1), new RectF(0.85f, 0, 1, 1));
public static final DockState BOTTOM = new DockState(
DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
- new RectF(0, 0.75f, 1, 1), new RectF(0, 0.75f, 1, 1));
+ new RectF(0, 0.85f, 1, 1), new RectF(0, 0.85f, 1, 1));
@Override
public boolean acceptsDrop(int x, int y, int width, int height) {
@@ -354,7 +354,7 @@
if (t.isAffiliatedTask()) {
// If this task is affiliated with another parent in the stack, then the historical state of this
// task depends on the state of the parent task
- Task parentTask = taskIdMap.get(t.taskAffiliationId);
+ Task parentTask = taskIdMap.get(t.affiliationTaskId);
if (parentTask != null) {
t = parentTask;
}
@@ -368,7 +368,7 @@
if (t.isAffiliatedTask()) {
// If this task is affiliated with another parent in the stack, then the historical state of this
// task depends on the state of the parent task
- Task parentTask = taskIdMap.get(t.taskAffiliationId);
+ Task parentTask = taskIdMap.get(t.affiliationTaskId);
if (parentTask != null) {
t = parentTask;
}
@@ -716,7 +716,7 @@
for (int i = 0; i < taskCount; i++) {
Task t = tasks.get(i);
TaskGrouping group;
- int affiliation = t.taskAffiliationId > 0 ? t.taskAffiliationId :
+ int affiliation = t.affiliationTaskId > 0 ? t.affiliationTaskId :
IndividualTaskIdOffset + t.key.id;
if (mAffinitiesGroups.containsKey(affiliation)) {
group = getGroupWithAffiliation(affiliation);
@@ -737,7 +737,7 @@
// Ignore the groups that only have one task
if (taskCount <= 1) continue;
// Calculate the group color distribution
- int affiliationColor = tasksMap.get(group.mTaskKeys.get(0)).taskAffiliationColor;
+ int affiliationColor = tasksMap.get(group.mTaskKeys.get(0)).affiliationColor;
float alphaStep = (1f - minAlpha) / taskCount;
float alpha = 1f;
for (int j = 0; j < taskCount; j++) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 135f0f9..0af7c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -157,7 +157,7 @@
ActivityOptions opts, IAppTransitionAnimationSpecsFuture transitionFuture,
final ActivityOptions.OnAnimationStartedListener animStartedListener) {
SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.startActivityFromRecents(mContext, task.key.id, task.activityLabel, opts)) {
+ if (ssp.startActivityFromRecents(mContext, task.key.id, task.title, opts)) {
// Keep track of the index of the task launch
int taskIndexFromFront = 0;
int taskIndex = stack.indexOfStackTask(task);
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 53c02cb..9b1315a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -132,10 +132,7 @@
mHistoryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- ReferenceCountedTrigger postHideStackAnimationTrigger = new ReferenceCountedTrigger(v.getContext());
- postHideStackAnimationTrigger.increment();
- EventBus.getDefault().send(new ShowHistoryEvent(postHideStackAnimationTrigger));
- postHideStackAnimationTrigger.decrement();
+ EventBus.getDefault().send(new ShowHistoryEvent());
}
});
addView(mHistoryButton);
@@ -576,8 +573,8 @@
public final void onBusEvent(ShowHistoryEvent event) {
// Hide the history button when the history view is shown
hideHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
- event.postHideStackAnimationTrigger);
- event.postHideStackAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ event.getAnimationTrigger());
+ event.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
setAlpha(0f);
@@ -589,7 +586,7 @@
// Show the history button when the history view is hidden
setAlpha(1f);
showHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
- event.postHideHistoryAnimationTrigger);
+ event.getAnimationTrigger());
}
public final void onBusEvent(ShowHistoryButtonEvent event) {
@@ -609,10 +606,9 @@
* Shows the history button.
*/
private void showHistoryButton(final int duration) {
- ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(getContext());
- postAnimationTrigger.increment();
+ ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
showHistoryButton(duration, postAnimationTrigger);
- postAnimationTrigger.decrement();
+ postAnimationTrigger.flushLastDecrementRunnables();
}
private void showHistoryButton(final int duration,
@@ -638,10 +634,9 @@
* Hides the history button.
*/
private void hideHistoryButton(int duration) {
- ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(getContext());
- postAnimationTrigger.increment();
+ ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
hideHistoryButton(duration, postAnimationTrigger);
- postAnimationTrigger.decrement();
+ postAnimationTrigger.flushLastDecrementRunnables();
}
private void hideHistoryButton(int duration,
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 37a0194..318801d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -188,12 +188,8 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
if (mDragging) {
- ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(
- mRv.getContext());
- postAnimationTrigger.increment();
EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
- mLastDropTarget, postAnimationTrigger));
- postAnimationTrigger.decrement();
+ mLastDropTarget));
break;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 9625e5d..10df156 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -118,7 +118,7 @@
*/
public static class StackState {
- public static final StackState FREEFORM_ONLY = new StackState(1f, 0);
+ public static final StackState FREEFORM_ONLY = new StackState(1f, 255);
public static final StackState STACK_ONLY = new StackState(0f, 0);
public static final StackState SPLIT = new StackState(0.5f, 255);
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 b9ca9fd..830d607 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -44,7 +44,6 @@
import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
@@ -216,7 +215,8 @@
R.drawable.recents_freeform_workspace_bg);
mFreeformWorkspaceBackground.setCallback(this);
if (ssp.hasFreeformWorkspaceSupport()) {
- setBackgroundColor(getContext().getColor(R.color.recents_freeform_workspace_bg_color));
+ mFreeformWorkspaceBackground.setColor(
+ getContext().getColor(R.color.recents_freeform_workspace_bg_color));
}
}
@@ -797,7 +797,7 @@
TaskView frontMostTask = taskViews.get(taskViewCount - 1);
event.setFromIndex(mStack.indexOfStackTask(backMostTask.getTask()));
event.setToIndex(mStack.indexOfStackTask(frontMostTask.getTask()));
- event.setContentDescription(frontMostTask.getTask().activityLabel);
+ event.setContentDescription(frontMostTask.getTask().title);
}
event.setItemCount(mStack.getStackTaskCount());
event.setScrollY(mStackScroller.mScroller.getCurrY());
@@ -1021,8 +1021,7 @@
if (launchTargetTask != null) {
occludesLaunchTarget = launchTargetTask.group.isTaskAboveTask(task,
launchTargetTask);
- hideTask = SystemServicesProxy.isFreeformStack(launchTargetTask.key.stackId) &&
- SystemServicesProxy.isFreeformStack(task.key.stackId);
+ hideTask = launchTargetTask.isFreeformTask() && task.isFreeformTask();
}
tv.prepareEnterRecentsAnimation(task.isLaunchTarget, hideTask, occludesLaunchTarget,
offscreenY);
@@ -1494,7 +1493,6 @@
(!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
(isFreeformTask && event.dropTarget == mStackDropTarget);
- event.postAnimationTrigger.increment();
if (hasChangedStacks) {
// Move the task to the right position in the stack (ie. the front of the stack if
// freeform or the front of the stack if fullscreen). Note, we MUST move the tasks
@@ -1507,7 +1505,7 @@
updateLayout(true);
// Move the task to the new stack in the system after the animation completes
- event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ event.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
SystemServicesProxy ssp = Recents.getSystemServices();
@@ -1515,8 +1513,8 @@
}
});
}
- event.taskView.animate()
- .withEndAction(event.postAnimationTrigger.decrementAsRunnable());
+ event.getAnimationTrigger().increment();
+ event.taskView.animate().withEndAction(event.getAnimationTrigger().decrementAsRunnable());
// We translated the view but we need to animate it back from the current layout-space rect
// to its final layout-space rect
@@ -1556,7 +1554,7 @@
for (int i = 0; i < taskViewCount; i++) {
TaskView tv = taskViews.get(i);
Task task = tv.getTask();
- if (SystemServicesProxy.isFreeformStack(task.key.stackId)) {
+ if (task.isFreeformTask()) {
tv.setVisibility(event.visible ? View.VISIBLE : View.INVISIBLE);
}
}
@@ -1577,9 +1575,9 @@
.setUpdateListener(null)
.setListener(null)
.withLayer()
- .withEndAction(event.postHideStackAnimationTrigger.decrementAsRunnable())
+ .withEndAction(event.getAnimationTrigger().decrementAsRunnable())
.start();
- event.postHideStackAnimationTrigger.increment();
+ event.getAnimationTrigger().increment();
}
}
@@ -1592,7 +1590,7 @@
int taskViewCount = taskViews.size();
for (int i = taskViewCount - 1; i >= 0; i--) {
final TaskView tv = taskViews.get(i);
- event.postHideHistoryAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ event.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
tv.animate()
@@ -1616,7 +1614,7 @@
// Announce for accessibility
tv.announceForAccessibility(getContext().getString(
- R.string.accessibility_recents_item_dismissed, tv.getTask().activityLabel));
+ R.string.accessibility_recents_item_dismissed, tv.getTask().title));
// Remove the task from the stack
mStack.removeTask(task);
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 df7b9a6..a3e8b2d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -651,7 +651,7 @@
*/
public void setFocusedState(boolean isFocused, boolean animated, boolean requestViewFocus) {
if (DEBUG) {
- Log.d(TAG, "setFocusedState: " + mTask.activityLabel + " focused: " + isFocused +
+ Log.d(TAG, "setFocusedState: " + mTask.title + " focused: " + isFocused +
" animated: " + animated + " requestViewFocus: " + requestViewFocus +
" isFocused(): " + isFocused() +
" isAccessibilityFocused(): " + isAccessibilityFocused());
@@ -771,7 +771,7 @@
public final void onBusEvent(DragEndEvent event) {
if (!(event.dropTarget instanceof TaskStack.DockState)) {
- event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ event.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
// Animate the drag view back from where it is, to the view location, then after
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 0271ccd..9a2ffe7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -60,8 +60,8 @@
// Header views
ImageView mMoveTaskButton;
ImageView mDismissButton;
- ImageView mApplicationIcon;
- TextView mActivityDescription;
+ ImageView mIconView;
+ TextView mTitleView;
int mMoveTaskTargetStackId = INVALID_STACK_ID;
// Header drawables
@@ -128,16 +128,16 @@
@Override
protected void onFinishInflate() {
// Initialize the icon and description views
- mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
- mApplicationIcon.setOnLongClickListener(this);
- mActivityDescription = (TextView) findViewById(R.id.activity_description);
+ mIconView = (ImageView) findViewById(R.id.icon);
+ mIconView.setOnLongClickListener(this);
+ mTitleView = (TextView) findViewById(R.id.title);
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
mDismissButton.setOnClickListener(this);
mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
// Hide the backgrounds if they are ripple drawables
- if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
- mApplicationIcon.setBackground(null);
+ if (mIconView.getBackground() instanceof RippleDrawable) {
+ mIconView.setBackground(null);
}
mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(
@@ -158,8 +158,8 @@
public void onTaskViewSizeChanged(int width, int height) {
mTaskViewRect.set(0, 0, width, height);
boolean updateMoveTaskButton = mMoveTaskButton.getVisibility() != View.GONE;
- int appIconWidth = mApplicationIcon.getMeasuredWidth();
- int activityDescWidth = mActivityDescription.getMeasuredWidth();
+ int appIconWidth = mIconView.getMeasuredWidth();
+ int activityDescWidth = mTitleView.getMeasuredWidth();
int dismissIconWidth = mDismissButton.getMeasuredWidth();
int moveTaskIconWidth = mMoveTaskButton.getVisibility() == View.VISIBLE
? mMoveTaskButton.getMeasuredWidth()
@@ -168,26 +168,26 @@
// Priority-wise, we show the activity icon first, the dismiss icon if there is room, the
// move-task icon if there is room, and then finally, the activity label if there is room
if (width < (appIconWidth + dismissIconWidth)) {
- mActivityDescription.setVisibility(View.INVISIBLE);
+ mTitleView.setVisibility(View.INVISIBLE);
if (updateMoveTaskButton) {
mMoveTaskButton.setVisibility(View.INVISIBLE);
}
mDismissButton.setVisibility(View.INVISIBLE);
} else if (width < (appIconWidth + dismissIconWidth + moveTaskIconWidth)) {
- mActivityDescription.setVisibility(View.INVISIBLE);
+ mTitleView.setVisibility(View.INVISIBLE);
if (updateMoveTaskButton) {
mMoveTaskButton.setVisibility(View.INVISIBLE);
}
mDismissButton.setVisibility(View.VISIBLE);
} else if (width < (appIconWidth + dismissIconWidth + moveTaskIconWidth +
activityDescWidth)) {
- mActivityDescription.setVisibility(View.INVISIBLE);
+ mTitleView.setVisibility(View.INVISIBLE);
if (updateMoveTaskButton) {
mMoveTaskButton.setVisibility(View.VISIBLE);
}
mDismissButton.setVisibility(View.VISIBLE);
} else {
- mActivityDescription.setVisibility(View.VISIBLE);
+ mTitleView.setVisibility(View.VISIBLE);
if (updateMoveTaskButton) {
mMoveTaskButton.setVisibility(View.VISIBLE);
}
@@ -233,15 +233,13 @@
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
// otherwise, we fall back to the application icon
- if (t.activityIcon != null) {
- mApplicationIcon.setImageDrawable(t.activityIcon);
- } else if (t.applicationIcon != null) {
- mApplicationIcon.setImageDrawable(t.applicationIcon);
+ if (t.icon != null) {
+ mIconView.setImageDrawable(t.icon);
}
- if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
- mActivityDescription.setText(t.activityLabel);
+ if (!mTitleView.getText().toString().equals(t.title)) {
+ mTitleView.setText(t.title);
}
- mActivityDescription.setContentDescription(t.contentDescription);
+ mTitleView.setContentDescription(t.contentDescription);
// Try and apply the system ui tint
int existingBgColor = (getBackground() instanceof ColorDrawable) ?
@@ -254,7 +252,7 @@
R.color.recents_task_bar_light_text_color);
int taskBarViewDarkTextColor = getResources().getColor(
R.color.recents_task_bar_dark_text_color);
- mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
+ mTitleView.setTextColor(t.useLightOnPrimaryColor ?
taskBarViewLightTextColor : taskBarViewDarkTextColor);
mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
mLightDismissDrawable : mDarkDismissDrawable);
@@ -281,15 +279,15 @@
// In accessibility, a single click on the focused app info button will show it
if (ssp.isTouchExplorationEnabled()) {
- mApplicationIcon.setOnClickListener(this);
+ mIconView.setOnClickListener(this);
}
}
/** Unbinds the bar view from the task */
void unbindFromTask() {
mTask = null;
- mApplicationIcon.setImageDrawable(null);
- mApplicationIcon.setOnClickListener(null);
+ mIconView.setImageDrawable(null);
+ mIconView.setOnClickListener(null);
mMoveTaskButton.setOnClickListener(null);
}
@@ -357,7 +355,7 @@
@Override
public void onClick(View v) {
- if (v == mApplicationIcon) {
+ if (v == mIconView) {
// In accessibility, a single click on the focused app info button will show it
EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
} else if (v == mDismissButton) {
@@ -379,7 +377,7 @@
@Override
public boolean onLongClick(View v) {
- if (v == mApplicationIcon) {
+ if (v == mIconView) {
EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 6d4dc872..b891c21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -947,7 +947,7 @@
final StatusBarNotification sbn = row.getStatusBarNotification();
PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
row.setTag(sbn.getPackageName());
- final View guts = row.getGuts();
+ final NotificationGuts guts = row.getGuts();
final String pkg = sbn.getPackageName();
String appname = pkg;
Drawable pkgicon = null;
@@ -969,8 +969,6 @@
((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
((TextView) row.findViewById(R.id.pkgname)).setText(appname);
- bindTopicImportance(sbn, row);
-
final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
if (appUid >= 0) {
final int appUidF = appUid;
@@ -983,69 +981,8 @@
} else {
settingsButton.setVisibility(View.GONE);
}
- }
- private void bindTopicImportance(final StatusBarNotification sbn,
- ExpandableNotificationRow row) {
- final INotificationManager sINM = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
- final Notification.Topic topic = sbn.getNotification().getTopic() == null
- ? new Notification.Topic(Notification.TOPIC_DEFAULT, mContext.getString(
- com.android.internal.R.string.default_notification_topic_label))
- : sbn.getNotification().getTopic();
-
- ((TextView) row.findViewById(R.id.topic_details)).setText(topic.getLabel());
- final TextView topicSummary = ((TextView) row.findViewById(R.id.summary));
- int importance = mNotificationData.getImportance(sbn.getKey());
- SeekBar seekBar = (SeekBar) row.findViewById(R.id.seekbar);
- seekBar.setMax(4);
- seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- topicSummary.setText(getProgressSummary(progress));
- if (fromUser) {
- try {
- sINM.setTopicImportance(sbn.getPackageName(), sbn.getUid(), topic,
- progress);
- } catch (RemoteException e) {
- // :(
- }
- }
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- // no-op
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- // no-op
- }
-
- private String getProgressSummary(int progress) {
- switch (progress) {
- case NotificationListenerService.Ranking.IMPORTANCE_NONE:
- return mContext.getString(
- com.android.internal.R.string.notification_importance_blocked);
- case NotificationListenerService.Ranking.IMPORTANCE_LOW:
- return mContext.getString(
- com.android.internal.R.string.notification_importance_low);
- case NotificationListenerService.Ranking.IMPORTANCE_DEFAULT:
- return mContext.getString(
- com.android.internal.R.string.notification_importance_default);
- case NotificationListenerService.Ranking.IMPORTANCE_HIGH:
- return mContext.getString(
- com.android.internal.R.string.notification_importance_high);
- case NotificationListenerService.Ranking.IMPORTANCE_MAX:
- return mContext.getString(
- com.android.internal.R.string.notification_importance_max);
- default:
- return "";
- }
- }
- });
- seekBar.setProgress(importance);
+ guts.bindImportance(sbn, row, mNotificationData.getImportance(sbn.getKey()));
}
protected SwipeHelper.LongPressListener getNotificationLongClicker() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 0081496..57db80a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -16,12 +16,22 @@
package com.android.systemui.statusbar;
+import android.app.INotificationManager;
+import android.app.Notification;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.SeekBar;
+import android.widget.TextView;
import com.android.systemui.R;
@@ -83,6 +93,88 @@
}
}
+ void bindImportance(final StatusBarNotification sbn, final ExpandableNotificationRow row,
+ final int importance) {
+ final INotificationManager sINM = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ final Notification.Topic topic = sbn.getNotification().getTopic() == null
+ ? new Notification.Topic(Notification.TOPIC_DEFAULT, mContext.getString(
+ com.android.internal.R.string.default_notification_topic_label))
+ : sbn.getNotification().getTopic();
+
+ final RadioButton applyToTopic = (RadioButton) row.findViewById(R.id.apply_to_topic);
+ if (sbn.getNotification().getTopic() != null) {
+ applyToTopic.setVisibility(View.VISIBLE);
+ applyToTopic.setChecked(true);
+ applyToTopic.setText(mContext.getString(R.string.apply_to_topic, topic.getLabel()));
+ row.findViewById(R.id.apply_to_app).setVisibility(View.VISIBLE);
+ }
+
+ final TextView topicSummary = ((TextView) row.findViewById(R.id.summary));
+ final TextView topicTitle = ((TextView) row.findViewById(R.id.title));
+ SeekBar seekBar = (SeekBar) row.findViewById(R.id.seekbar);
+ seekBar.setMax(4);
+ seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ updateTitleAndSummary(progress);
+ if (fromUser) {
+ try {
+ if (applyToTopic.isChecked()) {
+ sINM.setTopicImportance(sbn.getPackageName(), sbn.getUid(), topic,
+ progress);
+ } else {
+ sINM.setAppImportance(sbn.getPackageName(), sbn.getUid(), progress);
+ }
+ } catch (RemoteException e) {
+ // :(
+ }
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ // no-op
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ // no-op
+ }
+
+ private void updateTitleAndSummary(int progress) {
+ switch (progress) {
+ case NotificationListenerService.Ranking.IMPORTANCE_NONE:
+ topicSummary.setText(mContext.getString(
+ R.string.notification_importance_blocked));
+ topicTitle.setText(mContext.getString(R.string.blocked_importance));
+ break;
+ case NotificationListenerService.Ranking.IMPORTANCE_LOW:
+ topicSummary.setText(mContext.getString(
+ R.string.notification_importance_low));
+ topicTitle.setText(mContext.getString(R.string.low_importance));
+ break;
+ case NotificationListenerService.Ranking.IMPORTANCE_DEFAULT:
+ topicSummary.setText(mContext.getString(
+ R.string.notification_importance_default));
+ topicTitle.setText(mContext.getString(R.string.default_importance));
+ break;
+ case NotificationListenerService.Ranking.IMPORTANCE_HIGH:
+ topicSummary.setText(mContext.getString(
+ R.string.notification_importance_high));
+ topicTitle.setText(mContext.getString(R.string.high_importance));
+ break;
+ case NotificationListenerService.Ranking.IMPORTANCE_MAX:
+ topicSummary.setText(mContext.getString(
+ R.string.notification_importance_max));
+ topicTitle.setText(mContext.getString(R.string.max_importance));
+ break;
+ }
+ }
+ });
+ seekBar.setProgress(importance);
+ }
+
public void setActualHeight(int actualHeight) {
mActualHeight = actualHeight;
invalidate();
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index df18d3e..6f8f8eb 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -16,6 +16,7 @@
package com.android.server.appwidget;
+import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -76,6 +77,7 @@
import android.view.WindowManager;
import android.widget.RemoteViews;
+import com.android.internal.R;
import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.BackgroundThread;
@@ -83,7 +85,6 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.widget.IRemoteViewsFactory;
-import com.android.internal.R;
import com.android.server.LocalServices;
import com.android.server.WidgetBackupProvider;
@@ -139,8 +140,10 @@
private static final int CURRENT_VERSION = 1;
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
+ final String action = intent.getAction();
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
if (DEBUG) {
Slog.i(TAG, "Received broadcast: " + action);
@@ -148,23 +151,16 @@
if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
onConfigurationChanged();
- } else if (Intent.ACTION_USER_STARTED.equals(action)) {
- onUserStarted(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_NULL));
+ } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+ onUserUnlocked(userId);
} else if (Intent.ACTION_USER_STOPPED.equals(action)) {
- onUserStopped(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_NULL));
+ onUserStopped(userId);
} else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- refreshProfileWidgetsMaskedState(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_NULL));
+ refreshProfileWidgetsMaskedState(userId);
} else if (Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED.equals(action)) {
- UserHandle profile = (UserHandle)intent.getParcelableExtra(Intent.EXTRA_USER);
- if (profile != null) {
- refreshWidgetMaskedState(profile.getIdentifier());
- }
+ refreshWidgetMaskedState(userId);
} else {
- onPackageBroadcastReceived(intent, intent.getIntExtra(
- Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
+ onPackageBroadcastReceived(intent, userId);
}
}
};
@@ -263,7 +259,7 @@
sdFilter, null, null);
IntentFilter userFilter = new IntentFilter();
- userFilter.addAction(Intent.ACTION_USER_STARTED);
+ userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
userFilter.addAction(Intent.ACTION_USER_STOPPED);
userFilter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
@@ -340,6 +336,8 @@
}
private void onPackageBroadcastReceived(Intent intent, int userId) {
+ if (!mUserManager.isUserUnlocked(userId)) return;
+
final String action = intent.getAction();
boolean added = false;
boolean changed = false;
@@ -419,9 +417,7 @@
* Refresh the masked state for all profiles under the given user.
*/
private void refreshProfileWidgetsMaskedState(int userId) {
- if (userId == UserHandle.USER_NULL) {
- return;
- }
+ if (!mUserManager.isUserUnlocked(userId)) return;
List<UserInfo> profiles = mUserManager.getEnabledProfiles(userId);
if (profiles != null) {
for (int i = 0; i < profiles.size(); i++) {
@@ -435,6 +431,7 @@
* Mask/unmask widgets in the given profile, depending on the quiet state of the profile.
*/
private void refreshWidgetMaskedState(int profileId) {
+ if (!mUserManager.isUserUnlocked(profileId)) return;
final long identity = Binder.clearCallingIdentity();
try {
UserInfo user = mUserManager.getUserInfo(profileId);
@@ -484,6 +481,11 @@
}
private void ensureGroupStateLoadedLocked(int userId) {
+ if (!mUserManager.isUserUnlocked(userId)) {
+ throw new IllegalStateException(
+ "User " + userId + " must be unlocked for widgets to be available");
+ }
+
final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
// Careful lad, we may have already loaded the state for some
@@ -2321,7 +2323,7 @@
}
}
- private void onUserStarted(int userId) {
+ private void onUserUnlocked(int userId) {
synchronized (mLock) {
ensureGroupStateLoadedLocked(userId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b805c10..1d555c6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17061,6 +17061,8 @@
}
}
+ // Verify that protected broadcasts are only being sent by system code,
+ // and that system code is only sending protected broadcasts.
final String action = intent.getAction();
final boolean isProtectedBroadcast;
try {
@@ -17070,35 +17072,47 @@
return ActivityManager.BROADCAST_SUCCESS;
}
- /*
- * Prevent non-system code (defined here to be non-persistent
- * processes) from sending protected broadcasts.
- */
- int callingAppId = UserHandle.getAppId(callingUid);
- if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
- || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID
- || callingAppId == Process.NFC_UID || callingUid == 0) {
- // Always okay.
+ final boolean isCallerSystem;
+ switch (UserHandle.getAppId(callingUid)) {
+ case Process.ROOT_UID:
+ case Process.SYSTEM_UID:
+ case Process.PHONE_UID:
+ case Process.SHELL_UID:
+ case Process.BLUETOOTH_UID:
+ case Process.NFC_UID:
+ isCallerSystem = true;
+ break;
+ default:
+ isCallerSystem = (callerApp != null) && callerApp.persistent;
+ break;
+ }
- // Yell if the system is trying to send a non-protected broadcast.
- // The vast majority of broadcasts sent from system callers should
- // be protected to avoid security holes, so exceptions here should
- // be incredibly rare.
- if (!isProtectedBroadcast
- && !Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
- // TODO: eventually switch over to hard throw
+ if (isCallerSystem) {
+ if (isProtectedBroadcast
+ || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+ || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
+ || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
+ || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
+ // Broadcast is either protected, or it's a public action that
+ // we've relaxed, so it's fine for system internals to send.
+ } else {
+ // The vast majority of broadcasts sent from system internals
+ // should be protected to avoid security holes, so yell loudly
+ // to ensure we examine these cases.
Log.wtf(TAG, "Sending non-protected broadcast " + action
+ " from system", new Throwable());
}
- } else if (callerApp == null || !callerApp.persistent) {
+ } else {
if (isProtectedBroadcast) {
String msg = "Permission Denial: not allowed to send broadcast "
+ action + " from pid="
+ callingPid + ", uid=" + callingUid;
Slog.w(TAG, msg);
throw new SecurityException(msg);
- } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)) {
+
+ } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
+ || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
// Special case for compatibility: we don't want apps to send this,
// but historically it has not been protected and apps may be using it
// to poke their own app widget. So, instead of making it protected,
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index d215fc6..3da0f8d 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -16,22 +16,21 @@
package com.android.server.am;
-import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_THUMBNAILS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_THUMBNAILS;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import android.app.ActivityManager.TaskDescription;
-import android.app.PendingIntent;
-import android.os.PersistableBundle;
-import android.os.Trace;
-
-import com.android.internal.app.ResolverActivity;
-import com.android.internal.content.ReferrerIntent;
-import com.android.internal.util.XmlUtils;
-import com.android.server.AttributeCache;
-import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
-
import android.app.ActivityOptions;
+import android.app.PendingIntent;
import android.app.ResultInfo;
import android.content.ComponentName;
import android.content.Intent;
@@ -45,9 +44,11 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.util.EventLog;
import android.util.Log;
@@ -57,9 +58,12 @@
import android.view.IApplicationToken;
import android.view.WindowManager;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
+import com.android.internal.app.ResolverActivity;
+import com.android.internal.content.ReferrerIntent;
+import com.android.internal.util.XmlUtils;
+import com.android.server.AttributeCache;
+import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
import java.io.File;
import java.io.IOException;
@@ -69,6 +73,10 @@
import java.util.HashSet;
import java.util.Objects;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
/**
* An entry in the history stack, representing an activity.
*/
@@ -398,6 +406,11 @@
}
}
+ boolean isFreeform() {
+ return task != null && task.stack != null
+ && task.stack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
+ }
+
static class Token extends IApplicationToken.Stub {
private final WeakReference<ActivityRecord> weakActivity;
private final ActivityManagerService mService;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index bfd17b2..ada71f67 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -813,10 +813,9 @@
if (hasVisibleBehindActivity()) {
// Stop visible behind activity before going to sleep.
- final ActivityRecord r = mActivityContainer.mActivityDisplay.mVisibleBehindActivity;
+ final ActivityRecord r = getVisibleBehindActivity();
mStackSupervisor.mStoppingActivities.add(r);
- if (DEBUG_STATES) Slog.v(TAG_STATES,
- "Sleep still waiting to stop visible behind " + r);
+ if (DEBUG_STATES) Slog.v(TAG_STATES, "Sleep still waiting to stop visible behind " + r);
return true;
}
@@ -1053,7 +1052,7 @@
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
r.stopped = true;
r.state = ActivityState.STOPPED;
- if (mActivityContainer.mActivityDisplay.mVisibleBehindActivity == r) {
+ if (getVisibleBehindActivity() == r) {
mStackSupervisor.requestVisibleBehindLocked(r, false);
}
if (r.finishing) {
@@ -1214,9 +1213,9 @@
next.returningOptions = null;
- if (mActivityContainer.mActivityDisplay.mVisibleBehindActivity == next) {
+ if (getVisibleBehindActivity() == next) {
// When resuming an activity, require it to call requestVisibleBehind() again.
- mActivityContainer.mActivityDisplay.setVisibleBehindActivity(null);
+ setVisibleBehindActivity(null);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 80d531e..483293f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2260,7 +2260,8 @@
// Also put noDisplay activities in the source task. These by itself can
// be placed in any task/stack, however it could launch other activities
// like ResolverActivity, and we want those to stay in the original task.
- if (r.isResolverActivity() || r.noDisplay) {
+ if ((r.isResolverActivity() || r.noDisplay) && sourceRecord != null
+ && sourceRecord.isFreeform()) {
addingToTask = true;
}
}
@@ -5170,7 +5171,7 @@
/** All of the stacks on this display. Order matters, topmost stack is in front of all other
* stacks, bottommost behind. Accessed directly by ActivityManager package classes */
- final ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
+ final ArrayList<ActivityStack> mStacks = new ArrayList<>();
ActivityRecord mVisibleBehindActivity;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 62e78a4..5426b72 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -215,7 +215,8 @@
Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mService.broadcastIntentLocked(null, null, intent, null, resultTo, 0, null, null,
new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
@@ -245,6 +246,7 @@
mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0));
final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
+ unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
unlockedIntent.addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
mService.broadcastIntentLocked(null, null, unlockedIntent, null, null, 0, null,
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index d9f94d0..472e8f6 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -46,6 +46,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -348,9 +349,27 @@
private void writeBundleToXml(PersistableBundle extras, XmlSerializer out)
throws IOException, XmlPullParserException {
out.startTag(null, XML_TAG_EXTRAS);
- extras.saveToXml(out);
+ PersistableBundle extrasCopy = deepCopyBundle(extras, 10);
+ extrasCopy.saveToXml(out);
out.endTag(null, XML_TAG_EXTRAS);
}
+
+ private PersistableBundle deepCopyBundle(PersistableBundle bundle, int maxDepth) {
+ if (maxDepth <= 0) {
+ return null;
+ }
+ PersistableBundle copy = (PersistableBundle) bundle.clone();
+ Set<String> keySet = bundle.keySet();
+ for (String key: keySet) {
+ PersistableBundle b = copy.getPersistableBundle(key);
+ if (b != null) {
+ PersistableBundle bCopy = deepCopyBundle(b, maxDepth-1);
+ copy.putPersistableBundle(key, bCopy);
+ }
+ }
+ return copy;
+ }
+
/**
* Write out a tag with data identifying this job's constraints. If the constraint isn't here
* it doesn't apply.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bb07e9d..0bfbd7f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1248,6 +1248,13 @@
return mRankingHelper.getTopicImportance(pkg, uid, topic);
}
+ @Override
+ public void setAppImportance(String pkg, int uid, int importance) {
+ enforceSystemOrSystemUI("Caller not system or systemui");
+ mRankingHelper.setAppImportance(pkg, uid, importance);
+ savePolicyFile();
+ }
+
/**
* System-only API for getting a list of current (i.e. not cleared) notifications.
*
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index acdd90a..a6c9b0d 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -35,4 +35,6 @@
void setTopicImportance(String packageName, int uid, Notification.Topic topic, int importance);
int getTopicImportance(String packageName, int uid, Notification.Topic topic);
+
+ void setAppImportance(String packageName, int uid, int importance);
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 5a31c6a..32c0ce2 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -251,6 +251,7 @@
}
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATT_NAME, r.pkg);
+ out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
if (!forBackup) {
out.attribute(null, ATT_UID, Integer.toString(r.uid));
@@ -426,6 +427,20 @@
updateConfig();
}
+ /**
+ * Sets the default importance for all new topics that appear in the future, and resets
+ * the importance of all current topics.
+ */
+ @Override
+ public void setAppImportance(String pkgName, int uid, int importance) {
+ final Record r = getOrCreateRecord(pkgName, uid);
+ r.importance = importance;
+ for (Topic t : r.topics.values()) {
+ t.importance = importance;
+ }
+ updateConfig();
+ }
+
private Topic getOrCreateTopic(Record r, Notification.Topic topic) {
if (topic == null) {
topic = createDefaultTopic();
@@ -435,6 +450,7 @@
return t;
} else {
t = new Topic(topic);
+ t.importance = r.importance;
r.topics.put(topic.getId(), t);
return t;
}
@@ -477,6 +493,8 @@
pw.print(" (");
pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
pw.print(')');
+ pw.print(" importance=");
+ pw.print(Ranking.importanceToString(r.importance));
pw.println();
for (Topic t : r.topics.values()) {
pw.print(prefix);
@@ -532,6 +550,7 @@
String pkg;
int uid = UNKNOWN_UID;
+ int importance = DEFAULT_IMPORTANCE;
Map<String, Topic> topics = new ArrayMap<>();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index fa0aa37..66d10b5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -482,7 +482,6 @@
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to resolve stage location", e);
}
- final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
// Verify that stage looks sane with respect to existing application.
// This currently only ensures packageName, versionCode, and certificate
@@ -490,10 +489,7 @@
validateInstallLocked();
Preconditions.checkNotNull(mPackageName);
- // TODO: fix b/25118622; don't bypass signature check
- if (!quickInstall) {
- Preconditions.checkNotNull(mSignatures);
- }
+ Preconditions.checkNotNull(mSignatures);
Preconditions.checkNotNull(mResolvedBaseFile);
if (!mPermissionsAccepted) {
@@ -603,7 +599,6 @@
* {@link PackageManagerService}.
*/
private void validateInstallLocked() throws PackageManagerException {
- final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
mPackageName = null;
mVersionCode = -1;
mSignatures = null;
@@ -627,9 +622,7 @@
final ApkLite apk;
try {
- // TODO: fix b/25118622; always use PARSE_COLLECT_CERTIFICATES
- final int parseFlags = quickInstall ? 0 : PackageParser.PARSE_COLLECT_CERTIFICATES;
- apk = PackageParser.parseApkLite(file, parseFlags);
+ apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
@@ -750,7 +743,6 @@
}
private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException {
- final boolean quickInstall = (params.installFlags & PackageManager.INSTALL_QUICK) != 0;
if (!mPackageName.equals(apk.packageName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
+ apk.packageName + " inconsistent with " + mPackageName);
@@ -760,8 +752,7 @@
+ " version code " + apk.versionCode + " inconsistent with "
+ mVersionCode);
}
- // TODO: fix b/25118622; don't bypass signature check
- if (!quickInstall && !Signature.areExactMatch(mSignatures, apk.signatures)) {
+ if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
tag + " signatures are inconsistent");
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 42e8b01..dfb01eb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10426,10 +10426,6 @@
if (!DEFAULT_VERIFY_ENABLE) {
return false;
}
- // TODO: fix b/25118622; don't bypass verification
- if (Build.IS_DEBUGGABLE && (installFlags & PackageManager.INSTALL_QUICK) != 0) {
- return false;
- }
// Ephemeral apps don't get the full verification treatment
if ((installFlags & PackageManager.INSTALL_EPHEMERAL) != 0) {
if (DEBUG_EPHEMERAL) {
@@ -12792,7 +12788,6 @@
final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
|| (args.volumeUuid != null));
- final boolean quickInstall = ((installFlags & PackageManager.INSTALL_QUICK) != 0);
final boolean ephemeral = ((installFlags & PackageManager.INSTALL_EPHEMERAL) != 0);
boolean replace = false;
int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
@@ -12818,7 +12813,6 @@
| PackageParser.PARSE_ENFORCE_CODE
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
- | (quickInstall ? PackageParser.PARSE_SKIP_VERIFICATION : 0)
| (ephemeral ? PackageParser.PARSE_IS_EPHEMERAL : 0);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f5da103..13f4826 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -526,6 +526,7 @@
if (parentHandle != null) {
intent = new Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
intent.putExtra(Intent.EXTRA_USER, profileHandle);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, profileHandle.getIdentifier());
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtras(extras);
mContext.sendBroadcastAsUser(intent, parentHandle);
@@ -2073,6 +2074,7 @@
managedProfileIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
Intent.FLAG_RECEIVER_FOREGROUND);
managedProfileIntent.putExtra(Intent.EXTRA_USER, new UserHandle(removedUserId));
+ managedProfileIntent.putExtra(Intent.EXTRA_USER_HANDLE, removedUserId);
mContext.sendBroadcastAsUser(managedProfileIntent, new UserHandle(parentUserId), null);
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 7f4c42b..9bf7ae4 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -17,6 +17,7 @@
package com.android.server.tv;
import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
+import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
import android.content.BroadcastReceiver;
@@ -102,7 +103,6 @@
private int mCurrentIndex = 0;
private int mCurrentMaxIndex = 0;
- // TODO: Should handle STANDBY case.
private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
@@ -206,7 +206,7 @@
String inputId = mHardwareInputIdMap.get(deviceId);
if (inputId != null) {
mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
- convertConnectedToState(configs.length > 0), 0, inputId).sendToTarget();
+ obtainStateFromConfigs(configs), 0, inputId).sendToTarget();
}
ITvInputHardwareCallback callback = connection.getCallbackLocked();
if (callback != null) {
@@ -256,12 +256,13 @@
|| connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId;
}
- private int convertConnectedToState(boolean connected) {
- if (connected) {
- return INPUT_STATE_CONNECTED;
- } else {
- return INPUT_STATE_DISCONNECTED;
+ private int obtainStateFromConfigs(TvStreamConfig[] configs) {
+ for (TvStreamConfig config : configs) {
+ if ((config.getFlags() & TvStreamConfig.FLAG_MASK_SIGNAL_DETECTION) != 0) {
+ return INPUT_STATE_CONNECTED;
+ }
}
+ return (configs.length > 0) ? INPUT_STATE_CONNECTED_STANDBY : INPUT_STATE_DISCONNECTED;
}
public void addHardwareTvInput(int deviceId, TvInputInfo info) {
@@ -286,9 +287,14 @@
}
String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
if (inputId != null && inputId.equals(info.getId())) {
- mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
- convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
- inputId).sendToTarget();
+ // No HDMI hotplug does not necessarily mean disconnected, as old devices may
+ // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
+ // denote unknown state.
+ int state = mHdmiStateMap.valueAt(i)
+ ? INPUT_STATE_CONNECTED
+ : INPUT_STATE_CONNECTED_STANDBY;
+ mHandler.obtainMessage(
+ ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
return;
}
}
@@ -296,7 +302,7 @@
Connection connection = mConnections.get(deviceId);
if (connection != null) {
mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
- convertConnectedToState(connection.getConfigsLocked().length > 0), 0,
+ obtainStateFromConfigs(connection.getConfigsLocked()), 0,
info.getId()).sendToTarget();
}
}
@@ -1110,8 +1116,14 @@
if (inputId == null) {
return;
}
- mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
- convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
+ // No HDMI hotplug does not necessarily mean disconnected, as old devices may
+ // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
+ // denote unknown state.
+ int state = event.isConnected()
+ ? INPUT_STATE_CONNECTED
+ : INPUT_STATE_CONNECTED_STANDBY;
+ mHandler.obtainMessage(
+ ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
}
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index ab47f07..8292997 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -223,7 +223,7 @@
}
if (DEBUG_LAYERS) Slog.v(TAG, "Updating layer " + w + ": " + winAnimator.mAnimLayer);
if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
- mService.setInputMethodAnimLayerAdjustment(adj);
+ mService.mLayersController.setInputMethodAnimLayerAdjustment(adj);
}
wallpaperController.setAnimLayerAdjustment(w, adj);
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index da89481..7b0a8d7 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -35,7 +35,6 @@
import android.util.Slog;
import android.view.Display;
import android.view.DragEvent;
-import android.view.DropPermissionHolder;
import android.view.InputChannel;
import android.view.SurfaceControl;
import android.view.View;
@@ -54,6 +53,8 @@
import com.android.server.wm.WindowManagerService.DragInputEventReceiver;
import com.android.server.wm.WindowManagerService.H;
+import com.android.internal.view.IDropPermissions;
+
import java.util.ArrayList;
/**
@@ -428,7 +429,7 @@
// Tell the drop target about the data. Returns 'true' if we can immediately
// dispatch the global drag-ended message, 'false' if we need to wait for a
// result from the recipient.
- boolean notifyDropLw(WindowState touchedWin, DropPermissionHolder dropPermissionHolder,
+ boolean notifyDropLw(WindowState touchedWin, IDropPermissions dropPermissions,
float x, float y) {
if (mAnimation != null) {
return false;
@@ -449,7 +450,7 @@
final int myPid = Process.myPid();
final IBinder token = touchedWin.mClient.asBinder();
DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
- null, null, mData, dropPermissionHolder, false);
+ null, null, mData, dropPermissions, false);
try {
touchedWin.mClient.dispatchDragEvent(evt);
@@ -516,7 +517,7 @@
private static DragEvent obtainDragEvent(WindowState win, int action,
float x, float y, Object localState,
ClipDescription description, ClipData data,
- DropPermissionHolder dropPermissionHolder,
+ IDropPermissions dropPermissions,
boolean result) {
float winX = x - win.mFrame.left;
float winY = y - win.mFrame.top;
@@ -525,7 +526,7 @@
winY *= win.mGlobalScale;
}
return DragEvent.obtain(action, winX, winY, localState, description, data,
- dropPermissionHolder, result);
+ dropPermissions, result);
}
boolean stepAnimationLocked(long currentTimeMs) {
diff --git a/services/core/java/com/android/server/wm/DropPermissionsHandler.java b/services/core/java/com/android/server/wm/DropPermissionsHandler.java
new file mode 100644
index 0000000..2ac1ef4
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DropPermissionsHandler.java
@@ -0,0 +1,86 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.server.wm;
+
+import android.app.ActivityManagerNative;
+import android.content.ClipData;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.view.IDropPermissions;
+
+import java.util.ArrayList;
+
+class DropPermissionsHandler extends IDropPermissions.Stub {
+
+ private final int mSourceUid;
+ private final String mTargetPackage;
+ private final int mMode;
+ private final int mSourceUserId;
+ private final int mTargetUserId;
+
+ private final ArrayList<Uri> mUris = new ArrayList<Uri>();
+
+ private IBinder mPermissionOwner = null;
+
+ DropPermissionsHandler(ClipData clipData, int sourceUid, String targetPackage, int mode,
+ int sourceUserId, int targetUserId) {
+ mSourceUid = sourceUid;
+ mTargetPackage = targetPackage;
+ mMode = mode;
+ mSourceUserId = sourceUserId;
+ mTargetUserId = targetUserId;
+
+ clipData.collectUris(mUris);
+ }
+
+ @Override
+ public void take() throws RemoteException {
+ if (mPermissionOwner != null) {
+ return;
+ }
+
+ mPermissionOwner = ActivityManagerNative.getDefault().newUriPermissionOwner("drop");
+
+ long origId = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < mUris.size(); i++) {
+ ActivityManagerNative.getDefault().grantUriPermissionFromOwner(
+ mPermissionOwner, mSourceUid, mTargetPackage, mUris.get(i), mMode,
+ mSourceUserId, mTargetUserId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void release() throws RemoteException {
+ if (mPermissionOwner == null) {
+ return;
+ }
+
+ for (int i = 0; i < mUris.size(); ++i) {
+ ActivityManagerNative.getDefault().revokeUriPermissionFromOwner(
+ mPermissionOwner, mUris.get(i), mMode, mSourceUserId);
+ }
+
+ mPermissionOwner = null;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 6a5183f..62d4f36 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -782,7 +782,7 @@
SurfaceControl.openTransaction();
try {
for (int i = mService.mDisplayContents.size() - 1; i >= 0; i--) {
- DisplayContent display = mService.mDisplayContents.get(i);
+ DisplayContent display = mService.mDisplayContents.valueAt(i);
final WindowList windows = mService.getWindowListLocked(display.getDisplayId());
for (int j = windows.size() - 1; j >= 0; j--) {
windows.get(j).maybeRemoveReplacedWindow();
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
new file mode 100644
index 0000000..4a77b22
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -0,0 +1,202 @@
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
+
+import android.app.ActivityManager.StackId;
+import android.util.Slog;
+import android.view.Display;
+
+import java.io.PrintWriter;
+
+/**
+ * Controller for assigning layers to windows on the display.
+ *
+ * This class encapsulates general algorithm for assigning layers and special rules that we need to
+ * apply on top. The general algorithm goes through windows from bottom to the top and the higher
+ * the window is, the higher layer is assigned. The final layer is equal to base layer +
+ * adjustment from the order. This means that the window list is assumed to be ordered roughly by
+ * the base layer (there are exceptions, e.g. due to keyguard and wallpaper and they need to be
+ * handled with care, because they break the algorithm).
+ *
+ * On top of the general algorithm we add special rules, that govern such amazing things as:
+ * <li>IME (which has higher base layer, but will be positioned above application windows)</li>
+ * <li>docked/pinned windows (that need to be lifted above other application windows, including
+ * animations)
+ * <li>dock divider (which needs to live above applications, but below IME)</li>
+ * <li>replaced windows, which need to live above their normal level, because they anticipate
+ * an animation</li>.
+ */
+public class WindowLayersController {
+ private final WindowManagerService mService;
+
+ private int mInputMethodAnimLayerAdjustment;
+
+ public WindowLayersController(WindowManagerService service) {
+ mService = service;
+ }
+
+ private int mHighestApplicationLayer = 0;
+ private WindowState mPinnedWindow = null;
+ private WindowState mDockedWindow = null;
+ private WindowState mDockDivider = null;
+ private WindowState mImeWindow = null;
+ private WindowState mReplacingWindow = null;
+
+ final void assignLayersLocked(WindowList windows) {
+ if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based on windows=" + windows,
+ new RuntimeException("here").fillInStackTrace());
+
+ clear();
+ int curBaseLayer = 0;
+ int curLayer = 0;
+ boolean anyLayerChanged = false;
+ for (int i = 0, windowCount = windows.size(); i < windowCount; i++) {
+ final WindowState w = windows.get(i);
+ boolean layerChanged = false;
+
+ int oldLayer = w.mLayer;
+ if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
+ curLayer += WINDOW_LAYER_MULTIPLIER;
+ w.mLayer = curLayer;
+ } else {
+ curBaseLayer = curLayer = w.mBaseLayer;
+ w.mLayer = curLayer;
+ }
+ if (w.mLayer != oldLayer) {
+ layerChanged = true;
+ anyLayerChanged = true;
+ }
+
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+ oldLayer = winAnimator.mAnimLayer;
+ winAnimator.mAnimLayer = w.mLayer + w.getAnimLayerAdjustment() +
+ getSpecialWindowAnimLayerAdjustment(w);
+ if (winAnimator.mAnimLayer != oldLayer) {
+ layerChanged = true;
+ anyLayerChanged = true;
+ }
+
+ if (w.mAppToken != null) {
+ mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
+ winAnimator.mAnimLayer);
+ }
+ collectSpecialWindows(w);
+
+ if (layerChanged) {
+ w.scheduleAnimationIfDimming();
+ }
+ }
+
+ adjustSpecialWindows();
+
+ //TODO (multidisplay): Magnification is supported only for the default display.
+ if (mService.mAccessibilityController != null && anyLayerChanged
+ && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
+ mService.mAccessibilityController.onWindowLayersChangedLocked();
+ }
+
+ if (DEBUG_LAYERS) logDebugLayers(windows);
+ }
+
+ void setInputMethodAnimLayerAdjustment(int adj) {
+ if (DEBUG_LAYERS) Slog.v(TAG_WM, "Setting im layer adj to " + adj);
+ mInputMethodAnimLayerAdjustment = adj;
+ final WindowState imw = mService.mInputMethodWindow;
+ if (imw != null) {
+ imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
+ if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
+ + " anim layer: " + imw.mWinAnimator.mAnimLayer);
+ for (int i = imw.mChildWindows.size() - 1; i >= 0; i--) {
+ final WindowState childWindow = imw.mChildWindows.get(i);
+ childWindow.mWinAnimator.mAnimLayer = childWindow.mLayer + adj;
+ if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + childWindow
+ + " anim layer: " + childWindow.mWinAnimator.mAnimLayer);
+ }
+ }
+ for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) {
+ final WindowState dialog = mService.mInputMethodDialogs.get(i);
+ dialog.mWinAnimator.mAnimLayer = dialog.mLayer + adj;
+ if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
+ + " anim layer: " + dialog.mWinAnimator.mAnimLayer);
+ }
+ }
+
+ int getSpecialWindowAnimLayerAdjustment(WindowState win) {
+ if (win.mIsImWindow) {
+ return mInputMethodAnimLayerAdjustment;
+ } else if (win.mIsWallpaper) {
+ return mService.mWallpaperControllerLocked.getAnimLayerAdjustment();
+ }
+ return 0;
+ }
+
+ private void logDebugLayers(WindowList windows) {
+ for (int i = 0, n = windows.size(); i < n; i++) {
+ final WindowState w = windows.get(i);
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+ Slog.v(TAG_WM, "Assign layer " + w + ": " + "mBase=" + w.mBaseLayer
+ + " mLayer=" + w.mLayer + (w.mAppToken == null
+ ? "" : " mAppLayer=" + w.mAppToken.mAppAnimator.animLayerAdjustment)
+ + " =mAnimLayer=" + winAnimator.mAnimLayer);
+ }
+ }
+
+ private void clear() {
+ mHighestApplicationLayer = 0;
+ mImeWindow = null;
+ mPinnedWindow = null;
+ mDockedWindow = null;
+ mDockDivider = null;
+ }
+
+ private void collectSpecialWindows(WindowState w) {
+ if (w.mIsImWindow) {
+ mImeWindow = w;
+ } else if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
+ mDockDivider = w;
+ } else {
+ final TaskStack stack = w.getStack();
+ if (stack.mStackId == StackId.PINNED_STACK_ID) {
+ mPinnedWindow = w;
+ } else if (stack.mStackId == StackId.DOCKED_STACK_ID) {
+ mDockedWindow = w;
+ }
+ }
+ }
+
+ private void adjustSpecialWindows() {
+ int layer = mHighestApplicationLayer + 1;
+ // For pinned and docked stack window, we want to make them above other windows
+ // also when these windows are animating.
+ layer = assignAndIncreaseLayerIfNeeded(mDockedWindow, layer);
+ layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
+ // We know that we will be animating a relaunching window in the near future,
+ // which will receive a z-order increase. We want the replaced window to
+ // immediately receive the same treatment, e.g. to be above the dock divider.
+ layer = assignAndIncreaseLayerIfNeeded(mReplacingWindow, layer);
+ layer = assignAndIncreaseLayerIfNeeded(mPinnedWindow, layer);
+ layer = assignAndIncreaseLayerIfNeeded(mImeWindow, layer);
+ }
+
+ private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
+ if (win != null) {
+ win.mLayer = layer;
+ win.mWinAnimator.mAnimLayer = layer;
+ layer++;
+ }
+ return layer;
+ }
+
+ void dump(PrintWriter pw, String s) {
+ if (mInputMethodAnimLayerAdjustment != 0 ||
+ mService.mWallpaperControllerLocked.getAnimLayerAdjustment() != 0) {
+ pw.print(" mInputMethodAnimLayerAdjustment=");
+ pw.print(mInputMethodAnimLayerAdjustment);
+ pw.print(" mWallpaperAnimLayerAdjustment=");
+ pw.println(mService.mWallpaperControllerLocked.getAnimLayerAdjustment());
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 456c416..6385caa 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -66,7 +66,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
@@ -92,7 +91,6 @@
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManager.StackId;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.IActivityManager;
@@ -153,7 +151,6 @@
import android.view.Choreographer;
import android.view.Display;
import android.view.DisplayInfo;
-import android.view.DropPermissionHolder;
import android.view.Gravity;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
@@ -567,7 +564,6 @@
/** If true hold off on modifying the animation layer of mInputMethodTarget */
boolean mInputMethodTargetWaitingAnim;
- int mInputMethodAnimLayerAdjustment;
WindowState mInputMethodWindow = null;
final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<>();
@@ -609,6 +605,8 @@
WallpaperController mWallpaperControllerLocked;
+ final WindowLayersController mLayersController;
+
boolean mAnimateWallpaperWithTarget;
AppWindowToken mFocusedApp = null;
@@ -758,13 +756,12 @@
private boolean completeDropLw(float x, float y) {
WindowState dropTargetWin = mDragState.getDropTargetWinLw(x, y);
- DropPermissionHolder dropPermissionHolder = null;
+ DropPermissionsHandler dropPermissions = null;
if (dropTargetWin != null &&
(mDragState.mFlags & View.DRAG_FLAG_GLOBAL) != 0 &&
(mDragState.mFlags & DRAG_FLAGS_URI_ACCESS) != 0) {
- dropPermissionHolder = new DropPermissionHolder(
+ dropPermissions = new DropPermissionsHandler(
mDragState.mData,
- mActivityManager,
mDragState.mUid,
dropTargetWin.getOwningPackage(),
mDragState.mFlags & DRAG_FLAGS_URI_PERMISSIONS,
@@ -772,7 +769,7 @@
UserHandle.getUserId(dropTargetWin.getOwningUid()));
}
- return mDragState.notifyDropLw(dropTargetWin, dropPermissionHolder, x, y);
+ return mDragState.notifyDropLw(dropTargetWin, dropPermissions, x, y);
}
/**
@@ -882,6 +879,7 @@
mWallpaperControllerLocked = new WallpaperController(this);
mWindowPlacerLocked = new WindowSurfacePlacer(this);
+ mLayersController = new WindowLayersController(this);
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
@@ -1511,9 +1509,10 @@
mInputMethodTarget = w;
mInputMethodTargetWaitingAnim = false;
if (w.mAppToken != null) {
- setInputMethodAnimLayerAdjustment(w.mAppToken.mAppAnimator.animLayerAdjustment);
+ mLayersController.setInputMethodAnimLayerAdjustment(
+ w.mAppToken.mAppAnimator.animLayerAdjustment);
} else {
- setInputMethodAnimLayerAdjustment(0);
+ mLayersController.setInputMethodAnimLayerAdjustment(0);
}
}
return i+1;
@@ -1522,7 +1521,7 @@
if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget + " to null."
+ (HIDE_STACK_CRAWLS ? "" : " Callers=" + Debug.getCallers(4)));
mInputMethodTarget = null;
- setInputMethodAnimLayerAdjustment(0);
+ mLayersController.setInputMethodAnimLayerAdjustment(0);
}
return -1;
}
@@ -1544,33 +1543,6 @@
moveInputMethodDialogsLocked(pos);
}
- void setInputMethodAnimLayerAdjustment(int adj) {
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "Setting im layer adj to " + adj);
- mInputMethodAnimLayerAdjustment = adj;
- WindowState imw = mInputMethodWindow;
- if (imw != null) {
- imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
- + " anim layer: " + imw.mWinAnimator.mAnimLayer);
- int wi = imw.mChildWindows.size();
- while (wi > 0) {
- wi--;
- WindowState cw = imw.mChildWindows.get(wi);
- cw.mWinAnimator.mAnimLayer = cw.mLayer + adj;
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + cw
- + " anim layer: " + cw.mWinAnimator.mAnimLayer);
- }
- }
- int di = mInputMethodDialogs.size();
- while (di > 0) {
- di --;
- imw = mInputMethodDialogs.get(di);
- imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
- + " anim layer: " + imw.mWinAnimator.mAnimLayer);
- }
- }
-
private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {
WindowList windows = win.getWindowList();
int wpos = windows.indexOf(win);
@@ -1769,7 +1741,7 @@
}
if (needAssignLayers) {
- assignLayersLocked(windows);
+ mLayersController.assignLayersLocked(windows);
}
return true;
@@ -2061,7 +2033,7 @@
moveInputMethodWindowsIfNeededLocked(false);
}
- assignLayersLocked(displayContent.getWindowList());
+ mLayersController.assignLayersLocked(displayContent.getWindowList());
// Don't do layout here, the window must call
// relayout to be displayed, so we'll do it there.
@@ -2388,7 +2360,7 @@
if (windows != null) {
windows.remove(win);
if (!mWindowPlacerLocked.isInLayout()) {
- assignLayersLocked(windows);
+ mLayersController.assignLayersLocked(windows);
win.setDisplayLayoutNeeded();
mWindowPlacerLocked.performSurfacePlacement();
if (win.mAppToken != null) {
@@ -2743,7 +2715,7 @@
// its layer recomputed. However, if the IME was hidden
// and isn't actually moved in the list, its layer may be
// out of data so we make sure to recompute it.
- assignLayersLocked(win.getWindowList());
+ mLayersController.assignLayersLocked(win.getWindowList());
}
if (wallpaperMayMove) {
@@ -4586,7 +4558,7 @@
if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/)) {
- assignLayersLocked(displayContent.getWindowList());
+ mLayersController.assignLayersLocked(displayContent.getWindowList());
}
mInputMonitor.setUpdateInputWindowsNeededLw();
@@ -8662,102 +8634,6 @@
Arrays.fill(mRebuildTmp, null);
}
- final void assignLayersLocked(WindowList windows) {
- int N = windows.size();
- int curBaseLayer = 0;
- int curLayer = 0;
- int i;
-
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based on windows=" + windows,
- new RuntimeException("here").fillInStackTrace());
-
- boolean anyLayerChanged = false;
-
- for (i=0; i<N; i++) {
- final WindowState w = windows.get(i);
- final WindowStateAnimator winAnimator = w.mWinAnimator;
- boolean layerChanged = false;
- int oldLayer = w.mLayer;
- if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
- curLayer += WINDOW_LAYER_MULTIPLIER;
- w.mLayer = curLayer;
- } else {
- curBaseLayer = curLayer = w.mBaseLayer;
- w.mLayer = curLayer;
- }
- if (w.mLayer != oldLayer) {
- layerChanged = true;
- anyLayerChanged = true;
- }
- final AppWindowToken wtoken = w.mAppToken;
- oldLayer = winAnimator.mAnimLayer;
- if (w.mTargetAppToken != null) {
- winAnimator.mAnimLayer =
- w.mLayer + w.mTargetAppToken.mAppAnimator.animLayerAdjustment;
- } else if (wtoken != null) {
- winAnimator.mAnimLayer =
- w.mLayer + wtoken.mAppAnimator.animLayerAdjustment;
- forceHigherLayerIfNeeded(w, winAnimator, wtoken);
- } else {
- winAnimator.mAnimLayer = w.mLayer;
- }
- if (w.mIsImWindow) {
- winAnimator.mAnimLayer += mInputMethodAnimLayerAdjustment;
- } else if (w.mIsWallpaper) {
- winAnimator.mAnimLayer += mWallpaperControllerLocked.getAnimLayerAdjustment();
- }
- if (winAnimator.mAnimLayer != oldLayer) {
- layerChanged = true;
- anyLayerChanged = true;
- }
- final DimLayer.DimLayerUser dimLayerUser = w.getDimLayerUser();
- final DisplayContent displayContent = w.getDisplayContent();
- if (layerChanged && dimLayerUser != null && displayContent != null &&
- displayContent.mDimLayerController.isDimming(dimLayerUser, winAnimator)) {
- // Force an animation pass just to update the mDimLayer layer.
- scheduleAnimationLocked();
- }
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assign layer " + w + ": "
- + "mBase=" + w.mBaseLayer
- + " mLayer=" + w.mLayer
- + (wtoken == null ?
- "" : " mAppLayer=" + wtoken.mAppAnimator.animLayerAdjustment)
- + " =mAnimLayer=" + winAnimator.mAnimLayer);
- //System.out.println(
- // "Assigned layer " + curLayer + " to " + w.mClient.asBinder());
- }
-
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (mAccessibilityController != null && anyLayerChanged
- && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
- mAccessibilityController.onWindowLayersChangedLocked();
- }
- }
-
- private void forceHigherLayerIfNeeded(WindowState w, WindowStateAnimator winAnimator,
- AppWindowToken wtoken) {
- boolean force = false;
-
- if (w.mWillReplaceWindow) {
- // We know that we will be animating a relaunching window in the near future,
- // which will receive a z-order increase. We want the replaced window to
- // immediately receive the same treatment, e.g. to be above the dock divider.
- force = true;
- }
- if (!force) {
- final TaskStack stack = w.getStack();
- if (stack != null && (StackId.shouldIncreaseApplicationWindowLayer(stack.mStackId))) {
- // For pinned and docked stack window, we want to make them above other windows
- // also when these windows are animating.
- force = true;
- }
- }
- if (force) {
- w.mLayer += TYPE_LAYER_OFFSET;
- winAnimator.mAnimLayer += TYPE_LAYER_OFFSET;
- }
- }
-
void makeWindowFreezingScreenIfNeededLocked(WindowState w) {
// If the screen is currently frozen or off, then keep
// it frozen/off until this window draws at its new
@@ -9128,7 +9004,7 @@
} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
// Client will do the layout, but we need to assign layers
// for handleNewWindowLocked() below.
- assignLayersLocked(displayContent.getWindowList());
+ mLayersController.assignLayersLocked(displayContent.getWindowList());
}
}
@@ -9816,13 +9692,7 @@
}
mWindowPlacerLocked.dump(pw, " ");
mWallpaperControllerLocked.dump(pw, " ");
- if (mInputMethodAnimLayerAdjustment != 0 ||
- mWallpaperControllerLocked.getAnimLayerAdjustment() != 0) {
- pw.print(" mInputMethodAnimLayerAdjustment=");
- pw.print(mInputMethodAnimLayerAdjustment);
- pw.print(" mWallpaperAnimLayerAdjustment=");
- pw.println(mWallpaperControllerLocked.getAnimLayerAdjustment());
- }
+ mLayersController.dump(pw, " ");
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
if (needsLayout()) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e4a6806..9a24942 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1273,6 +1273,29 @@
mHasSurface = hasSurface;
}
+ int getAnimLayerAdjustment() {
+ if (mTargetAppToken != null) {
+ return mTargetAppToken.mAppAnimator.animLayerAdjustment;
+ } else if (mAppToken != null) {
+ return mAppToken.mAppAnimator.animLayerAdjustment;
+ } else {
+ // Nothing is animating, so there is no animation adjustment.
+ return 0;
+ }
+ }
+
+ void scheduleAnimationIfDimming() {
+ if (mDisplayContent == null) {
+ return;
+ }
+ final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
+ if (dimLayerUser != null && mDisplayContent.mDimLayerController.isDimming(
+ dimLayerUser, mWinAnimator)) {
+ // Force an animation pass just to update the mDimLayer layer.
+ mService.scheduleAnimationLocked();
+ }
+ }
+
private final class DeadWindowEventReceiver extends InputEventReceiver {
DeadWindowEventReceiver(InputChannel inputChannel) {
super(inputChannel, mService.mH.getLooper());
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d4001cd..7605af0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -384,12 +384,8 @@
if (mAnimator.mWindowDetachedWallpaper == mWin) {
mAnimator.mWindowDetachedWallpaper = null;
}
- mAnimLayer = mWin.mLayer;
- if (mWin.mIsImWindow) {
- mAnimLayer += mService.mInputMethodAnimLayerAdjustment;
- } else if (mIsWallpaper) {
- mAnimLayer += mWallpaperControllerLocked.getAnimLayerAdjustment();
- }
+ mAnimLayer = mWin.mLayer
+ + mService.mLayersController.getSpecialWindowAnimLayerAdjustment(mWin);
if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer);
mHasTransformation = false;
mHasLocalTransformation = false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 160c97f..cbfb201 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -524,7 +524,7 @@
}
for (DisplayContent displayContent : displayList) {
- mService.assignLayersLocked(displayContent.getWindowList());
+ mService.mLayersController.assignLayersLocked(displayContent.getWindowList());
displayContent.layoutNeeded = true;
}
}
@@ -599,7 +599,7 @@
if ((displayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0 &&
mWallpaperControllerLocked.adjustWallpaperWindows()) {
- mService.assignLayersLocked(windows);
+ mService.mLayersController.assignLayersLocked(windows);
displayContent.layoutNeeded = true;
}
@@ -1134,7 +1134,7 @@
// TODO(multidisplay): IMEs are only supported on the default display.
if (windows == mService.getDefaultWindowListLocked()
&& !mService.moveInputMethodWindowsIfNeededLocked(true)) {
- mService.assignLayersLocked(windows);
+ mService.mLayersController.assignLayersLocked(windows);
}
mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
true /*updateInputWindows*/);
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 01acdef..6c640ba 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -54,6 +54,7 @@
jmethodID maxWidth;
jmethodID maxHeight;
jmethodID generation;
+ jmethodID flags;
jmethodID build;
} gTvStreamConfigBuilderClassInfo;
@@ -239,7 +240,7 @@
int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface);
int removeStream(int deviceId, int streamId);
- const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
+ const tv_stream_config_ext_t* getStreamConfigs(int deviceId, int* numConfigs);
void onDeviceAvailable(const tv_input_device_info_t& info);
void onDeviceUnavailable(int deviceId);
@@ -288,10 +289,15 @@
sp<Looper> mLooper;
KeyedVector<int, KeyedVector<int, Connection> > mConnections;
+
+ tv_stream_config_ext_t* mConfigBuffer;
+ int mConfigBufferSize;
};
JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device,
- const sp<Looper>& looper) {
+ const sp<Looper>& looper)
+ : mConfigBuffer(NULL),
+ mConfigBufferSize(0) {
mThiz = env->NewWeakGlobalRef(thiz);
mDevice = device;
mCallback.notify = &JTvInputHal::notify;
@@ -306,6 +312,10 @@
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteWeakGlobalRef(mThiz);
mThiz = NULL;
+
+ if (mConfigBuffer != NULL) {
+ delete[] mConfigBuffer;
+ }
}
JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
@@ -354,15 +364,14 @@
if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
// Need to configure stream
int numConfigs = 0;
- const tv_stream_config_t* configs = NULL;
- if (mDevice->get_stream_configurations(
- mDevice, deviceId, &numConfigs, &configs) != 0) {
+ const tv_stream_config_ext_t* configs = getStreamConfigs(deviceId, &numConfigs);
+ if (configs == NULL) {
ALOGE("Couldn't get stream configs");
return UNKNOWN_ERROR;
}
int configIndex = -1;
for (int i = 0; i < numConfigs; ++i) {
- if (configs[i].stream_id == streamId) {
+ if (configs[i].config.stream_id == streamId) {
configIndex = i;
break;
}
@@ -371,13 +380,13 @@
ALOGE("Cannot find a config with given stream ID: %d", streamId);
return BAD_VALUE;
}
- connection.mStreamType = configs[configIndex].type;
+ connection.mStreamType = configs[configIndex].config.type;
tv_stream_t stream;
- stream.stream_id = configs[configIndex].stream_id;
+ stream.stream_id = configs[configIndex].config.stream_id;
if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
- stream.buffer_producer.width = configs[configIndex].max_video_width;
- stream.buffer_producer.height = configs[configIndex].max_video_height;
+ stream.buffer_producer.width = configs[configIndex].config.max_video_width;
+ stream.buffer_producer.height = configs[configIndex].config.max_video_height;
}
if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
ALOGE("Couldn't add stream");
@@ -431,12 +440,33 @@
return NO_ERROR;
}
-const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
- const tv_stream_config_t* configs = NULL;
- if (mDevice->get_stream_configurations(
- mDevice, deviceId, numConfigs, &configs) != 0) {
- ALOGE("Couldn't get stream configs");
- return NULL;
+const tv_stream_config_ext_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
+ const tv_stream_config_ext_t* configs = NULL;
+ if (mDevice->common.version >= TV_INPUT_DEVICE_API_VERSION_0_2) {
+ if (mDevice->get_stream_configurations_ext(
+ mDevice, deviceId, numConfigs, &configs) != 0) {
+ ALOGE("Couldn't get stream configs");
+ return NULL;
+ }
+ } else {
+ const tv_stream_config_t* oldConfigs;
+ if (mDevice->get_stream_configurations(
+ mDevice, deviceId, numConfigs, &oldConfigs) != 0) {
+ ALOGE("Couldn't get stream configs");
+ return NULL;
+ }
+ if (mConfigBufferSize < *numConfigs) {
+ mConfigBufferSize = (*numConfigs / 16 + 1) * 16;
+ if (mConfigBuffer != NULL) {
+ delete[] mConfigBuffer;
+ }
+ mConfigBuffer = new tv_stream_config_ext_t[mConfigBufferSize];
+ }
+ for (int i = 0; i < *numConfigs; ++i) {
+ mConfigBuffer[i].config = oldConfigs[i];
+ mConfigBuffer[i].flags = 0;
+ }
+ configs = mConfigBuffer;
}
return configs;
}
@@ -629,7 +659,7 @@
jlong ptr, jint deviceId, jint generation) {
JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
int numConfigs = 0;
- const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
+ const tv_stream_config_ext_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL);
for (int i = 0; i < numConfigs; ++i) {
@@ -637,15 +667,20 @@
gTvStreamConfigBuilderClassInfo.clazz,
gTvStreamConfigBuilderClassInfo.constructor);
env->CallObjectMethod(
- builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id);
+ builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].config.stream_id);
env->CallObjectMethod(
- builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type);
+ builder, gTvStreamConfigBuilderClassInfo.type, configs[i].config.type);
env->CallObjectMethod(
- builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width);
+ builder, gTvStreamConfigBuilderClassInfo.maxWidth,
+ configs[i].config.max_video_width);
env->CallObjectMethod(
- builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height);
+ builder, gTvStreamConfigBuilderClassInfo.maxHeight,
+ configs[i].config.max_video_height);
env->CallObjectMethod(
builder, gTvStreamConfigBuilderClassInfo.generation, generation);
+ env->CallObjectMethod(
+ builder, gTvStreamConfigBuilderClassInfo.flags,
+ configs[i].flags);
jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build);
@@ -737,6 +772,10 @@
gTvStreamConfigBuilderClassInfo.clazz,
"generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.flags,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "flags", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
+ GET_METHOD_ID(
gTvStreamConfigBuilderClassInfo.build,
gTvStreamConfigBuilderClassInfo.clazz,
"build", "()Landroid/media/tv/TvStreamConfig;");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 810ee6b..2082911 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -16,8 +16,6 @@
package com.android.server.devicepolicy;
-import com.google.android.collect.Sets;
-
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
@@ -28,6 +26,8 @@
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.TEXT;
+import com.google.android.collect.Sets;
+
import android.Manifest.permission;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accounts.AccountManager;
@@ -193,6 +193,8 @@
private static final String ATTR_PERMISSION_POLICY = "permission-policy";
private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
+ private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
+ = "application-restrictions-manager";
private static final int STATUS_BAR_DISABLE_MASK =
StatusBarManager.DISABLE_EXPAND |
@@ -322,6 +324,8 @@
boolean doNotAskCredentialsOnBoot = false;
+ String mApplicationRestrictionsManagingPackage;
+
public DevicePolicyData(int userHandle) {
mUserHandle = userHandle;
}
@@ -1035,19 +1039,15 @@
saveSettingsLocked(policy.mUserHandle);
}
- if (policy.mDelegatedCertInstallerPackage != null &&
- (packageName == null
- || packageName.equals(policy.mDelegatedCertInstallerPackage))) {
- try {
- // Check if delegated cert installer package is removed.
- if (mIPackageManager.getPackageInfo(
- policy.mDelegatedCertInstallerPackage, 0, userHandle) == null) {
- policy.mDelegatedCertInstallerPackage = null;
- saveSettingsLocked(policy.mUserHandle);
- }
- } catch (RemoteException e) {
- // Shouldn't happen
- }
+ // Check if delegated cert installer or app restrictions managing packages are removed.
+ if (isRemovedPackage(packageName, policy.mDelegatedCertInstallerPackage, userHandle)) {
+ policy.mDelegatedCertInstallerPackage = null;
+ saveSettingsLocked(policy.mUserHandle);
+ }
+ if (isRemovedPackage(
+ packageName, policy.mApplicationRestrictionsManagingPackage, userHandle)) {
+ policy.mApplicationRestrictionsManagingPackage = null;
+ saveSettingsLocked(policy.mUserHandle);
}
}
if (removed) {
@@ -1056,6 +1056,18 @@
}
}
+ private boolean isRemovedPackage(String changedPackage, String targetPackage, int userHandle) {
+ try {
+ return targetPackage != null
+ && (changedPackage == null || changedPackage.equals(targetPackage))
+ && mIPackageManager.getPackageInfo(targetPackage, 0, userHandle) == null;
+ } catch (RemoteException e) {
+ // Shouldn't happen
+ }
+
+ return false;
+ }
+
/**
* Unit test will subclass it to inject mocks.
*/
@@ -1162,6 +1174,10 @@
mContext.getSystemService(PowerManager.class).goToSleep(time, reason, flags);
}
+ void powerManagerReboot(String reason) {
+ mContext.getSystemService(PowerManager.class).reboot(reason);
+ }
+
boolean systemPropertiesGetBoolean(String key, boolean def) {
return SystemProperties.getBoolean(key, def);
}
@@ -1795,6 +1811,10 @@
out.attribute(null, ATTR_DELEGATED_CERT_INSTALLER,
policy.mDelegatedCertInstallerPackage);
}
+ if (policy.mApplicationRestrictionsManagingPackage != null) {
+ out.attribute(null, ATTR_APPLICATION_RESTRICTIONS_MANAGER,
+ policy.mApplicationRestrictionsManagingPackage);
+ }
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
@@ -1920,6 +1940,8 @@
}
policy.mDelegatedCertInstallerPackage = parser.getAttributeValue(null,
ATTR_DELEGATED_CERT_INSTALLER);
+ policy.mApplicationRestrictionsManagingPackage = parser.getAttributeValue(null,
+ ATTR_APPLICATION_RESTRICTIONS_MANAGER);
type = parser.next();
int outerDepth = parser.getDepth();
@@ -4815,6 +4837,7 @@
DevicePolicyData policy = getUserData(userId);
policy.mPermissionPolicy = DevicePolicyManager.PERMISSION_POLICY_PROMPT;
policy.mDelegatedCertInstallerPackage = null;
+ policy.mApplicationRestrictionsManagingPackage = null;
policy.mStatusBarDisabled = false;
saveSettingsLocked(userId);
@@ -5187,18 +5210,68 @@
}
@Override
- public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
- Preconditions.checkNotNull(who, "ComponentName is null");
- final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+ public void setApplicationRestrictionsManagingPackage(ComponentName admin, String packageName) {
+ final int userHandle = mInjector.userHandleGetCallingUserId();
synchronized (this) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ DevicePolicyData policy = getUserData(userHandle);
+ policy.mApplicationRestrictionsManagingPackage = packageName;
+ saveSettingsLocked(userHandle);
+ }
+ }
- long id = mInjector.binderClearCallingIdentity();
- try {
- mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
+ @Override
+ public String getApplicationRestrictionsManagingPackage(ComponentName admin) {
+ final int userHandle = mInjector.userHandleGetCallingUserId();
+ synchronized (this) {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ DevicePolicyData policy = getUserData(userHandle);
+ return policy.mApplicationRestrictionsManagingPackage;
+ }
+ }
+
+ @Override
+ public boolean isCallerApplicationRestrictionsManagingPackage() {
+ final int callingUid = mInjector.binderGetCallingUid();
+ final int userHandle = UserHandle.getUserId(callingUid);
+ synchronized (this) {
+ final DevicePolicyData policy = getUserData(userHandle);
+ if (policy.mApplicationRestrictionsManagingPackage == null) {
+ return false;
}
+
+ try {
+ int uid = mContext.getPackageManager().getPackageUid(
+ policy.mApplicationRestrictionsManagingPackage, userHandle);
+ return uid == callingUid;
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+ }
+
+ private void enforceCanManageApplicationRestrictions(ComponentName who) {
+ if (who != null) {
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ }
+ } else if (!isCallerApplicationRestrictionsManagingPackage()) {
+ throw new SecurityException(
+ "No admin component given, and caller cannot manage application restrictions "
+ + "for other apps.");
+ }
+ }
+
+ @Override
+ public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
+ enforceCanManageApplicationRestrictions(who);
+
+ final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -5764,21 +5837,17 @@
@Override
public Bundle getApplicationRestrictions(ComponentName who, String packageName) {
- Preconditions.checkNotNull(who, "ComponentName is null");
- final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+ enforceCanManageApplicationRestrictions(who);
- synchronized (this) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
- long id = mInjector.binderClearCallingIdentity();
- try {
- Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle);
- // if no restrictions were saved, mUserManager.getApplicationRestrictions
- // returns null, but DPM method should return an empty Bundle as per JavaDoc
- return bundle != null ? bundle : Bundle.EMPTY;
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
- }
+ final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle);
+ // if no restrictions were saved, mUserManager.getApplicationRestrictions
+ // returns null, but DPM method should return an empty Bundle as per JavaDoc
+ return bundle != null ? bundle : Bundle.EMPTY;
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
}
}
@@ -7018,4 +7087,19 @@
return UserManager.isSplitSystemUser() && callingUserId == UserHandle.USER_SYSTEM;
}
+ @Override
+ public void reboot(ComponentName admin) {
+ Preconditions.checkNotNull(admin);
+ // Make sure caller has DO.
+ synchronized (this) {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ mInjector.powerManagerReboot(PowerManager.REBOOT_REQUESTED_BY_DEVICE_OWNER);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+
}
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 0a8c014..f18617e 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -77,8 +77,8 @@
}
@Override
- public void onStartUser(int userHandle) {
- mPrintManagerImpl.handleUserStarted(userHandle);
+ public void onUnlockUser(int userHandle) {
+ mPrintManagerImpl.handleUserUnlocked(userHandle);
}
@Override
@@ -473,14 +473,11 @@
public void onChange(boolean selfChange, Uri uri, int userId) {
if (enabledPrintServicesUri.equals(uri)) {
synchronized (mLock) {
- if (userId != UserHandle.USER_ALL) {
- UserState userState = getOrCreateUserStateLocked(userId);
- userState.updateIfNeededLocked();
- } else {
- final int userCount = mUserStates.size();
- for (int i = 0; i < userCount; i++) {
- UserState userState = mUserStates.valueAt(i);
- userState.updateIfNeededLocked();
+ final int userCount = mUserStates.size();
+ for (int i = 0; i < userCount; i++) {
+ if (userId == UserHandle.USER_ALL
+ || userId == mUserStates.keyAt(i)) {
+ mUserStates.valueAt(i).updateIfNeededLocked();
}
}
}
@@ -496,6 +493,7 @@
PackageMonitor monitor = new PackageMonitor() {
@Override
public void onPackageModified(String packageName) {
+ if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
synchronized (mLock) {
// A background user/profile's print jobs are running but there is
// no UI shown. Hence, if the packages of such a user change we need
@@ -517,6 +515,7 @@
@Override
public void onPackageRemoved(String packageName, int uid) {
+ if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
synchronized (mLock) {
// A background user/profile's print jobs are running but there is
// no UI shown. Hence, if the packages of such a user change we need
@@ -544,6 +543,7 @@
@Override
public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
int uid, boolean doit) {
+ if (!mUserManager.isUserUnlocked(getChangingUserId())) return false;
synchronized (mLock) {
// A background user/profile's print jobs are running but there is
// no UI shown. Hence, if the packages of such a user change we need
@@ -574,6 +574,8 @@
@Override
public void onPackageAdded(String packageName, int uid) {
+ if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
+
// A background user/profile's print jobs are running but there is
// no UI shown. Hence, if the packages of such a user change we need
// to handle it as the change may affect ongoing print jobs.
@@ -634,6 +636,11 @@
}
private UserState getOrCreateUserStateLocked(int userId) {
+ if (!mUserManager.isUserUnlocked(userId)) {
+ throw new IllegalStateException(
+ "User " + userId + " must be unlocked for printing to be available");
+ }
+
UserState userState = mUserStates.get(userId);
if (userState == null) {
userState = new UserState(mContext, userId, mLock);
@@ -642,7 +649,7 @@
return userState;
}
- private void handleUserStarted(final int userId) {
+ private void handleUserUnlocked(final int userId) {
// This code will touch the remote print spooler which
// must be called off the main thread, so post the work.
BackgroundThread.getHandler().post(new Runnable() {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 565ef4b..7747fd9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -944,6 +944,88 @@
assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size());
}
+ public void testApplicationRestrictionsManagingApp() throws Exception {
+ setAsProfileOwner(admin1);
+
+ final String appRestrictionsManagerPackage = "com.google.app.restrictions.manager";
+ final int appRestrictionsManagerAppId = 20987;
+ final int appRestrictionsManagerUid = UserHandle.getUid(
+ DpmMockContext.CALLER_USER_HANDLE, appRestrictionsManagerAppId);
+ doReturn(appRestrictionsManagerUid).when(mContext.packageManager).getPackageUid(
+ eq(appRestrictionsManagerPackage),
+ eq(DpmMockContext.CALLER_USER_HANDLE));
+ mContext.binder.callingUid = appRestrictionsManagerUid;
+
+ // appRestrictionsManager package shouldn't be able to manage restrictions as the PO hasn't
+ // delegated that permission yet.
+ assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
+ Bundle rest = new Bundle();
+ rest.putString("KEY_STRING", "Foo1");
+ try {
+ dpm.setApplicationRestrictions(null, "pkg1", rest);
+ fail("Didn't throw expected SecurityException");
+ } catch (SecurityException expected) {
+ MoreAsserts.assertContainsRegex(
+ "caller cannot manage application restrictions", expected.getMessage());
+ }
+ try {
+ dpm.getApplicationRestrictions(null, "pkg1");
+ fail("Didn't throw expected SecurityException");
+ } catch (SecurityException expected) {
+ MoreAsserts.assertContainsRegex(
+ "caller cannot manage application restrictions", expected.getMessage());
+ }
+
+ // Check via the profile owner that no restrictions were set.
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
+
+ // Let appRestrictionsManagerPackage manage app restrictions
+ dpm.setApplicationRestrictionsManagingPackage(admin1, appRestrictionsManagerPackage);
+ assertEquals(appRestrictionsManagerPackage,
+ dpm.getApplicationRestrictionsManagingPackage(admin1));
+
+ // Now that package should be able to set and retrieve app restrictions.
+ mContext.binder.callingUid = appRestrictionsManagerUid;
+ assertTrue(dpm.isCallerApplicationRestrictionsManagingPackage());
+ dpm.setApplicationRestrictions(null, "pkg1", rest);
+ Bundle returned = dpm.getApplicationRestrictions(null, "pkg1");
+ assertEquals(1, returned.size(), 1);
+ assertEquals("Foo1", returned.get("KEY_STRING"));
+
+ // The same app running on a separate user shouldn't be able to manage app restrictions.
+ mContext.binder.callingUid = UserHandle.getUid(
+ UserHandle.USER_SYSTEM, appRestrictionsManagerAppId);
+ assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
+ try {
+ dpm.setApplicationRestrictions(null, "pkg1", rest);
+ fail("Didn't throw expected SecurityException");
+ } catch (SecurityException expected) {
+ MoreAsserts.assertContainsRegex(
+ "caller cannot manage application restrictions", expected.getMessage());
+ }
+
+ // The DPM is still able to manage app restrictions, even if it allowed another app to do it
+ // too.
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ assertEquals(returned, dpm.getApplicationRestrictions(admin1, "pkg1"));
+ dpm.setApplicationRestrictions(admin1, "pkg1", null);
+ assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
+
+ // Removing the ability for the package to manage app restrictions.
+ dpm.setApplicationRestrictionsManagingPackage(admin1, null);
+ assertNull(dpm.getApplicationRestrictionsManagingPackage(admin1));
+ mContext.binder.callingUid = appRestrictionsManagerUid;
+ assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
+ try {
+ dpm.setApplicationRestrictions(null, "pkg1", null);
+ fail("Didn't throw expected SecurityException");
+ } catch (SecurityException expected) {
+ MoreAsserts.assertContainsRegex(
+ "caller cannot manage application restrictions", expected.getMessage());
+ }
+ }
+
public void testSetUserRestriction_asDo() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_USERS);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 3b281e2..0b5aaa3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -766,7 +766,7 @@
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getDeviceIdForPhone(slotId);
+ return info.getDeviceIdForPhone(slotId, mContext.getOpPackageName());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -4865,4 +4865,4 @@
}
return null;
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index ed85392..dc2b297 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -36,7 +36,7 @@
* Retrieves the unique device ID of a phone for the device, e.g., IMEI
* for GSM phones.
*/
- String getDeviceIdForPhone(int phoneId);
+ String getDeviceIdForPhone(int phoneId, String callingPackage);
/**
* Retrieves the IMEI.
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index cf1a4aa..fecfdf9 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -23,6 +23,7 @@
import android.content.ContentResolver;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Vibrator;
@@ -180,6 +181,9 @@
new Test("with topic GoodBye") {
public void run() {
+ Notification.BigPictureStyle picture = new Notification.BigPictureStyle();
+ picture.bigPicture(BitmapFactory.decodeResource(getResources(),
+ R.id.large_icon_pineapple2));
Notification n = new Notification.Builder(NotificationTestList.this)
.setSmallIcon(R.drawable.icon1)
.setWhen(mActivityCreateTime)
@@ -187,11 +191,29 @@
.setContentText("This is a notification!!!")
.setContentIntent(makeIntent2())
.setTopic(new Notification.Topic("bye", "Goodbye"))
+ .setStyle(picture)
.build();
mNM.notify(9999, n);
}
},
+ new Test("with topic Bananas") {
+ public void run() {
+ Notification.BigTextStyle bigText = new Notification.BigTextStyle();
+ bigText.bigText("bananas are great\nso tasty\nyum\nyum\nyum\n");
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon1)
+ .setStyle(bigText)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle("bananananana")
+ .setContentText("This is a banana!!!")
+ .setContentIntent(makeIntent2())
+ .setTopic(new Notification.Topic("bananas", "Bananas"))
+ .build();
+
+ mNM.notify(999, n);
+ }
+ },
new Test("Whens") {
public void run()