Merge "Add flags to requests for package UID/GIDs."
diff --git a/api/current.txt b/api/current.txt
index 17b4ff3..83f8fdf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5762,6 +5762,7 @@
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
+ method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
method public long getPasswordExpiration(android.content.ComponentName);
method public long getPasswordExpirationTimeout(android.content.ComponentName);
method public int getPasswordHistoryLength(android.content.ComponentName);
@@ -30781,7 +30782,7 @@
field public static final java.lang.String COLUMN_MIME_TYPE = "mime_type";
field public static final java.lang.String COLUMN_SIZE = "_size";
field public static final java.lang.String COLUMN_SUMMARY = "summary";
- field public static final int FLAG_ARCHIVE = 2048; // 0x800
+ field public static final int FLAG_ARCHIVE = 1024; // 0x400
field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -30790,9 +30791,8 @@
field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
- field public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 512; // 0x200
field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
- field public static final int FLAG_VIRTUAL_DOCUMENT = 1024; // 0x400
+ field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
}
diff --git a/api/system-current.txt b/api/system-current.txt
index c9cd437..87760b4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5890,6 +5890,7 @@
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
+ method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
method public long getPasswordExpiration(android.content.ComponentName);
method public long getPasswordExpirationTimeout(android.content.ComponentName);
method public int getPasswordHistoryLength(android.content.ComponentName);
@@ -32814,7 +32815,7 @@
field public static final java.lang.String COLUMN_MIME_TYPE = "mime_type";
field public static final java.lang.String COLUMN_SIZE = "_size";
field public static final java.lang.String COLUMN_SUMMARY = "summary";
- field public static final int FLAG_ARCHIVE = 2048; // 0x800
+ field public static final int FLAG_ARCHIVE = 1024; // 0x400
field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -32823,9 +32824,8 @@
field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
- field public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 512; // 0x200
field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
- field public static final int FLAG_VIRTUAL_DOCUMENT = 1024; // 0x400
+ field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
}
diff --git a/api/test-current.txt b/api/test-current.txt
index c70cd34..c9584a3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5764,6 +5764,7 @@
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
+ method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
method public long getPasswordExpiration(android.content.ComponentName);
method public long getPasswordExpirationTimeout(android.content.ComponentName);
method public int getPasswordHistoryLength(android.content.ComponentName);
@@ -30793,7 +30794,7 @@
field public static final java.lang.String COLUMN_MIME_TYPE = "mime_type";
field public static final java.lang.String COLUMN_SIZE = "_size";
field public static final java.lang.String COLUMN_SUMMARY = "summary";
- field public static final int FLAG_ARCHIVE = 2048; // 0x800
+ field public static final int FLAG_ARCHIVE = 1024; // 0x400
field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -30802,9 +30803,8 @@
field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
- field public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 512; // 0x200
field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
- field public static final int FLAG_VIRTUAL_DOCUMENT = 1024; // 0x400
+ field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 177fabe..4531a74 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -101,6 +101,9 @@
import android.view.WindowManagerGlobal;
import android.renderscript.RenderScriptCacheDir;
import android.security.keystore.AndroidKeyStoreProvider;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.ErrnoException;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IVoiceInteractor;
@@ -820,48 +823,6 @@
setCoreSettings(coreSettings);
- /*
- * Two possible indications that this package could be
- * sharing its runtime with other packages:
- *
- * 1.) the sharedUserId attribute is set in the manifest,
- * indicating a request to share a VM with other
- * packages with the same sharedUserId.
- *
- * 2.) the application element of the manifest has an
- * attribute specifying a non-default process name,
- * indicating the desire to run in another packages VM.
- *
- * If sharing is enabled we do not have a unique application
- * in a process and therefore cannot rely on the package
- * name inside the runtime.
- */
- IPackageManager pm = getPackageManager();
- android.content.pm.PackageInfo pi = null;
- try {
- pi = pm.getPackageInfo(appInfo.packageName, 0, UserHandle.myUserId());
- } catch (RemoteException e) {
- }
- if (pi != null) {
- boolean sharedUserIdSet = (pi.sharedUserId != null);
- boolean processNameNotDefault =
- (pi.applicationInfo != null &&
- !appInfo.packageName.equals(pi.applicationInfo.processName));
- boolean sharable = (sharedUserIdSet || processNameNotDefault);
-
- // Tell the VMRuntime about the application, unless it is shared
- // inside a process.
- if (!sharable) {
- final List<String> codePaths = new ArrayList<>();
- codePaths.add(appInfo.sourceDir);
- if (appInfo.splitSourceDirs != null) {
- Collections.addAll(codePaths, appInfo.splitSourceDirs);
- }
- VMRuntime.registerAppInfo(appInfo.packageName, appInfo.dataDir,
- codePaths.toArray(new String[codePaths.size()]));
- }
- }
-
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
@@ -4697,6 +4658,87 @@
}
}
+ private static void setupJitProfileSupport(LoadedApk loadedApk, File cacheDir) {
+ final ApplicationInfo appInfo = loadedApk.getApplicationInfo();
+ if (isSharingRuntime(appInfo)) {
+ // If sharing is enabled we do not have a unique application
+ // in a process and therefore cannot rely on the package
+ // name inside the runtime.
+ return;
+ }
+ final List<String> codePaths = new ArrayList<>();
+ if ((appInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ codePaths.add(appInfo.sourceDir);
+ }
+ if (appInfo.splitSourceDirs != null) {
+ Collections.addAll(codePaths, appInfo.splitSourceDirs);
+ }
+
+ if (codePaths.isEmpty()) {
+ // If there are no code paths there's no need to setup a profile file and register with
+ // the runtime,
+ return;
+ }
+
+ // Add an extension to the file name to better reveal its intended use.
+ // Keep in sync with BackgroundDexOptService.
+ final String profileExtension = ".prof";
+ final File profileFile = new File(cacheDir, loadedApk.mPackageName + profileExtension);
+ if (!profileFile.exists()) {
+ FileDescriptor fd = null;
+ try {
+ final int permissions = 0600; // read-write for user.
+ fd = Os.open(profileFile.getAbsolutePath(), OsConstants.O_CREAT, permissions);
+ Os.fchmod(fd, permissions);
+ Os.fchown(fd, appInfo.uid, appInfo.uid);
+ } catch (ErrnoException e) {
+ Log.w(TAG, "Unable to create jit profile file " + profileFile, e);
+ try {
+ Os.unlink(profileFile.getAbsolutePath());
+ } catch (ErrnoException unlinkErr) {
+ Log.v(TAG, "Unable to unlink jit profile file " + profileFile, unlinkErr);
+ }
+ return;
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
+ VMRuntime.registerAppInfo(profileFile.getAbsolutePath(), appInfo.dataDir,
+ codePaths.toArray(new String[codePaths.size()]));
+ }
+
+ /*
+ * Two possible indications that this package could be
+ * sharing its runtime with other packages:
+ *
+ * 1) the sharedUserId attribute is set in the manifest,
+ * indicating a request to share a VM with other
+ * packages with the same sharedUserId.
+ *
+ * 2) the application element of the manifest has an
+ * attribute specifying a non-default process name,
+ * indicating the desire to run in another packages VM.
+ */
+ private static boolean isSharingRuntime(ApplicationInfo appInfo) {
+ IPackageManager pm = getPackageManager();
+ android.content.pm.PackageInfo pi = null;
+ try {
+ pi = pm.getPackageInfo(appInfo.packageName, 0, UserHandle.myUserId());
+ } catch (RemoteException e) {
+ }
+ if (pi != null) {
+ boolean sharedUserIdSet = (pi.sharedUserId != null);
+ boolean processNameNotDefault = (pi.applicationInfo != null) &&
+ !appInfo.packageName.equals(pi.applicationInfo.processName);
+ boolean sharable = sharedUserIdSet || processNameNotDefault;
+ return sharable;
+ }
+ // We couldn't get information for the package. Be pessimistic and assume
+ // it's sharing the runtime.
+ return true;
+ }
+
private void updateDefaultDensity() {
if (mCurDefaultDisplayDpi != Configuration.DENSITY_DPI_UNDEFINED
&& mCurDefaultDisplayDpi != DisplayMetrics.DENSITY_DEVICE
@@ -4900,12 +4942,14 @@
+ "due to missing cache directory");
}
- // Use codeCacheDir to store generated/compiled graphics code
+ // Use codeCacheDir to store generated/compiled graphics code and jit profiling data.
final File codeCacheDir = appContext.getCodeCacheDir();
if (codeCacheDir != null) {
setupGraphicsSupport(data.info, codeCacheDir);
+ setupJitProfileSupport(data.info, codeCacheDir);
} else {
- Log.e(TAG, "Unable to setupGraphicsSupport due to missing code-cache directory");
+ Log.e(TAG, "Unable to setupGraphicsSupport and setupJitProfileSupport " +
+ "due to missing code-cache directory");
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f940bd6..08e9b1e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -88,13 +88,15 @@
private final Context mContext;
private final IDevicePolicyManager mService;
+ private boolean mParentInstance;
private static final String REMOTE_EXCEPTION_MESSAGE =
"Failed to talk with device policy manager service";
- private DevicePolicyManager(Context context) {
+ private DevicePolicyManager(Context context, boolean parentInstance) {
this(context, IDevicePolicyManager.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)));
+ mParentInstance = parentInstance;
}
/** @hide */
@@ -106,7 +108,7 @@
/** @hide */
public static DevicePolicyManager create(Context context) {
- DevicePolicyManager me = new DevicePolicyManager(context);
+ DevicePolicyManager me = new DevicePolicyManager(context, false);
return me.mService != null ? me : null;
}
@@ -1031,7 +1033,7 @@
public void setPasswordQuality(@NonNull ComponentName admin, int quality) {
if (mService != null) {
try {
- mService.setPasswordQuality(admin, quality);
+ mService.setPasswordQuality(admin, quality, mParentInstance);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
@@ -1052,7 +1054,7 @@
public int getPasswordQuality(@Nullable ComponentName admin, int userHandle) {
if (mService != null) {
try {
- return mService.getPasswordQuality(admin, userHandle);
+ return mService.getPasswordQuality(admin, userHandle, mParentInstance);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
@@ -1622,7 +1624,7 @@
public boolean isActivePasswordSufficient() {
if (mService != null) {
try {
- return mService.isActivePasswordSufficient(myUserId());
+ return mService.isActivePasswordSufficient(myUserId(), mParentInstance);
} catch (RemoteException e) {
Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
}
@@ -1791,6 +1793,9 @@
* not acceptable for the current constraints or if the user has not been decrypted yet.
*/
public boolean resetPassword(String password, int flags) {
+ if (mParentInstance) {
+ throw new SecurityException("Reset password does not work across profiles.");
+ }
if (mService != null) {
try {
return mService.resetPassword(password, flags);
@@ -3060,6 +3065,8 @@
*
* <p>If the device owner information is {@code null} or empty then the device owner info is
* cleared and the user owner info is shown on the lock screen if it is set.
+ * <p>If the device owner information contains only whitespaces then the message on the lock
+ * screen will be blank and the user will not be allowed to change it.
*
* @param admin The name of the admin component to check.
* @param info Device owner information which will be displayed instead of the user
@@ -4927,4 +4934,23 @@
}
return null;
}
+
+ /**
+ * Obtains a {@link DevicePolicyManager} whose calls act on the parent profile.
+ *
+ * <p> Note only some methods will work on the parent Manager.
+ *
+ * @return a new instance of {@link DevicePolicyManager} that acts on the parent profile.
+ */
+ public DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
+ try {
+ if (!mService.isManagedProfile(admin)) {
+ throw new SecurityException("The current user does not have a parent profile.");
+ }
+ return new DevicePolicyManager(mContext, true);
+ } catch (RemoteException re) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+ return null;
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f480a02..754cb43 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -35,8 +35,8 @@
* {@hide}
*/
interface IDevicePolicyManager {
- void setPasswordQuality(in ComponentName who, int quality);
- int getPasswordQuality(in ComponentName who, int userHandle);
+ void setPasswordQuality(in ComponentName who, int quality, boolean parent);
+ int getPasswordQuality(in ComponentName who, int userHandle, boolean parent);
void setPasswordMinimumLength(in ComponentName who, int length);
int getPasswordMinimumLength(in ComponentName who, int userHandle);
@@ -67,7 +67,7 @@
long getPasswordExpiration(in ComponentName who, int userHandle);
- boolean isActivePasswordSufficient(int userHandle);
+ boolean isActivePasswordSufficient(int userHandle, boolean parent);
int getCurrentFailedPasswordAttempts(int userHandle);
int getProfileWithMinimumFailedPasswordsForWipe(int userHandle);
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index c3625b8..469a4ea 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -98,5 +98,11 @@
void writePrintJobData(in ParcelFileDescriptor fd, in PrintJobId printJobId);
void setClient(IPrintSpoolerClient client);
void setPrintJobCancelling(in PrintJobId printJobId, boolean cancelling);
- void removeApprovedPrintService(in ComponentName serviceToRemove);
+
+ /**
+ * Remove all approved print services that are not in the given set.
+ *
+ * @param servicesToKeep The names of the services to keep
+ */
+ void pruneApprovedPrintServices(in List<ComponentName> servicesToKeep);
}
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index b33ef83..91e01f2 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -16,6 +16,7 @@
package android.printservice;
+import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -94,6 +95,16 @@
}
/**
+ * Return the component name for this print service.
+ *
+ * @return The component name for this print service.
+ */
+ public @NonNull ComponentName getComponentName() {
+ return new ComponentName(mResolveInfo.serviceInfo.packageName,
+ mResolveInfo.serviceInfo.name);
+ }
+
+ /**
* Creates a new instance.
*
* @param resolveInfo The service resolve info.
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 084ff77..a401ac2 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -230,7 +230,6 @@
* @see #FLAG_SUPPORTS_WRITE
* @see #FLAG_SUPPORTS_DELETE
* @see #FLAG_SUPPORTS_THUMBNAIL
- * @see #FLAG_SUPPORTS_TYPED_DOCUMENT
* @see #FLAG_DIR_PREFERS_GRID
* @see #FLAG_DIR_PREFERS_LAST_MODIFIED
* @see #FLAG_VIRTUAL_DOCUMENT
@@ -349,15 +348,6 @@
public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
/**
- * Flag indicating that a document can be converted to alternative types.
- *
- * @see #COLUMN_FLAGS
- * @see DocumentsProvider#openTypedDocument(String, String, Bundle,
- * android.os.CancellationSignal)
- */
- public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 1 << 9;
-
- /**
* Flag indicating that a document is virtual, and doesn't have byte
* representation in the MIME type specified as {@link #COLUMN_MIME_TYPE}.
*
@@ -366,7 +356,7 @@
* @see DocumentsProvider#openTypedDocument(String, String, Bundle,
* android.os.CancellationSignal)
*/
- public static final int FLAG_VIRTUAL_DOCUMENT = 1 << 10;
+ public static final int FLAG_VIRTUAL_DOCUMENT = 1 << 9;
/**
* Flag indicating that a document is an archive, and it's contents can be
@@ -378,7 +368,7 @@
* @see #COLUMN_FLAGS
* @see DocumentsProvider#queryChildDocuments(String, String[], String)
*/
- public static final int FLAG_ARCHIVE = 1 << 11;
+ public static final int FLAG_ARCHIVE = 1 << 10;
/**
* Flag indicating that document titles should be hidden when viewing
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index e25ba35..94b4157 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -517,13 +517,12 @@
* provider.
* @param signal used by the caller to signal if the request should be
* cancelled. May be null.
- * @see Document#FLAG_SUPPORTS_TYPED_DOCUMENT
*/
@SuppressWarnings("unused")
public AssetFileDescriptor openTypedDocument(
String documentId, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
throws FileNotFoundException {
- throw new UnsupportedOperationException("Typed documents not supported");
+ throw new FileNotFoundException("The requested MIME type is not supported.");
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d0f2159..df7e6da 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4974,19 +4974,22 @@
/**
* List of the enabled print services.
+ *
+ * N and beyond uses {@link #DISABLED_PRINT_SERVICES}. But this might be used in an upgrade
+ * from pre-N.
+ *
* @hide
*/
public static final String ENABLED_PRINT_SERVICES =
"enabled_print_services";
/**
- * List of the system print services we enabled on first boot. On
- * first boot we enable all system, i.e. bundled print services,
- * once, so they work out-of-the-box.
+ * List of the disabled print services.
+ *
* @hide
*/
- public static final String ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES =
- "enabled_on_first_boot_system_print_services";
+ public static final String DISABLED_PRINT_SERVICES =
+ "disabled_print_services";
/**
* Setting to always use the default text-to-speech settings regardless
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index d9068dc..44811cb 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -150,6 +150,7 @@
ds[0].mX = event.getX();
ds[0].mY = event.getY();
+ int nx = widget.getScrollX() + (int) dx;
int ny = widget.getScrollY() + (int) dy;
int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom();
@@ -161,6 +162,8 @@
int oldX = widget.getScrollX();
int oldY = widget.getScrollY();
+ scrollTo(widget, layout, nx, ny);
+
// If we actually scrolled, then cancel the up action.
if (oldX != widget.getScrollX() || oldY != widget.getScrollY()) {
widget.cancelLongPress();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3a1e9ab..68f1ac3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3592,6 +3592,9 @@
private int[] mDrawableState = null;
+ /** Whether draw() is currently being called. */
+ private boolean mInDraw = false;
+
ViewOutlineProvider mOutlineProvider = ViewOutlineProvider.BACKGROUND;
/**
@@ -16470,6 +16473,8 @@
*/
@CallSuper
public void draw(Canvas canvas) {
+ mInDraw = true;
+
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
@@ -16514,6 +16519,7 @@
onDrawForeground(canvas);
// we're done...
+ mInDraw = false;
return;
}
@@ -16661,6 +16667,8 @@
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
+
+ mInDraw = false;
}
/**
@@ -17105,7 +17113,8 @@
*/
@Override
public void invalidateDrawable(@NonNull Drawable drawable) {
- if (verifyDrawable(drawable)) {
+ // Don't invalidate if a drawable changes during drawing.
+ if (verifyDrawable(drawable) && !mInDraw) {
final Rect dirty = drawable.getDirtyBounds();
final int scrollX = mScrollX;
final int scrollY = mScrollY;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1c9f3b4..0fb3951 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -384,6 +384,8 @@
int localChanges;
}
+ private String mTag = TAG;
+
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
@@ -510,6 +512,7 @@
mWindowAttributes.packageName = mBasePackageName;
}
attrs = mWindowAttributes;
+ setTag();
// Keep track of the actual window flags supplied by the client.
mClientWindowLayoutFlags = attrs.flags;
@@ -546,7 +549,7 @@
attrs.backup();
mTranslator.translateWindowLayout(attrs);
}
- if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
+ if (DEBUG_LAYOUT) Log.d(mTag, "WindowLayout in setView:" + attrs);
if (!compatibilityInfo.supportsScreen()) {
attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
@@ -607,7 +610,7 @@
mPendingContentInsets.set(mAttachInfo.mContentInsets);
mPendingStableInsets.set(mAttachInfo.mStableInsets);
mPendingVisibleInsets.set(0, 0, 0, 0);
- if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
+ if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
mAdded = false;
@@ -701,6 +704,13 @@
}
}
+ private void setTag() {
+ final String[] split = mWindowAttributes.getTitle().toString().split("\\.");
+ if (split.length > 0) {
+ mTag = TAG + "[" + split[split.length - 1] + "]";
+ }
+ }
+
/** Whether the window is in local focus mode or not */
private boolean isInLocalFocusMode() {
return (mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
@@ -990,7 +1000,7 @@
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
- if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
+ if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
if (dirty == null) {
invalidate();
@@ -1174,7 +1184,7 @@
private boolean collectViewAttributes() {
if (mAttachInfo.mRecomputeGlobalAttributes) {
- //Log.i(TAG, "Computing view hierarchy attributes!");
+ //Log.i(mTag, "Computing view hierarchy attributes!");
mAttachInfo.mRecomputeGlobalAttributes = false;
boolean oldScreenOn = mAttachInfo.mKeepScreenOn;
mAttachInfo.mKeepScreenOn = false;
@@ -1215,7 +1225,7 @@
int childHeightMeasureSpec;
boolean windowSizeMayChange = false;
- if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,
+ if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(mTag,
"Measuring " + host + " in display " + desiredWindowWidth
+ "x" + desiredWindowHeight + "...");
@@ -1231,26 +1241,26 @@
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
- if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
+ if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": baseSize=" + baseSize);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
- if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
// Didn't fit in that size... try expanding a bit.
baseSize = (baseSize+desiredWindowWidth)/2;
- if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
+ if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
- if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
- if (DEBUG_DIALOG) Log.v(TAG, "Good!");
+ if (DEBUG_DIALOG) Log.v(mTag, "Good!");
goodMeasure = true;
}
}
@@ -1350,8 +1360,8 @@
int desiredWindowHeight;
final int viewVisibility = getHostVisibility();
- boolean viewVisibilityChanged = mViewVisibility != viewVisibility
- || mNewSurfaceNeeded;
+ final boolean viewVisibilityChanged = !mFirst
+ && (mViewVisibility != viewVisibility || mNewSurfaceNeeded);
WindowManager.LayoutParams params = null;
if (mWindowAttributesChanged) {
@@ -1401,7 +1411,6 @@
mAttachInfo.mHasWindowFocus = false;
mAttachInfo.mWindowVisibility = viewVisibility;
mAttachInfo.mRecomputeGlobalAttributes = false;
- viewVisibilityChanged = false;
mLastConfiguration.setTo(host.getResources().getConfiguration());
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
// Set the layout direction if it has not been set before (inherit is the default)
@@ -1411,14 +1420,13 @@
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
- //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
+ //Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
- if (DEBUG_ORIENTATION) Log.v(TAG,
- "View " + host + " resized to: " + frame);
+ if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
mFullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
@@ -1471,28 +1479,22 @@
}
if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
- if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
+ if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
if (!mPendingOutsets.equals(mAttachInfo.mOutsets)) {
insetsChanged = true;
}
- if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
- || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ if ((lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
+ || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT)
+ && (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
+ || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD)) {
windowSizeMayChange = true;
-
- if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
- || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
- // NOTE -- system code, won't try to do compat mode.
- Point size = new Point();
- mDisplay.getRealSize(size);
- desiredWindowWidth = size.x;
- desiredWindowHeight = size.y;
- } else {
- DisplayMetrics packageMetrics = res.getDisplayMetrics();
- desiredWindowWidth = packageMetrics.widthPixels;
- desiredWindowHeight = packageMetrics.heightPixels;
- }
+ // NOTE -- system code, won't try to do compat mode.
+ Point size = new Point();
+ mDisplay.getRealSize(size);
+ desiredWindowWidth = size.x;
+ desiredWindowHeight = size.y;
}
}
@@ -1616,7 +1618,7 @@
try {
if (DEBUG_LAYOUT) {
- Log.i(TAG, "host=w:" + host.getMeasuredWidth() + ", h:" +
+ Log.i(mTag, "host=w:" + host.getMeasuredWidth() + ", h:" +
host.getMeasuredHeight() + ", params=" + params);
}
@@ -1634,7 +1636,7 @@
final int surfaceGenerationId = mSurface.getGenerationId();
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
- if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
+ if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
+ " overscan=" + mPendingOverscanInsets.toShortString()
+ " content=" + mPendingContentInsets.toShortString()
+ " visible=" + mPendingVisibleInsets.toShortString()
@@ -1643,7 +1645,7 @@
+ " surface=" + mSurface);
if (mPendingConfiguration.seq != 0) {
- if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: "
+ if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
+ mPendingConfiguration);
updateConfiguration(new Configuration(mPendingConfiguration), !mFirst);
mPendingConfiguration.seq = 0;
@@ -1662,19 +1664,19 @@
& WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
if (contentInsetsChanged) {
mAttachInfo.mContentInsets.set(mPendingContentInsets);
- if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
+ if (DEBUG_LAYOUT) Log.v(mTag, "Content insets changing to: "
+ mAttachInfo.mContentInsets);
}
if (overscanInsetsChanged) {
mAttachInfo.mOverscanInsets.set(mPendingOverscanInsets);
- if (DEBUG_LAYOUT) Log.v(TAG, "Overscan insets changing to: "
+ if (DEBUG_LAYOUT) Log.v(mTag, "Overscan insets changing to: "
+ mAttachInfo.mOverscanInsets);
// Need to relayout with content insets.
contentInsetsChanged = true;
}
if (stableInsetsChanged) {
mAttachInfo.mStableInsets.set(mPendingStableInsets);
- if (DEBUG_LAYOUT) Log.v(TAG, "Decor insets changing to: "
+ if (DEBUG_LAYOUT) Log.v(mTag, "Decor insets changing to: "
+ mAttachInfo.mStableInsets);
// Need to relayout with content insets.
contentInsetsChanged = true;
@@ -1691,7 +1693,7 @@
}
if (visibleInsetsChanged) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
- if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
+ if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
@@ -1872,7 +1874,7 @@
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
- if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed! mWidth="
+ if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth="
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
+ " mHeight=" + mHeight
+ " measuredHeight=" + host.getMeasuredHeight()
@@ -1902,7 +1904,7 @@
}
if (measureAgain) {
- if (DEBUG_LAYOUT) Log.v(TAG,
+ if (DEBUG_LAYOUT) Log.v(mTag,
"And hey let's measure once more: width=" + width
+ " height=" + height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
@@ -2024,15 +2026,15 @@
if (mFirst) {
// handle first focus request
- if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: mView.hasFocus()="
+ mView.hasFocus());
if (mView != null) {
if (!mView.hasFocus()) {
mView.requestFocus(View.FOCUS_FORWARD);
- if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: requested focused view="
+ mView.findFocus());
} else {
- if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: existing focused view="
+ mView.findFocus());
}
}
@@ -2104,11 +2106,11 @@
}
private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
- Log.e(TAG, "OutOfResourcesException initializing HW surface", e);
+ Log.e(mTag, "OutOfResourcesException initializing HW surface", e);
try {
if (!mWindowSession.outOfMemory(mWindow) &&
Process.myUid() != Process.SYSTEM_UID) {
- Slog.w(TAG, "No processes killed for memory; killing self");
+ Slog.w(mTag, "No processes killed for memory; killing self");
Process.killProcess(Process.myPid());
}
} catch (RemoteException ex) {
@@ -2184,7 +2186,7 @@
final View host = mView;
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
- Log.v(TAG, "Laying out " + host + " to (" +
+ Log.v(mTag, "Laying out " + host + " to (" +
host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
}
@@ -2427,11 +2429,11 @@
String thisHash = Integer.toHexString(System.identityHashCode(this));
long frameTime = nowTime - mFpsPrevTime;
long totalTime = nowTime - mFpsStartTime;
- Log.v(TAG, "0x" + thisHash + "\tFrame time:\t" + frameTime);
+ Log.v(mTag, "0x" + thisHash + "\tFrame time:\t" + frameTime);
mFpsPrevTime = nowTime;
if (totalTime > 1000) {
float fps = (float) mFpsNumFrames * 1000 / totalTime;
- Log.v(TAG, "0x" + thisHash + "\tFPS:\t" + fps);
+ Log.v(mTag, "0x" + thisHash + "\tFPS:\t" + fps);
mFpsStartTime = nowTime;
mFpsNumFrames = 0;
}
@@ -2473,7 +2475,7 @@
try {
mWindowDrawCountDown.await();
} catch (InterruptedException e) {
- Log.e(TAG, "Window redraw count down interruped!");
+ Log.e(mTag, "Window redraw count down interruped!");
}
mWindowDrawCountDown = null;
}
@@ -2483,7 +2485,7 @@
}
if (LOCAL_LOGV) {
- Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
+ Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
if (mSurfaceHolder != null && mSurface.isValid()) {
mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
@@ -2566,7 +2568,7 @@
}
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
- Log.v(TAG, "Draw " + mView + "/"
+ Log.v(mTag, "Draw " + mView + "/"
+ mWindowAttributes.getTitle()
+ ": dirty={" + dirty.left + "," + dirty.top
+ "," + dirty.right + "," + dirty.bottom + "} surface="
@@ -2700,7 +2702,7 @@
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
- Log.e(TAG, "Could not lock surface", e);
+ Log.e(mTag, "Could not lock surface", e);
// Don't assume this is due to out of memory, it could be
// something else, and if it is something else then we could
// kill stuff (or ourself) for no reason.
@@ -2710,7 +2712,7 @@
try {
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
- Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
+ Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
+ canvas.getWidth() + ", h=" + canvas.getHeight());
//canvas.drawARGB(255, 255, 0, 0);
}
@@ -2733,7 +2735,7 @@
if (DEBUG_DRAW) {
Context cxt = mView.getContext();
- Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
+ Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
", metrics=" + cxt.getResources().getDisplayMetrics() +
", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
}
@@ -2758,14 +2760,14 @@
try {
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
- Log.e(TAG, "Could not unlock surface", e);
+ Log.e(mTag, "Could not unlock surface", e);
mLayoutRequested = true; // ask wm for a new surface next time.
//noinspection ReturnInsideFinallyBlock
return false;
}
if (LOCAL_LOGV) {
- Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
+ Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
}
}
return true;
@@ -2870,14 +2872,14 @@
// view is visible.
rectangle = null;
}
- if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Eval scroll: focus=" + focus
+ " rectangle=" + rectangle + " ci=" + ci
+ " vi=" + vi);
if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) {
// Optimization: if the focus hasn't changed since last
// time, and no layout has happened, then just leave things
// as they are.
- if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y="
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Keeping scroll y="
+ mScrollY + " vi=" + vi.toShortString());
} else {
// We need to determine if the currently focused view is
@@ -2885,51 +2887,51 @@
// a pan so it can be seen.
mLastScrolledFocus = new WeakReference<View>(focus);
mScrollMayChange = false;
- if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?");
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Need to scroll?");
// Try to find the rectangle from the focus view.
if (focus.getGlobalVisibleRect(mVisRect, null)) {
- if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w="
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Root w="
+ mView.getWidth() + " h=" + mView.getHeight()
+ " ci=" + ci.toShortString()
+ " vi=" + vi.toShortString());
if (rectangle == null) {
focus.getFocusedRect(mTempRect);
- if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Focus " + focus
+ ": focusRect=" + mTempRect.toShortString());
if (mView instanceof ViewGroup) {
((ViewGroup) mView).offsetDescendantRectToMyCoords(
focus, mTempRect);
}
- if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Focus in window: focusRect="
+ mTempRect.toShortString()
+ " visRect=" + mVisRect.toShortString());
} else {
mTempRect.set(rectangle);
- if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Request scroll to rect: "
+ mTempRect.toShortString()
+ " visRect=" + mVisRect.toShortString());
}
if (mTempRect.intersect(mVisRect)) {
- if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Focus window visible rect: "
+ mTempRect.toShortString());
if (mTempRect.height() >
(mView.getHeight()-vi.top-vi.bottom)) {
// If the focus simply is not going to fit, then
// best is probably just to leave things as-is.
- if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Too tall; leaving scrollY=" + scrollY);
} else if ((mTempRect.top-scrollY) < vi.top) {
scrollY -= vi.top - (mTempRect.top-scrollY);
- if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Top covered; scrollY=" + scrollY);
} else if ((mTempRect.bottom-scrollY)
> (mView.getHeight()-vi.bottom)) {
scrollY += (mTempRect.bottom-scrollY)
- (mView.getHeight()-vi.bottom);
- if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag,
"Bottom covered; scrollY=" + scrollY);
}
handled = true;
@@ -2939,7 +2941,7 @@
}
if (scrollY != mScrollY) {
- if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
+ if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Pan scroll changed: old="
+ mScrollY + " , new=" + scrollY);
if (!immediate) {
if (mScroller == null) {
@@ -3018,7 +3020,7 @@
void setPointerCapture(View view) {
if (!mAttachInfo.mHasWindowFocus) {
- Log.w(TAG, "Can't set capture if it's not focused.");
+ Log.w(mTag, "Can't set capture if it's not focused.");
return;
}
if (mCapturingView == view) {
@@ -3044,7 +3046,7 @@
@Override
public void requestChildFocus(View child, View focused) {
if (DEBUG_INPUT_RESIZE) {
- Log.v(TAG, "Request child focus: focus now " + focused);
+ Log.v(mTag, "Request child focus: focus now " + focused);
}
checkThread();
scheduleTraversals();
@@ -3053,7 +3055,7 @@
@Override
public void clearChildFocus(View child) {
if (DEBUG_INPUT_RESIZE) {
- Log.v(TAG, "Clearing child focus");
+ Log.v(mTag, "Clearing child focus");
}
checkThread();
scheduleTraversals();
@@ -3152,7 +3154,7 @@
}
void updateConfiguration(Configuration config, boolean force) {
- if (DEBUG_CONFIGURATION) Log.v(TAG,
+ if (DEBUG_CONFIGURATION) Log.v(mTag,
"Applying new config to window "
+ mWindowAttributes.getTitle()
+ ": " + config);
@@ -3388,10 +3390,10 @@
mAttachInfo.mHardwareRenderer.initializeIfNeeded(
mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
- Log.e(TAG, "OutOfResourcesException locking surface", e);
+ Log.e(mTag, "OutOfResourcesException locking surface", e);
try {
if (!mWindowSession.outOfMemory(mWindow)) {
- Slog.w(TAG, "No processes killed for memory; killing self");
+ Slog.w(mTag, "No processes killed for memory; killing self");
Process.killProcess(Process.myPid());
}
} catch (RemoteException ex) {
@@ -3714,7 +3716,7 @@
*/
protected void onDeliverToNext(QueuedInputEvent q) {
if (DEBUG_INPUT_STAGES) {
- Log.v(TAG, "Done with " + getClass().getSimpleName() + ". " + q);
+ Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
}
if (mNext != null) {
mNext.deliver(q);
@@ -3725,7 +3727,7 @@
protected boolean shouldDropInputEvent(QueuedInputEvent q) {
if (mView == null || !mAdded) {
- Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent);
+ Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent);
return true;
} else if ((!mAttachInfo.mHasWindowFocus
&& !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) || mStopped
@@ -3736,12 +3738,12 @@
if (isTerminalInputEvent(q.mEvent)) {
// Don't drop terminal input events, however mark them as canceled.
q.mEvent.cancel();
- Slog.w(TAG, "Cancelling event due to no window focus: " + q.mEvent);
+ Slog.w(mTag, "Cancelling event due to no window focus: " + q.mEvent);
return false;
}
// Drop non-terminal input events.
- Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+ Slog.w(mTag, "Dropping event due to no window focus: " + q.mEvent);
return true;
}
return false;
@@ -3981,7 +3983,7 @@
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
final InputEvent event = q.mEvent;
- if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
+ if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
int result = imm.dispatchInputEvent(event, q, this, mHandler);
if (result == InputMethodManager.DISPATCH_HANDLED) {
return FINISH_HANDLED;
@@ -4413,7 +4415,7 @@
break;
}
- if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + mX.position + " step="
+ if (DEBUG_TRACKBALL) Log.v(mTag, "TB X=" + mX.position + " step="
+ mX.step + " dir=" + mX.dir + " acc=" + mX.acceleration
+ " move=" + event.getX()
+ " / Y=" + mY.position + " step="
@@ -4452,11 +4454,11 @@
if (keycode != 0) {
if (movement < 0) movement = -movement;
int accelMovement = (int)(movement * accel);
- if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
+ if (DEBUG_TRACKBALL) Log.v(mTag, "Move: movement=" + movement
+ " accelMovement=" + accelMovement
+ " accel=" + accel);
if (accelMovement > movement) {
- if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
+ if (DEBUG_TRACKBALL) Log.v(mTag, "Delivering fake DPAD: "
+ keycode);
movement--;
int repeatCount = accelMovement - movement;
@@ -4466,7 +4468,7 @@
InputDevice.SOURCE_KEYBOARD));
}
while (movement > 0) {
- if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
+ if (DEBUG_TRACKBALL) Log.v(mTag, "Delivering fake DPAD: "
+ keycode);
movement--;
curTime = SystemClock.uptimeMillis();
@@ -4708,7 +4710,7 @@
update(event, true);
break;
default:
- Log.w(TAG, "Unexpected action: " + event.getActionMasked());
+ Log.w(mTag, "Unexpected action: " + event.getActionMasked());
}
}
@@ -5347,7 +5349,7 @@
mWindowSession.dragRecipientEntered(mWindow);
}
} catch (RemoteException e) {
- Slog.e(TAG, "Unable to note drag target change");
+ Slog.e(mTag, "Unable to note drag target change");
}
}
@@ -5355,10 +5357,10 @@
if (what == DragEvent.ACTION_DROP) {
mDragDescription = null;
try {
- Log.i(TAG, "Reporting drop result: " + result);
+ Log.i(mTag, "Reporting drop result: " + result);
mWindowSession.reportDropResult(mWindow, result);
} catch (RemoteException e) {
- Log.e(TAG, "Unable to report drop result");
+ Log.e(mTag, "Unable to report drop result");
}
}
@@ -5444,14 +5446,14 @@
mTranslator.translateWindowLayout(params);
}
if (params != null) {
- if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
+ if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
}
mPendingConfiguration.seq = 0;
- //Log.d(TAG, ">>>>>> CALLING relayout");
+ //Log.d(mTag, ">>>>>> CALLING relayout");
if (params != null && mOrigWindowType != params.type) {
// For compatibility with old apps, don't crash here.
if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- Slog.w(TAG, "Window type can not be changed after "
+ Slog.w(mTag, "Window type can not be changed after "
+ "the window is added; ignoring change of " + mView);
params.type = mOrigWindowType;
}
@@ -5463,7 +5465,7 @@
viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingConfiguration, mSurface);
- //Log.d(TAG, "<<<<<< BACK FROM relayout");
+ //Log.d(mTag, "<<<<<< BACK FROM relayout");
if (restore) {
params.restore();
}
@@ -5510,7 +5512,7 @@
}
} catch (IllegalStateException e) {
// Exception thrown by getAudioManager() when mView is null
- Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e);
+ Log.e(mTag, "FATAL EXCEPTION when attempting to play sound effect: " + e);
e.printStackTrace();
}
}
@@ -5633,7 +5635,7 @@
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
- Log.e(TAG, "Attempting to destroy the window while drawing!\n" +
+ Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
mHandler.sendEmptyMessage(MSG_DIE);
@@ -5642,7 +5644,7 @@
void doDie() {
checkThread();
- if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
+ if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
@@ -5735,7 +5737,7 @@
public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
Configuration newConfig, Rect backDropFrame) {
- if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": frame=" + frame.toShortString()
+ if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
+ " contentInsets=" + contentInsets.toShortString()
+ " visibleInsets=" + visibleInsets.toShortString()
+ " reportDraw=" + reportDraw
@@ -5773,7 +5775,7 @@
}
public void dispatchMoved(int newX, int newY) {
- if (DEBUG_LAYOUT) Log.v(TAG, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
+ if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
if (mTranslator != null) {
PointF point = new PointF(newX, newY);
mTranslator.translatePointInScreenToAppWindow(point);
@@ -6690,7 +6692,7 @@
}
void changeCanvasOpacity(boolean opaque) {
- Log.d(TAG, "changeCanvasOpacity: opaque=" + opaque);
+ Log.d(mTag, "changeCanvasOpacity: opaque=" + opaque);
if (mAttachInfo.mHardwareRenderer != null) {
mAttachInfo.mHardwareRenderer.setOpaque(opaque);
}
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 0032f17..48d1d2b 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -20,6 +20,8 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -134,7 +136,7 @@
}
@Override
- public void initForMenu(Context context, MenuBuilder menu) {
+ public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
super.initForMenu(context, menu);
final Resources res = context.getResources();
@@ -629,8 +631,16 @@
}
public boolean flagActionItems() {
- final ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
- final int itemsSize = visibleItems.size();
+ final ArrayList<MenuItemImpl> visibleItems;
+ final int itemsSize;
+ if (mMenu != null) {
+ visibleItems = mMenu.getVisibleItems();
+ itemsSize = visibleItems.size();
+ } else {
+ visibleItems = null;
+ itemsSize = 0;
+ }
+
int maxActions = mMaxItems;
int widthLimit = mActionItemWidthLimit;
final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
@@ -786,7 +796,7 @@
if (isVisible) {
// Not a submenu, but treat it like one.
super.onSubMenuSelected(null);
- } else {
+ } else if (mMenu != null) {
mMenu.close(false /* closeAllMenus */);
}
}
@@ -938,7 +948,9 @@
@Override
protected void onDismiss() {
- mMenu.close();
+ if (mMenu != null) {
+ mMenu.close();
+ }
mOverflowPopup = null;
super.onDismiss();
@@ -999,7 +1011,9 @@
}
public void run() {
- mMenu.changeMenuMode();
+ if (mMenu != null) {
+ mMenu.changeMenuMode();
+ }
final View menuView = (View) mMenuView;
if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) {
mOverflowPopup = mPopup;
diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java
index 1f02c3b..4d0a1c8 100644
--- a/core/java/android/widget/ActionMenuView.java
+++ b/core/java/android/widget/ActionMenuView.java
@@ -622,7 +622,7 @@
}
/** @hide */
- public void initialize(MenuBuilder menu) {
+ public void initialize(@Nullable MenuBuilder menu) {
mMenu = menu;
}
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index acbf5eb..8e711b0 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -2070,7 +2070,7 @@
MenuItemImpl mCurrentExpandedItem;
@Override
- public void initForMenu(Context context, MenuBuilder menu) {
+ public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
// Clear the expanded action view when menus change.
if (mMenu != null && mCurrentExpandedItem != null) {
mMenu.collapseItemActionView(mCurrentExpandedItem);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 40eaaf7..cc2f714 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -23,7 +23,6 @@
import com.android.internal.view.StandaloneActionMode;
import com.android.internal.view.menu.ContextMenuBuilder;
import com.android.internal.view.menu.MenuHelper;
-import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.DecorCaptionView;
@@ -72,6 +71,7 @@
import static android.app.ActivityManager.StackId;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.getMode;
@@ -194,7 +194,12 @@
private Drawable mCaptionBackgroundDrawable;
private Drawable mUserCaptionBackgroundDrawable;
- DecorView(Context context, int featureId, PhoneWindow window) {
+ private float mAvailableWidth;
+
+ String mLogTag = TAG;
+
+ DecorView(Context context, int featureId, PhoneWindow window,
+ WindowManager.LayoutParams params) {
super(context);
mFeatureId = featureId;
@@ -210,7 +215,11 @@
mSemiTransparentStatusBarColor = context.getResources().getColor(
R.color.system_bar_background_semi_transparent, null /* theme */);
+ updateAvailableWidth();
+
setWindow(window);
+
+ updateLogTag(params);
}
void setBackgroundFallback(int resId) {
@@ -408,7 +417,7 @@
if (mFeatureId >= 0) {
if (action == MotionEvent.ACTION_DOWN) {
- Log.i(TAG, "Watchiing!");
+ Log.i(mLogTag, "Watchiing!");
mWatchingForMenu = true;
mDownY = (int) event.getY();
return false;
@@ -421,7 +430,7 @@
int y = (int)event.getY();
if (action == MotionEvent.ACTION_MOVE) {
if (y > (mDownY+30)) {
- Log.i(TAG, "Closing!");
+ Log.i(mLogTag, "Closing!");
mWindow.closePanel(mFeatureId);
mWatchingForMenu = false;
return true;
@@ -433,13 +442,13 @@
return false;
}
- //Log.i(TAG, "Intercept: action=" + action + " y=" + event.getY()
+ //Log.i(mLogTag, "Intercept: action=" + action + " y=" + event.getY()
// + " (in " + getHeight() + ")");
if (action == MotionEvent.ACTION_DOWN) {
int y = (int)event.getY();
if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
- Log.i(TAG, "Watching!");
+ Log.i(mLogTag, "Watching!");
mWatchingForMenu = true;
}
return false;
@@ -452,7 +461,7 @@
int y = (int)event.getY();
if (action == MotionEvent.ACTION_MOVE) {
if (y < (getHeight()-30)) {
- Log.i(TAG, "Opening!");
+ Log.i(mLogTag, "Opening!");
mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, new KeyEvent(
KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
mWatchingForMenu = false;
@@ -543,15 +552,15 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
- final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
+ final boolean isPortrait =
+ getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
final int widthMode = getMode(widthMeasureSpec);
final int heightMode = getMode(heightMeasureSpec);
boolean fixedWidth = false;
if (widthMode == AT_MOST) {
- final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor
- : mWindow.mFixedWidthMajor;
+ final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor : mWindow.mFixedWidthMajor;
if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
final int w;
if (tvw.type == TypedValue.TYPE_DIMENSION) {
@@ -623,7 +632,7 @@
if (tv.type == TypedValue.TYPE_DIMENSION) {
min = (int)tv.getDimension(metrics);
} else if (tv.type == TypedValue.TYPE_FRACTION) {
- min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
+ min = (int)tv.getFraction(mAvailableWidth, mAvailableWidth);
} else {
min = 0;
}
@@ -1217,7 +1226,7 @@
int fop = fg.getOpacity();
int bop = bg.getOpacity();
if (false)
- Log.v(TAG, "Background opacity: " + bop + ", Frame opacity: " + fop);
+ Log.v(mLogTag, "Background opacity: " + bop + ", Frame opacity: " + fop);
if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
opacity = PixelFormat.OPAQUE;
} else if (fop == PixelFormat.UNKNOWN) {
@@ -1232,16 +1241,16 @@
// frame with padding... there is no way to tell if the
// frame and background together will draw all pixels.
if (false)
- Log.v(TAG, "Padding: " + mFramePadding);
+ Log.v(mLogTag, "Padding: " + mFramePadding);
opacity = PixelFormat.TRANSLUCENT;
}
}
if (false)
- Log.v(TAG, "Background: " + bg + ", Frame: " + fg);
+ Log.v(mLogTag, "Background: " + bg + ", Frame: " + fg);
}
if (false)
- Log.v(TAG, "Selected default opacity: " + opacity);
+ Log.v(mLogTag, "Selected default opacity: " + opacity);
mDefaultOpacity = opacity;
if (mFeatureId < 0) {
@@ -1600,6 +1609,7 @@
enableCaption(StackId.hasWindowDecor(workspaceId));
}
}
+ updateAvailableWidth();
initializeElevation();
}
@@ -1744,7 +1754,7 @@
// We shouldn't really get here as the background fallback should be always available since
// it is defaulted by the system.
- Log.w(TAG, "Failed to find background drawable for PhoneWindow=" + mWindow);
+ Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
return null;
}
@@ -1761,7 +1771,7 @@
try {
workspaceId = callback.getWindowStackId();
} catch (RemoteException ex) {
- Log.e(TAG, "Failed to get the workspace ID of a PhoneWindow.");
+ Log.e(mLogTag, "Failed to get the workspace ID of a PhoneWindow.");
}
}
if (workspaceId == INVALID_STACK_ID) {
@@ -1927,6 +1937,19 @@
}
}
+ void updateLogTag(WindowManager.LayoutParams params) {
+ final String[] split = params.getTitle().toString().split("\\.");
+ if (split.length > 0) {
+ mLogTag = TAG + "[" + split[split.length - 1] + "]";
+ }
+ }
+
+ private void updateAvailableWidth() {
+ Resources res = getResources();
+ mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
+ }
+
private static class ColorViewState {
View view = null;
int targetVisibility = View.INVISIBLE;
@@ -1988,12 +2011,12 @@
isPrimary = mode == mPrimaryActionMode;
isFloating = mode == mFloatingActionMode;
if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
- Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
+ Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
+ mode + " was not the current primary action mode! Expected "
+ mPrimaryActionMode);
}
if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
- Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
+ Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
+ mode + " was not the current floating action mode! Expected "
+ mFloatingActionMode);
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 2f951cc..f159a4d 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -113,6 +113,8 @@
private final static String TAG = "PhoneWindow";
+ private static final boolean DEBUG = false;
+
private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;
private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |
@@ -2286,7 +2288,7 @@
} else {
context = getContext();
}
- return new DecorView(context, featureId, this);
+ return new DecorView(context, featureId, this, getAttributes());
}
protected ViewGroup generateLayout(DecorView decor) {
@@ -2365,6 +2367,8 @@
a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
+ if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString()
+ + ", major: " + mMinWidthMajor.coerceToString());
if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {
if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
a.getValue(R.styleable.Window_windowFixedWidthMajor,
@@ -3781,4 +3785,12 @@
int getDecorCaptionShade() {
return mDecorCaptionShade;
}
+
+ @Override
+ public void setAttributes(WindowManager.LayoutParams params) {
+ super.setAttributes(params);
+ if (mDecor != null) {
+ mDecor.updateLogTag(params);
+ }
+ }
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 849d314..4dd71e7 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -46,7 +46,7 @@
void cancelPreloadRecentApps();
void showScreenPinningRequest();
- void showKeyboardShortcutsMenu();
+ void toggleKeyboardShortcutsMenu();
/**
* Notifies the status bar that an app transition is pending to delay applying some flags with
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 0a4ad06..0125d37 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -68,7 +68,7 @@
void preloadRecentApps();
void cancelPreloadRecentApps();
- void showKeyboardShortcutsMenu();
+ void toggleKeyboardShortcutsMenu();
/**
* Notifies the status bar that an app transition is pending to delay applying some flags with
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 406b487..dc66818 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -779,7 +779,7 @@
@Override
public final void handleMessage(Message msg) {
if (!mHasQuit) {
- if (mSm != null) {
+ if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPreHandleMessage(msg);
}
@@ -807,7 +807,7 @@
// We need to check if mSm == null here as we could be quitting.
if (mDbg && mSm != null) mSm.log("handleMessage: X");
- if (mSm != null) {
+ if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
mSm.onPostHandleMessage(msg);
}
}
diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
index 92e9ea6..7ac0ac3 100644
--- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
@@ -16,6 +16,8 @@
package com.android.internal.view.menu;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
@@ -58,7 +60,7 @@
}
@Override
- public void initForMenu(Context context, MenuBuilder menu) {
+ public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
mContext = context;
mInflater = LayoutInflater.from(mContext);
mMenu = menu;
diff --git a/core/java/com/android/internal/view/menu/IconMenuPresenter.java b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
index 2439b5d..5223a7b 100644
--- a/core/java/com/android/internal/view/menu/IconMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
@@ -17,6 +17,8 @@
import com.android.internal.view.menu.MenuView.ItemView;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
@@ -49,7 +51,7 @@
}
@Override
- public void initForMenu(Context context, MenuBuilder menu) {
+ public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
super.initForMenu(context, menu);
mMaxItems = -1;
}
diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
index c476354..2fff3ba 100644
--- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
@@ -16,6 +16,8 @@
package com.android.internal.view.menu;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
@@ -76,7 +78,7 @@
}
@Override
- public void initForMenu(Context context, MenuBuilder menu) {
+ public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
if (mThemeRes != 0) {
mContext = new ContextThemeWrapper(context, mThemeRes);
mInflater = LayoutInflater.from(mContext);
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 465d775..31b2f96 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -17,6 +17,7 @@
package com.android.internal.view.menu;
+import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -1027,23 +1028,24 @@
mIsActionItemsStale = true;
onItemsChanged(true);
}
-
+
+ @NonNull
public ArrayList<MenuItemImpl> getVisibleItems() {
if (!mIsVisibleItemsStale) return mVisibleItems;
-
+
// Refresh the visible items
mVisibleItems.clear();
-
+
final int itemsSize = mItems.size();
MenuItemImpl item;
for (int i = 0; i < itemsSize; i++) {
item = mItems.get(i);
if (item.isVisible()) mVisibleItems.add(item);
}
-
+
mIsVisibleItemsStale = false;
mIsActionItemsStale = true;
-
+
return mVisibleItems;
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopup.java b/core/java/com/android/internal/view/menu/MenuPopup.java
index 98f5d90..b151f34 100644
--- a/core/java/com/android/internal/view/menu/MenuPopup.java
+++ b/core/java/com/android/internal/view/menu/MenuPopup.java
@@ -16,6 +16,8 @@
package com.android.internal.view.menu;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.view.MenuItem;
import android.view.View;
@@ -73,7 +75,7 @@
public abstract void setOnDismissListener(PopupWindow.OnDismissListener listener);
@Override
- public void initForMenu(Context context, MenuBuilder menu) {
+ public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
// Don't need to do anything; we added as a presenter in the constructor.
}
diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java
index c847c15..65bdc09 100644
--- a/core/java/com/android/internal/view/menu/MenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/MenuPresenter.java
@@ -16,6 +16,8 @@
package com.android.internal.view.menu;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.Parcelable;
import android.view.ViewGroup;
@@ -49,14 +51,16 @@
}
/**
- * Initialize this presenter for the given context and menu.
- * This method is called by MenuBuilder when a presenter is
- * added. See {@link MenuBuilder#addMenuPresenter(MenuPresenter)}
+ * Initializes this presenter for the given context and menu.
+ * <p>
+ * This method is called by MenuBuilder when a presenter is added. See
+ * {@link MenuBuilder#addMenuPresenter(MenuPresenter)}.
*
- * @param context Context for this presenter; used for view creation and resource management
- * @param menu Menu to host
+ * @param context the context for this presenter; used for view creation
+ * and resource management, must be non-{@code null}
+ * @param menu the menu to host, or {@code null} to clear the hosted menu
*/
- public void initForMenu(Context context, MenuBuilder menu);
+ public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu);
/**
* Retrieve a MenuView to display the menu specified in
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 825e336..f90b59d 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -17,6 +17,8 @@
package com.android.internal.widget;
import android.animation.LayoutTransition;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActionBar;
import android.content.Context;
import android.content.res.Configuration;
@@ -1593,7 +1595,7 @@
MenuItemImpl mCurrentExpandedItem;
@Override
- public void initForMenu(Context context, MenuBuilder menu) {
+ public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
// Clear the expanded action view when menus change.
if (mMenu != null && mCurrentExpandedItem != null) {
mMenu.collapseItemActionView(mCurrentExpandedItem);
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index d8ec22a..92f7812 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -260,6 +260,15 @@
return nullObjectReturn("SkAndroidCodec::NewFromStream returned null");
}
+ // Do not allow ninepatch decodes to 565. In the past, decodes to 565
+ // would dither, and we do not want to pre-dither ninepatches, since we
+ // know that they will be stretched. We no longer dither 565 decodes,
+ // but we continue to prevent ninepatches from decoding to 565, in order
+ // to maintain the old behavior.
+ if (peeker.mPatch && kRGB_565_SkColorType == prefColorType) {
+ prefColorType = kN32_SkColorType;
+ }
+
// Determine the output size and return if the client only wants the size.
SkISize size = codec->getSampledDimensions(sampleSize);
if (options != NULL) {
@@ -369,15 +378,7 @@
case SkCodec::kIncompleteInput:
break;
default:
- return nullObjectReturn("codec->getAndoridPixels() failed.");
- }
-
- // Some images may initially report that they have alpha due to the format
- // of the encoded data, but then never use any colors which have alpha
- // less than 100%. Here we check if the image really had alpha, and
- // mark it as opaque if it is actually opaque.
- if (kOpaque_SkAlphaType != alphaType && !codec->reallyHasAlpha()) {
- decodingBitmap.setAlphaType(kOpaque_SkAlphaType);
+ return nullObjectReturn("codec->getAndroidPixels() failed.");
}
int scaledWidth = size.width();
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 7860b74..768c7d4 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1049,6 +1049,10 @@
pJniStorage->mDeviceCallback.clear();
}
+static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
+ return FCC_8;
+}
+
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
@@ -1106,6 +1110,7 @@
{"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
{"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
{"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
+ {"native_get_FCC_8", "()I", (void *)android_media_AudioTrack_get_FCC_8},
};
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index bb4f7d9..a810ff1 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -97,9 +97,10 @@
/** Maximum value for sample rate */
private static final int SAMPLE_RATE_HZ_MAX = 192000;
- // FCC_8
- /** Maximum value for AudioTrack channel count */
- private static final int CHANNEL_COUNT_MAX = 8;
+ /** Maximum value for AudioTrack channel count
+ * @hide public for MediaCode only, do not un-hide or change to a numeric literal
+ */
+ public static final int CHANNEL_COUNT_MAX = native_get_FCC_8();
/** indicates AudioTrack state is stopped */
public static final int PLAYSTATE_STOPPED = 1; // matches SL_PLAYSTATE_STOPPED
@@ -2583,6 +2584,7 @@
private native final int native_getRoutedDeviceId();
private native final void native_enableDeviceCallback();
private native final void native_disableDeviceCallback();
+ static private native int native_get_FCC_8();
//---------------------------------------------------------
// Utility methods
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index e6bc8f1..9bcb5e3 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -907,7 +907,7 @@
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
sampleRateRange = Range.create(1, 96000);
bitRates = Range.create(1, 10000000);
- maxChannels = 8;
+ maxChannels = AudioTrack.CHANNEL_COUNT_MAX;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
sampleRateRange = Range.create(1, 655350);
// lossless codec, so bitrate is ignored
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 70d651f..b63df6f 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -655,7 +655,7 @@
goto error;
}
- if ((numChannels < 1) || (numChannels > 8)) {
+ if ((numChannels < 1) || (numChannels > FCC_8)) {
ALOGE("Sample channel count (%d) out of range", numChannels);
status = BAD_VALUE;
goto error;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 6a5911b..34614b4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -502,11 +502,6 @@
// 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) {
@@ -516,8 +511,7 @@
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.
+ // The virtual file is not available as any alternative streamable format.
// TODO: Log failures. b/26192412
mFailedFiles.add(srcInfo);
return false;
@@ -640,9 +634,8 @@
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()) {
+ // If the file is virtual, then try to copy it as an alternative format.
+ if (srcInfo.isVirtualDocument()) {
final AssetFileDescriptor srcFileAsAsset =
mSrcClient.openTypedAssetFileDescriptor(
srcInfo.derivedUri, mimeType, null, canceller);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 215c6e6..83df18c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -255,10 +255,6 @@
return (flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}
- public boolean isTypedDocument() {
- return (flags & Document.FLAG_SUPPORTS_TYPED_DOCUMENT) != 0;
- }
-
public int hashCode() {
return derivedUri.hashCode() + mimeType.hashCode();
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
index 24a8113..4ce0185 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
@@ -311,10 +311,8 @@
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());
+ null /* streamTypes */, "I love Tokyo!".getBytes());
Intent intent = createCopyIntent(Lists.newArrayList(testFile));
startService(intent);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index 2c311a7..50f4628 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -316,12 +316,9 @@
String documentId, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
throws FileNotFoundException {
final StubDocument document = mStorage.get(documentId);
- if (document == null || !document.file.isFile()) {
+ if (document == null || !document.file.isFile() || document.streamTypes == null) {
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.
@@ -349,13 +346,13 @@
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();
}
+ if (document.streamTypes == null) {
+ return null;
+ }
return document.streamTypes.toArray(new String[document.streamTypes.size()]);
}
@@ -628,9 +625,6 @@
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);
}
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 97a7bff..b662c58 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -178,12 +178,6 @@
<!-- Template for the notification label for a blocked print job. [CHAR LIMIT=25] -->
<string name="blocked_notification_title_template">Printer blocked <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
- <!-- Template for the notification label for a composite (multiple items) print jobs notification. [CHAR LIMIT=25] -->
- <plurals name="composite_notification_title_template">
- <item quantity="one"><xliff:g id="print_job_name" example="foo.jpg">%1$d</xliff:g> print job</item>
- <item quantity="other"><xliff:g id="print_job_name" example="foo.jpg">%1$d</xliff:g> print jobs</item>
- </plurals>
-
<!-- Label for the notification button for cancelling a print job. [CHAR LIMIT=25] -->
<string name="cancel">Cancel</string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index 3dc5d7e..0210693 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -40,6 +40,7 @@
import android.print.PrintJobInfo;
import android.print.PrintManager;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.Log;
import com.android.printspooler.R;
@@ -61,13 +62,22 @@
private static final String EXTRA_PRINT_JOB_ID = "EXTRA_PRINT_JOB_ID";
+ private static final String PRINT_JOB_NOTIFICATION_GROUP_KEY = "PRINT_JOB_NOTIFICATIONS";
+ private static final String PRINT_JOB_NOTIFICATION_SUMMARY = "PRINT_JOB_NOTIFICATIONS_SUMMARY";
+
private final Context mContext;
private final NotificationManager mNotificationManager;
+ /**
+ * Mapping from printJobIds to their notification Ids.
+ */
+ private final ArraySet<PrintJobId> mNotifications;
+
public NotificationController(Context context) {
mContext = context;
mNotificationManager = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mNotifications = new ArraySet<>(0);
}
public void onUpdateNotifications(List<PrintJobInfo> printJobs) {
@@ -81,16 +91,44 @@
}
}
- updateNotification(notifyPrintJobs);
+ updateNotifications(notifyPrintJobs);
}
- private void updateNotification(List<PrintJobInfo> printJobs) {
- if (printJobs.size() <= 0) {
- removeNotification();
- } else if (printJobs.size() == 1) {
- createSimpleNotification(printJobs.get(0));
- } else {
+ /**
+ * Update notifications for the given print jobs, remove all other notifications.
+ *
+ * @param printJobs The print job that we want to create notifications for.
+ */
+ private void updateNotifications(List<PrintJobInfo> printJobs) {
+ ArraySet<PrintJobId> removedPrintJobs = new ArraySet<>(mNotifications);
+
+ final int numPrintJobs = printJobs.size();
+
+ // Create summary notification
+ if (numPrintJobs > 1) {
createStackedNotification(printJobs);
+ } else {
+ mNotificationManager.cancel(PRINT_JOB_NOTIFICATION_SUMMARY, 0);
+ }
+
+ // Create per print job notification
+ for (int i = 0; i < numPrintJobs; i++) {
+ PrintJobInfo printJob = printJobs.get(i);
+ PrintJobId printJobId = printJob.getId();
+
+ removedPrintJobs.remove(printJobId);
+ mNotifications.add(printJobId);
+
+ createSimpleNotification(printJob);
+ }
+
+ // Remove notifications for print jobs that do not exist anymore
+ final int numRemovedPrintJobs = removedPrintJobs.size();
+ for (int i = 0; i < numRemovedPrintJobs; i++) {
+ PrintJobId removedPrintJob = removedPrintJobs.valueAt(i);
+
+ mNotificationManager.cancel(removedPrintJob.flattenToString(), 0);
+ mNotifications.remove(removedPrintJob);
}
}
@@ -148,7 +186,8 @@
.setOngoing(true)
.setShowWhen(true)
.setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
+ com.android.internal.R.color.system_notification_accent_color))
+ .setGroup(PRINT_JOB_NOTIFICATION_GROUP_KEY);
if (firstAction != null) {
builder.addAction(firstAction);
@@ -176,7 +215,7 @@
builder.setContentText(printJob.getPrinterName());
}
- mNotificationManager.notify(0, builder.build());
+ mNotificationManager.notify(printJob.getId().flattenToString(), 0, builder.build());
}
private void createPrintingNotification(PrintJobInfo printJob) {
@@ -204,33 +243,36 @@
.setContentIntent(createContentIntent(null))
.setWhen(System.currentTimeMillis())
.setOngoing(true)
- .setShowWhen(true);
+ .setShowWhen(true)
+ .setGroup(PRINT_JOB_NOTIFICATION_GROUP_KEY)
+ .setGroupSummary(true);
final int printJobCount = printJobs.size();
InboxStyle inboxStyle = new InboxStyle();
- inboxStyle.setBigContentTitle(String.format(mContext.getResources().getQuantityText(
- R.plurals.composite_notification_title_template,
- printJobCount).toString(), printJobCount));
+ int icon = com.android.internal.R.drawable.ic_print;
for (int i = printJobCount - 1; i>= 0; i--) {
PrintJobInfo printJob = printJobs.get(i);
- if (i == printJobCount - 1) {
- builder.setLargeIcon(((BitmapDrawable) mContext.getResources().getDrawable(
- computeNotificationIcon(printJob), null)).getBitmap());
- builder.setSmallIcon(computeNotificationIcon(printJob));
- builder.setContentTitle(computeNotificationTitle(printJob));
- builder.setContentText(printJob.getPrinterName());
- }
+
inboxStyle.addLine(computeNotificationTitle(printJob));
+
+ // if any print job is in an error state show an error icon for the summary
+ if (printJob.getState() == PrintJobInfo.STATE_FAILED
+ || printJob.getState() == PrintJobInfo.STATE_BLOCKED) {
+ icon = com.android.internal.R.drawable.ic_print_error;
+ }
}
+ builder.setSmallIcon(icon);
+ builder.setLargeIcon(
+ ((BitmapDrawable) mContext.getResources().getDrawable(icon, null)).getBitmap());
builder.setNumber(printJobCount);
builder.setStyle(inboxStyle);
builder.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color));
- mNotificationManager.notify(0, builder.build());
+ mNotificationManager.notify(PRINT_JOB_NOTIFICATION_SUMMARY, 0, builder.build());
}
private String computeNotificationTitle(PrintJobInfo printJob) {
@@ -264,10 +306,6 @@
}
}
- private void removeNotification() {
- mNotificationManager.cancel(0);
- }
-
private PendingIntent createContentIntent(PrintJobId printJobId) {
Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
if (printJobId != null) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index 496a0b0..18160ff 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -1416,9 +1416,9 @@
}
@Override
- public void removeApprovedPrintService(ComponentName serviceToRemove) {
+ public void pruneApprovedPrintServices(List<ComponentName> servicesToKeep) {
(new ApprovedPrintServices(PrintSpoolerService.this))
- .removeApprovedService(serviceToRemove);
+ .pruneApprovedServices(servicesToKeep);
}
@Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index cdfc7ee..81727ab 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -66,6 +66,7 @@
import android.widget.SearchView;
import android.widget.TextView;
+import com.android.internal.content.PackageMonitor;
import com.android.printspooler.R;
import java.util.ArrayList;
@@ -101,7 +102,8 @@
private AnnounceFilterResult mAnnounceFilterResult;
/** Monitor if new print services get enabled or disabled */
- private ContentObserver mPrintServicesObserver;
+ private ContentObserver mPrintServicesDisabledObserver;
+ private PackageMonitor mPackageObserver;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -245,28 +247,45 @@
* Register listener for changes to the enabled print services.
*/
private void registerServiceMonitor() {
- mPrintServicesObserver = new ContentObserver(new Handler()) {
+ // Listen for services getting disabled
+ mPrintServicesDisabledObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
onPrintServicesUpdate();
}
};
+ // Listen for services getting installed or uninstalled
+ mPackageObserver = new PackageMonitor() {
+ @Override
+ public void onPackageModified(String packageName) {
+ onPrintServicesUpdate();
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ onPrintServicesUpdate();
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ onPrintServicesUpdate();
+ }
+ };
+
getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ENABLED_PRINT_SERVICES), false,
- mPrintServicesObserver);
+ Settings.Secure.getUriFor(Settings.Secure.DISABLED_PRINT_SERVICES), false,
+ mPrintServicesDisabledObserver);
+
+ mPackageObserver.register(this, getMainLooper(), false);
}
/**
- * Unregister {@link #mPrintServicesObserver listener for changes to the enabled print services}
- * or nothing if the listener is not registered.
+ * Unregister the listeners for changes to the enabled print services.
*/
private void unregisterServiceMonitorIfNeeded() {
- if (mPrintServicesObserver != null) {
- getContentResolver().unregisterContentObserver(mPrintServicesObserver);
-
- mPrintServicesObserver = null;
- }
+ getContentResolver().unregisterContentObserver(mPrintServicesDisabledObserver);
+ mPackageObserver.unregister();
}
@Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java b/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java
index dd10567..a1e3ef4 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java
@@ -23,6 +23,7 @@
import android.printservice.PrintService;
import android.util.ArraySet;
+import java.util.List;
import java.util.Set;
/**
@@ -126,29 +127,27 @@
}
/**
- * If a {@link PrintService} is approved, remove it from the list of approved services.
+ * Remove all approved {@link PrintService print services} that are not in the given set.
*
- * @param serviceToRemove The {@link ComponentName} of the {@link PrintService} to be removed
+ * @param serviceNamesToKeep The {@link ComponentName names } of the services to keep
*/
- public void removeApprovedService(ComponentName serviceToRemove) {
+ public void pruneApprovedServices(List<ComponentName> serviceNamesToKeep) {
synchronized (sLock) {
- if (isApprovedService(serviceToRemove)) {
- // Copy approved services.
- ArraySet<String> approvedServices = new ArraySet<String>(
- mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null));
+ Set<String> approvedServices = getApprovedServices();
+ Set<String> newApprovedServices = new ArraySet<>(approvedServices.size());
+ final int numServiceNamesToKeep = serviceNamesToKeep.size();
+ for(int i = 0; i < numServiceNamesToKeep; i++) {
+ String serviceToKeep = serviceNamesToKeep.get(i).flattenToShortString();
+ if (approvedServices.contains(serviceToKeep)) {
+ newApprovedServices.add(serviceToKeep);
+ }
+ }
+
+ if (approvedServices.size() != newApprovedServices.size()) {
SharedPreferences.Editor editor = mPreferences.edit();
- final int numApprovedServices = approvedServices.size();
- for (int i = 0; i < numApprovedServices; i++) {
- if (approvedServices.valueAt(i)
- .equals(serviceToRemove.flattenToShortString())) {
- approvedServices.removeAt(i);
- break;
- }
- }
-
- editor.putStringSet(APPROVED_SERVICES_PREFERENCE, approvedServices);
+ editor.putStringSet(APPROVED_SERVICES_PREFERENCE, newApprovedServices);
editor.apply();
}
}
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index f7a2d75..00a6cbd 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -21,6 +21,7 @@
import static com.android.shell.BugreportPrefs.getWarningState;
import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -28,10 +29,13 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
import java.text.NumberFormat;
import java.util.ArrayList;
+import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import libcore.io.Streams;
@@ -813,6 +817,9 @@
Log.e(TAG, "INTERNAL ERROR: no info for PID " + pid + ": " + mProcesses);
return;
}
+
+ addDetailsToZipFile(info);
+
final Intent sendIntent = buildSendIntent(mContext, info);
final Intent notifIntent;
@@ -868,7 +875,7 @@
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
- info.bugreportFile = zipBugreport(info.bugreportFile);
+ zipBugreport(info);
sendBugreportNotification(context, info);
return null;
}
@@ -879,35 +886,92 @@
* Zips a bugreport file, returning the path to the new file (or to the
* original in case of failure).
*/
- private static File zipBugreport(File bugreportFile) {
- String bugreportPath = bugreportFile.getAbsolutePath();
- String zippedPath = bugreportPath.replace(".txt", ".zip");
+ private static void zipBugreport(BugreportInfo info) {
+ final String bugreportPath = info.bugreportFile.getAbsolutePath();
+ final String zippedPath = bugreportPath.replace(".txt", ".zip");
Log.v(TAG, "zipping " + bugreportPath + " as " + zippedPath);
- File bugreportZippedFile = new File(zippedPath);
- try (InputStream is = new FileInputStream(bugreportFile);
+ final File bugreportZippedFile = new File(zippedPath);
+ try (InputStream is = new FileInputStream(info.bugreportFile);
ZipOutputStream zos = new ZipOutputStream(
new BufferedOutputStream(new FileOutputStream(bugreportZippedFile)))) {
- ZipEntry entry = new ZipEntry(bugreportFile.getName());
- entry.setTime(bugreportFile.lastModified());
- zos.putNextEntry(entry);
- int totalBytes = Streams.copy(is, zos);
- Log.v(TAG, "size of original bugreport: " + totalBytes + " bytes");
- zos.closeEntry();
- // Delete old file;
- boolean deleted = bugreportFile.delete();
+ addEntry(zos, info.bugreportFile.getName(), is);
+ // Delete old file
+ final boolean deleted = info.bugreportFile.delete();
if (deleted) {
Log.v(TAG, "deleted original bugreport (" + bugreportPath + ")");
} else {
Log.e(TAG, "could not delete original bugreport (" + bugreportPath + ")");
}
- return bugreportZippedFile;
+ info.bugreportFile = bugreportZippedFile;
} catch (IOException e) {
Log.e(TAG, "exception zipping file " + zippedPath, e);
- return bugreportFile; // Return original.
}
}
/**
+ * Adds the user-provided info into the bugreport zip file.
+ * <p>
+ * If user provided a title, it will be saved into a {@code title.txt} entry; similarly, the
+ * description will be saved on {@code description.txt}.
+ */
+ private void addDetailsToZipFile(BugreportInfo info) {
+ // It's not possible to add a new entry into an existing file, so we need to create a new
+ // zip, copy all entries, then rename it.
+ final File dir = info.bugreportFile.getParentFile();
+ final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName());
+ Log.d(TAG, "Writing temporary zip file (" + tmpZip + ")");
+ try (ZipFile oldZip = new ZipFile(info.bugreportFile);
+ ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tmpZip))) {
+
+ // First copy contents from original zip.
+ Enumeration<? extends ZipEntry> entries = oldZip.entries();
+ while (entries.hasMoreElements()) {
+ final ZipEntry entry = entries.nextElement();
+ final String entryName = entry.getName();
+ if (!entry.isDirectory()) {
+ addEntry(zos, entryName, entry.getTime(), oldZip.getInputStream(entry));
+ } else {
+ Log.w(TAG, "skipping directory entry: " + entryName);
+ }
+ }
+
+ // Then add the user-provided info.
+ addEntry(zos, "title.txt", info.title);
+ addEntry(zos, "description.txt", info.description);
+ } catch (IOException e) {
+ Log.e(TAG, "exception zipping file " + tmpZip, e);
+ return;
+ }
+
+ if (!tmpZip.renameTo(info.bugreportFile)) {
+ Log.e(TAG, "Could not rename " + tmpZip + " to " + info.bugreportFile);
+ }
+ }
+
+ private static void addEntry(ZipOutputStream zos, String entry, String text)
+ throws IOException {
+ if (DEBUG) Log.v(TAG, "adding entry '" + entry + "': " + text);
+ if (!TextUtils.isEmpty(text)) {
+ addEntry(zos, entry, new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)));
+ }
+ }
+
+ private static void addEntry(ZipOutputStream zos, String entryName, InputStream is)
+ throws IOException {
+ addEntry(zos, entryName, System.currentTimeMillis(), is);
+ }
+
+ private static void addEntry(ZipOutputStream zos, String entryName, long timestamp,
+ InputStream is) throws IOException {
+ final ZipEntry entry = new ZipEntry(entryName);
+ entry.setTime(timestamp);
+ zos.putNextEntry(entry);
+ final int totalBytes = Streams.copy(is, zos);
+ if (DEBUG) Log.v(TAG, "size of '" + entryName + "' entry: " + totalBytes + " bytes");
+ zos.closeEntry();
+ }
+
+ /**
* Find the best matching {@link Account} based on build properties.
*/
private static Account findSendToAccount(Context context) {
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 8e8924a..d1a07ea 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -60,6 +60,7 @@
import android.support.test.uiautomator.UiObject;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
@@ -103,6 +104,12 @@
private static final String NAME = "BUG, Y U NO REPORT?";
private static final String NEW_NAME = "Bug_Forrest_Bug";
private static final String TITLE = "Wimbugdom Champion 2015";
+
+ private static final String NO_DESCRIPTION = null;
+ private static final String NO_NAME = null;
+ private static final String NO_SCREENSHOT = null;
+ private static final String NO_TITLE = null;
+
private String mDescription;
private String mPlainTextPath;
@@ -157,8 +164,8 @@
Bundle extras =
sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath, mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, NAME, ZIP_FILE,
- null, 1, true);
+ assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, ZIP_FILE,
+ NAME, NO_TITLE, NO_DESCRIPTION, 1, true);
assertServiceNotRunning();
}
@@ -174,13 +181,14 @@
Bundle extras =
sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath, mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, NAME, ZIP_FILE,
- null, 2, true);
+ assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, ZIP_FILE,
+ NAME, NO_TITLE, NO_DESCRIPTION, 2, true);
assertServiceNotRunning();
}
- public void testProgress_changeDetails() throws Exception {
+ public void testProgress_changeDetailsInvalidInput() throws Exception {
+
resetProperties();
sendBugreportStarted(1000);
waitForScreenshotButtonEnabled(true);
@@ -219,8 +227,47 @@
Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath,
mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, NEW_NAME, TITLE,
- mDescription, 1, true);
+ assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, TITLE,
+ NEW_NAME, TITLE, mDescription, 1, true);
+
+ assertServiceNotRunning();
+ }
+
+ public void testProgress_changeDetailsPlainBugreport() throws Exception {
+ changeDetailsTest(true);
+ }
+
+ public void testProgress_changeDetailsZippedBugreport() throws Exception {
+ changeDetailsTest(false);
+ }
+
+ public void changeDetailsTest(boolean plainText) throws Exception {
+
+ resetProperties();
+ sendBugreportStarted(1000);
+ waitForScreenshotButtonEnabled(true);
+
+ DetailsUi detailsUi = new DetailsUi(mUiBot);
+
+ // Check initial name.
+ String actualName = detailsUi.nameField.getText().toString();
+ assertEquals("Wrong value on field 'name'", NAME, actualName);
+
+ // Change fields.
+ detailsUi.reOpen();
+ detailsUi.nameField.setText(NEW_NAME);
+ detailsUi.titleField.setText(TITLE);
+ detailsUi.descField.setText(mDescription);
+
+ detailsUi.clickOk();
+
+ assertPropertyValue(NAME_PROPERTY, NEW_NAME);
+ assertProgressNotification(NEW_NAME, "0.00%");
+
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID,
+ plainText? mPlainTextPath : mZipPath, mScreenshotPath);
+ assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, TITLE,
+ NEW_NAME, TITLE, mDescription, 1, true);
assertServiceNotRunning();
}
@@ -270,8 +317,8 @@
// Finally, share bugreport.
Bundle extras = acceptBugreportAndGetSharedIntent();
- assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, NAME, TITLE,
- mDescription, 1, waitScreenshot);
+ assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, TITLE,
+ NAME, TITLE, mDescription, 1, waitScreenshot);
assertServiceNotRunning();
}
@@ -296,7 +343,7 @@
// Share the bugreport.
mUiBot.chooseActivity(UI_NAME);
Bundle extras = mListener.getExtras();
- assertActionSendMultiple(extras, BUGREPORT_CONTENT, null);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
// Make sure it's hidden now.
int newState = BugreportPrefs.getWarningState(mContext, BugreportPrefs.STATE_UNKNOWN);
@@ -314,13 +361,13 @@
}
public void testBugreportFinished_plainBugreportAndNoScreenshot() throws Exception {
- Bundle extras = sendBugreportFinishedAndGetSharedIntent(mPlainTextPath, null);
- assertActionSendMultiple(extras, BUGREPORT_CONTENT, null);
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(mPlainTextPath, NO_SCREENSHOT);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
}
public void testBugreportFinished_zippedBugreportAndNoScreenshot() throws Exception {
- Bundle extras = sendBugreportFinishedAndGetSharedIntent(mZipPath, null);
- assertActionSendMultiple(extras, BUGREPORT_CONTENT, null);
+ Bundle extras = sendBugreportFinishedAndGetSharedIntent(mZipPath, NO_SCREENSHOT);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
}
private void cancelExistingNotifications() {
@@ -426,8 +473,8 @@
*/
private void assertActionSendMultiple(Bundle extras, String bugreportContent,
String screenshotContent) throws IOException {
- assertActionSendMultiple(extras, bugreportContent, screenshotContent, PID, null, ZIP_FILE,
- null, 0, false);
+ assertActionSendMultiple(extras, bugreportContent, screenshotContent, PID, ZIP_FILE,
+ NO_NAME, NO_TITLE, NO_DESCRIPTION, 0, false);
}
/**
@@ -437,14 +484,16 @@
* @param bugreportContent expected content in the bugreport file
* @param screenshotContent expected content in the screenshot file (sent by dumpstate), if any
* @param pid emulated dumpstate pid
- * @param name bugreport name as provided by the user
- * @param title bugreport name as provided by the user (or received by dumpstate)
+ * @param name expected subject
+ * @param name bugreport name as provided by the user (or received by dumpstate)
+ * @param title bugreport name as provided by the user
* @param description bugreport description as provided by the user
* @param numberScreenshots expected number of screenshots taken by Shell.
* @param renamedScreenshots whether the screenshots are expected to be renamed
*/
private void assertActionSendMultiple(Bundle extras, String bugreportContent,
- String screenshotContent, int pid, String name, String title, String description,
+ String screenshotContent, int pid, String subject,
+ String name, String title, String description,
int numberScreenshots, boolean renamedScreenshots) throws IOException {
String body = extras.getString(Intent.EXTRA_TEXT);
assertContainsRegex("missing build info",
@@ -455,7 +504,7 @@
assertContainsRegex("missing description", description, body);
}
- assertEquals("wrong subject", title, extras.getString(Intent.EXTRA_SUBJECT));
+ assertEquals("wrong subject", subject, extras.getString(Intent.EXTRA_SUBJECT));
List<Uri> attachments = extras.getParcelableArrayList(Intent.EXTRA_STREAM);
int expectedNumberScreenshots = numberScreenshots;
@@ -478,6 +527,12 @@
}
assertNotNull("did not get .zip attachment", zipUri);
assertZipContent(zipUri, BUGREPORT_FILE, BUGREPORT_CONTENT);
+ if (!TextUtils.isEmpty(title)) {
+ assertZipContent(zipUri, "title.txt", title);
+ }
+ if (!TextUtils.isEmpty(description)) {
+ assertZipContent(zipUri, "description.txt", description);
+ }
// URI of the screenshot taken by dumpstate.
Uri externalScreenshotUri = null;
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 6a10c2c..bc18221 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -10,6 +10,7 @@
public void setGlowScale(float);
}
+-keep class com.android.systemui.statusbar.car.CarStatusBar
-keep class com.android.systemui.statusbar.phone.PhoneStatusBar
-keep class com.android.systemui.statusbar.tv.TvStatusBar
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
new file mode 100644
index 0000000..f7f673d
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_navigation_bar.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:background="@drawable/system_bar_background">
+
+ <!-- phone.NavigationBarView has rot0 and rot90 but we expect the car head unit to have a fixed
+ rotation so skip this level of the heirarchy.
+ -->
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:id="@+id/nav_buttons"
+ android:animateLayoutChanges="true">
+
+ <!-- Buttons get populated here from a car_arrays.xml. -->
+ </LinearLayout>
+
+ <!-- lights out layout to match exactly -->
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:id="@+id/lights_out"
+ android:visibility="gone">
+ <!-- Must match nav_buttons. -->
+ </LinearLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
new file mode 100644
index 0000000..460433e
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
@@ -0,0 +1,24 @@
+<?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
+ -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyboard_shortcuts_wrapper"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="40dp"
+ android:focusable="true">
+</RelativeLayout>
diff --git a/packages/SystemUI/res/values/arrays_car.xml b/packages/SystemUI/res/values/arrays_car.xml
new file mode 100644
index 0000000..230479d
--- /dev/null
+++ b/packages/SystemUI/res/values/arrays_car.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+<resources>
+ <!-- These should be overriden in an overlay. The default implementation is empty.
+ There needs to be correspondence per index between these arrays, which means that if there
+ isn't a longpress action associated with a shortcut item, put in an empty item to make
+ sure everything lines up.
+ -->
+ <array name="car_shortcut_icons" />
+ <array name="car_shortcut_intent_uris" />
+ <array name="car_shortcut_longpress_intent_uris" />
+</resources>
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 c95c73b..ad1ab14 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -145,14 +145,11 @@
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
mStack = stack;
- // Disable reusing task stack views until the visibility bug is fixed. b/25998134
- if (false && launchState.launchedReuseTaskStackViews) {
+ if (launchState.launchedReuseTaskStackViews) {
if (mTaskStackView != null) {
// If onRecentsHidden is not triggered, we need to the stack view again here
mTaskStackView.reset();
mTaskStackView.setStack(stack);
- removeView(mTaskStackView);
- addView(mTaskStackView);
} else {
mTaskStackView = new TaskStackView(getContext(), stack);
addView(mTaskStackView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 5906bda..6bcf148 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
@@ -80,11 +78,8 @@
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
-import android.widget.DateTimeView;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RemoteViews;
-import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
@@ -116,6 +111,7 @@
import java.util.List;
import java.util.Locale;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
public abstract class BaseStatusBar extends SystemUI implements
@@ -126,9 +122,6 @@
public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final boolean MULTIUSER_DEBUG = false;
- // STOPSHIP disable once we resolve b/18102199
- private static final boolean NOTIFICATION_CLICK_DEBUG = true;
-
public static final boolean ENABLE_REMOTE_INPUT =
SystemProperties.getBoolean("debug.enable_remote_input", true);
public static final boolean ENABLE_CHILD_NOTIFICATIONS
@@ -141,11 +134,9 @@
protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
- protected static final int MSG_SHOW_KEYBOARD_SHORTCUTS_MENU = 1026;
+ protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
protected static final boolean ENABLE_HEADS_UP = true;
- // scores above this threshold should be displayed in heads up mode.
- protected static final int INTERRUPTION_THRESHOLD = 10;
protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
// Should match the values in PhoneWindowManager
@@ -200,14 +191,10 @@
protected IDreamManager mDreamManager;
PowerManager mPowerManager;
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- protected int mRowMinHeightLegacy;
- protected int mRowMinHeight;
- protected int mRowMaxHeight;
// public mode, private notifications, etc
private boolean mLockscreenPublicMode = false;
private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
- private NotificationColorUtil mNotificationColorUtil;
private UserManager mUserManager;
@@ -237,6 +224,8 @@
private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn;
+ private KeyboardShortcuts mKeyboardShortcuts;
+
/**
* The {@link StatusBarState} of the status bar.
*/
@@ -357,9 +346,6 @@
ViewGroup actionGroup = (ViewGroup) parent;
index = actionGroup.indexOfChild(view);
}
- if (NOTIFICATION_CLICK_DEBUG) {
- Log.d(TAG, "Clicked on button " + index + " for " + key);
- }
try {
mBarService.onNotificationActionClick(key, index);
} catch (RemoteException e) {
@@ -617,8 +603,6 @@
mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
Context.DEVICE_POLICY_SERVICE);
- mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);
-
mNotificationData = new NotificationData(this);
mAccessibilityManager = (AccessibilityManager)
@@ -1118,8 +1102,8 @@
}
@Override
- public void showKeyboardShortcutsMenu() {
- int msg = MSG_SHOW_KEYBOARD_SHORTCUTS_MENU;
+ public void toggleKeyboardShortcutsMenu() {
+ int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
mHandler.removeMessages(msg);
mHandler.sendEmptyMessage(msg);
}
@@ -1142,7 +1126,7 @@
return new H();
}
- static void sendCloseSystemWindows(Context context, String reason) {
+ protected void sendCloseSystemWindows(String reason) {
if (ActivityManagerNative.isSystemReady()) {
try {
ActivityManagerNative.getDefault().closeSystemDialogs(reason);
@@ -1177,7 +1161,7 @@
protected void showRecents(boolean triggeredFromAltTab) {
if (mRecents != null) {
- sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
+ sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
mRecents.showRecents(triggeredFromAltTab, getStatusBarView());
}
}
@@ -1200,8 +1184,8 @@
}
}
- protected void showKeyboardShortcuts() {
- Toast.makeText(mContext, "Show keyboard shortcuts screen", Toast.LENGTH_LONG).show();
+ protected void toggleKeyboardShortcuts() {
+ getKeyboardShortcuts().toggleKeyboardShortcuts(mContext);
}
protected void cancelPreloadingRecents() {
@@ -1324,8 +1308,8 @@
case MSG_SHOW_PREV_AFFILIATED_TASK:
showRecentsPreviousAffiliatedTask();
break;
- case MSG_SHOW_KEYBOARD_SHORTCUTS_MENU:
- showKeyboardShortcuts();
+ case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
+ toggleKeyboardShortcuts();
break;
}
}
@@ -1531,6 +1515,14 @@
}
}
+ protected KeyboardShortcuts getKeyboardShortcuts() {
+ if (mKeyboardShortcuts == null) {
+ mKeyboardShortcuts = new KeyboardShortcuts();
+ }
+
+ return mKeyboardShortcuts;
+ }
+
public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
if (!isDeviceProvisioned()) return;
@@ -1613,9 +1605,6 @@
}
});
- if (NOTIFICATION_CLICK_DEBUG) {
- Log.d(TAG, "Clicked on content of " + notificationKey);
- }
final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
final boolean afterKeyguardGone = intent.isActivity()
&& PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index deedae0..5a2758d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -65,7 +65,7 @@
private static final int MSG_ASSIST_DISCLOSURE = 22 << MSG_SHIFT;
private static final int MSG_START_ASSIST = 23 << MSG_SHIFT;
private static final int MSG_CAMERA_LAUNCH_GESTURE = 24 << MSG_SHIFT;
- private static final int MSG_SHOW_KEYBOARD_SHORTCUTS = 25 << MSG_SHIFT;
+ private static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS = 25 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -100,7 +100,7 @@
public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
public void toggleRecentApps();
public void preloadRecentApps();
- public void showKeyboardShortcutsMenu();
+ public void toggleKeyboardShortcutsMenu();
public void cancelPreloadRecentApps();
public void setWindowState(int window, int state);
public void buzzBeepBlinked();
@@ -229,10 +229,10 @@
}
@Override
- public void showKeyboardShortcutsMenu() {
+ public void toggleKeyboardShortcutsMenu() {
synchronized (mList) {
- mHandler.removeMessages(MSG_SHOW_KEYBOARD_SHORTCUTS);
- mHandler.obtainMessage(MSG_SHOW_KEYBOARD_SHORTCUTS).sendToTarget();
+ mHandler.removeMessages(MSG_TOGGLE_KEYBOARD_SHORTCUTS);
+ mHandler.obtainMessage(MSG_TOGGLE_KEYBOARD_SHORTCUTS).sendToTarget();
}
}
@@ -380,8 +380,8 @@
case MSG_CANCEL_PRELOAD_RECENT_APPS:
mCallbacks.cancelPreloadRecentApps();
break;
- case MSG_SHOW_KEYBOARD_SHORTCUTS:
- mCallbacks.showKeyboardShortcutsMenu();
+ case MSG_TOGGLE_KEYBOARD_SHORTCUTS:
+ mCallbacks.toggleKeyboardShortcutsMenu();
break;
case MSG_SET_WINDOW_STATE:
mCallbacks.setWindowState(msg.arg1, msg.arg2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
new file mode 100644
index 0000000..3e0ea90
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+
+/**
+ * Contains functionality for handling keyboard shortcuts.
+ */
+public class KeyboardShortcuts {
+ private Dialog mKeyboardShortcutsDialog;
+
+ public KeyboardShortcuts() {}
+
+ public void toggleKeyboardShortcuts(Context context) {
+ if (mKeyboardShortcutsDialog == null) {
+ // Create dialog.
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ final View keyboardShortcutsView = inflater.inflate(
+ R.layout.keyboard_shortcuts_view, null);
+
+ populateKeyboardShortcuts(keyboardShortcutsView.findViewById(
+ R.id.keyboard_shortcuts_wrapper));
+ dialogBuilder.setView(keyboardShortcutsView);
+ mKeyboardShortcutsDialog = dialogBuilder.create();
+ mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true);
+
+ // Setup window.
+ Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow();
+ keyboardShortcutsWindow.setType(
+ WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+ keyboardShortcutsWindow.setBackgroundDrawable(
+ new ColorDrawable(android.graphics.Color.TRANSPARENT));
+ keyboardShortcutsWindow.setGravity(Gravity.TOP);
+ mKeyboardShortcutsDialog.show();
+ } else {
+ dismissKeyboardShortcutsDialog();
+ }
+ }
+
+ public void dismissKeyboardShortcutsDialog() {
+ if (mKeyboardShortcutsDialog != null) {
+ mKeyboardShortcutsDialog.dismiss();
+ mKeyboardShortcutsDialog = null;
+ }
+ }
+
+ /**
+ * @return {@code true} if the keyboard shortcuts have been successfully populated.
+ */
+ private boolean populateKeyboardShortcuts(View keyboardShortcutsLayout) {
+ // TODO: Populate shortcuts.
+ return true;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index f243b00..d7e47c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -30,11 +30,11 @@
public class RemoteInputController {
private final ArrayList<WeakReference<NotificationData.Entry>> mRemoteInputs = new ArrayList<>();
- private final StatusBarWindowManager mStatusBarWindowManager;
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>(3);
private final HeadsUpManager mHeadsUpManager;
public RemoteInputController(StatusBarWindowManager sbwm, HeadsUpManager headsUpManager) {
- mStatusBarWindowManager = sbwm;
+ addCallback(sbwm);
mHeadsUpManager = headsUpManager;
}
@@ -59,8 +59,12 @@
}
private void apply(NotificationData.Entry entry) {
- mStatusBarWindowManager.setRemoteInputActive(isRemoteInputActive());
mHeadsUpManager.setRemoteInputActive(entry, isRemoteInputActive(entry));
+ boolean remoteInputActive = isRemoteInputActive();
+ int N = mCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ mCallbacks.get(i).onRemoteInputActive(remoteInputActive);
+ }
}
/**
@@ -99,4 +103,12 @@
}
+ public void addCallback(Callback callback) {
+ Preconditions.checkNotNull(callback);
+ mCallbacks.add(callback);
+ }
+
+ public interface Callback {
+ void onRemoteInputActive(boolean active);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
new file mode 100644
index 0000000..5c0f38c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.R.color;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView.ScaleType;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.ActivityStarter;
+import com.android.systemui.statusbar.phone.NavigationBarView;
+import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
+import com.android.systemui.statusbar.policy.KeyButtonView;
+
+import java.net.URISyntaxException;
+
+/**
+ * A custom navigation bar for the automotive use case.
+ * <p>
+ * The navigation bar in the automotive use case is more like a list of shortcuts, which we
+ * expect to be customizable by the car OEMs. This implementation populates the nav_buttons layout
+ * from resources rather than the layout file so customization would then mean updating
+ * arrays_car.xml appropriately in an overlay.
+ */
+class CarNavigationBarView extends NavigationBarView {
+ private ActivityStarter mActivityStarter;
+
+ public CarNavigationBarView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ // Read up arrays_car.xml and populate the navigation bar here.
+ Context context = getContext();
+ Resources r = getContext().getResources();
+ TypedArray icons = r.obtainTypedArray(R.array.car_shortcut_icons);
+ TypedArray intents = r.obtainTypedArray(R.array.car_shortcut_intent_uris);
+ TypedArray longpressIntents =
+ r.obtainTypedArray(R.array.car_shortcut_longpress_intent_uris);
+
+ if (icons.length() != intents.length()) {
+ throw new RuntimeException("car_shortcut_icons and car_shortcut_intents do not match");
+ }
+
+ LinearLayout navButtons = (LinearLayout) findViewById(R.id.nav_buttons);
+ LinearLayout lightsOut = (LinearLayout) findViewById(R.id.lights_out);
+
+ for (int i = 0; i < icons.length(); i++) {
+ Drawable icon = icons.getDrawable(i);
+
+ try {
+ Intent intent = Intent.parseUri(intents.getString(i), Intent.URI_INTENT_SCHEME);
+ Intent longpress = null;
+ String longpressUri = longpressIntents.getString(i);
+ if (!longpressUri.isEmpty()) {
+ longpress = Intent.parseUri(longpressUri, Intent.URI_INTENT_SCHEME);
+ }
+
+ // nav_buttons and lights_out should match exactly.
+ navButtons.addView(makeButton(context, icon, intent, longpress));
+ lightsOut.addView(makeButton(context, icon, intent, longpress));
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Malformed intent uri", e);
+ }
+ }
+ }
+
+ private ImageButton makeButton(Context context, Drawable icon,
+ final Intent intent, final Intent longpress) {
+ ImageButton button = new ImageButton(context);
+
+ button.setImageDrawable(icon);
+ button.setScaleType(ScaleType.CENTER);
+ button.setBackgroundColor(color.transparent);
+ LinearLayout.LayoutParams lp =
+ new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
+ button.setLayoutParams(lp);
+
+ button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mActivityStarter != null) {
+ mActivityStarter.startActivity(intent, true);
+ }
+ }
+ });
+
+ // Long click handlers are optional.
+ if (longpress != null) {
+ button.setLongClickable(true);
+ button.setOnLongClickListener(new OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if (mActivityStarter != null) {
+ mActivityStarter.startActivity(longpress, true);
+ return true;
+ }
+ return false;
+ }
+ });
+ } else {
+ button.setLongClickable(false);
+ }
+
+ return button;
+ }
+
+ public void setActivityStarter(ActivityStarter activityStarter) {
+ mActivityStarter = activityStarter;
+ }
+
+ @Override
+ public void setDisabledFlags(int disabledFlags, boolean force) {
+ // TODO: Populate.
+ }
+
+ @Override
+ public void reorient() {
+ // We expect the car head unit to always have a fixed rotation so we ignore this. The super
+ // class implentation expects mRotatedViews to be populated, so if you call into it, there
+ // is a possibility of a NullPointerException.
+ }
+
+ @Override
+ public View getCurrentView() {
+ return this;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
new file mode 100644
index 0000000..a72b5d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+/**
+ * A status bar (and navigation bar) tailored for the automotive use case.
+ */
+public class CarStatusBar extends PhoneStatusBar {
+ @Override
+ protected void addNavigationBar() {
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ PixelFormat.TRANSLUCENT);
+ lp.setTitle("CarNavigationBar");
+ lp.windowAnimations = 0;
+ mWindowManager.addView(mNavigationBarView, lp);
+ }
+
+ @Override
+ protected void createNavigationBarView(Context context) {
+ if (mNavigationBarView != null) {
+ return;
+ }
+
+ CarNavigationBarView carNavBar =
+ (CarNavigationBarView) View.inflate(context, R.layout.car_navigation_bar, null);
+ carNavBar.setActivityStarter(this);
+ mNavigationBarView = carNavBar;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index a3d0ce6..02812a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -157,8 +157,8 @@
final String how = "" + m.obj;
final int w = getWidth();
final int h = getHeight();
- final int vw = mCurrentView.getWidth();
- final int vh = mCurrentView.getHeight();
+ final int vw = getCurrentView().getWidth();
+ final int vh = getCurrentView().getHeight();
if (h != vh || w != vw) {
Log.w(TAG, String.format(
@@ -230,27 +230,27 @@
}
public KeyButtonView getRecentsButton() {
- return (KeyButtonView) mCurrentView.findViewById(R.id.recent_apps);
+ return (KeyButtonView) getCurrentView().findViewById(R.id.recent_apps);
}
public View getMenuButton() {
- return mCurrentView.findViewById(R.id.menu);
+ return getCurrentView().findViewById(R.id.menu);
}
public View getBackButton() {
- return mCurrentView.findViewById(R.id.back);
+ return getCurrentView().findViewById(R.id.back);
}
public KeyButtonView getHomeButton() {
- return (KeyButtonView) mCurrentView.findViewById(R.id.home);
+ return (KeyButtonView) getCurrentView().findViewById(R.id.home);
}
public View getImeSwitchButton() {
- return mCurrentView.findViewById(R.id.ime_switcher);
+ return getCurrentView().findViewById(R.id.ime_switcher);
}
public View getAppShelf() {
- return mCurrentView.findViewById(R.id.app_shelf);
+ return getCurrentView().findViewById(R.id.app_shelf);
}
private void getIcons(Resources res) {
@@ -326,7 +326,7 @@
setSlippery(disableHome && disableRecent && disableBack && disableSearch);
}
- ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
+ ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
if (navButtons != null) {
LayoutTransition lt = navButtons.getLayoutTransition();
if (lt != null) {
@@ -379,7 +379,7 @@
private void updateLayoutTransitionsEnabled() {
boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled;
- ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
+ ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
LayoutTransition lt = navButtons.getLayoutTransition();
if (lt != null) {
if (enabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 42fd872..ba20679 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1318,7 +1318,6 @@
mHeader.setExpansion(getHeaderExpansionFraction());
setQsTranslation(height);
requestScrollerTopPaddingUpdate(false /* animate */);
- updateNotificationScrim(height);
if (mKeyguardShowing) {
updateHeaderKeyguard();
}
@@ -1355,12 +1354,6 @@
}
}
- private void updateNotificationScrim(float height) {
- int startDistance = mQsMinExpansionHeight + mNotificationScrimWaitDistance;
- float progress = (height - startDistance) / (mQsMaxExpansionHeight - startDistance);
- progress = Math.max(0.0f, Math.min(progress, 1.0f));
- }
-
private float getHeaderExpansionFraction() {
if (!mKeyguardShowing) {
return getQsExpansionFraction();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index e1a400d..6aa072f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -71,7 +71,6 @@
Log.e(TAG, "setPanelHolder: null PanelHolder", new Throwable());
return;
}
- ph.setBar(this);
mPanelHolder = ph;
final int N = ph.getChildCount();
for (int i=0; i<N; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
index d7f34d5..5095ebb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
@@ -28,7 +28,6 @@
public static final boolean DEBUG_GESTURES = true;
private int mSelectedPanelIndex = -1;
- private PanelBar mBar;
public PanelHolder(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -79,8 +78,4 @@
}
return false;
}
-
- public void setBar(PanelBar panelBar) {
- mBar = panelBar;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 5e54ba7..7b2498f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -58,7 +58,6 @@
private float mPeekHeight;
private float mHintDistance;
- private int mEdgeTapAreaWidth;
private float mInitialOffsetOnTouch;
private boolean mCollapsedAndHeadsUpOnDown;
private float mExpandedFraction = 0;
@@ -202,7 +201,6 @@
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mHintDistance = res.getDimension(R.dimen.hint_move_distance);
- mEdgeTapAreaWidth = res.getDimensionPixelSize(R.dimen.edge_tap_area_width);
mUnlockFalsingThreshold = res.getDimensionPixelSize(R.dimen.unlock_falsing_threshold);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 78d09e3..d688250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -728,32 +728,7 @@
boolean showNav = mWindowManagerService.hasNavigationBar();
if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) {
- // Optionally show app shortcuts in the nav bar "shelf" area.
- if (shouldShowAppShelf()) {
- mNavigationBarView = (NavigationBarView) View.inflate(
- context, R.layout.navigation_bar_with_apps, null);
- } else {
- mNavigationBarView = (NavigationBarView) View.inflate(
- context, R.layout.navigation_bar, null);
- }
- mNavigationBarView.setDisabledFlags(mDisabled1);
- mNavigationBarView.setComponents(mRecents, getComponent(Divider.class));
- mNavigationBarView.setOnVerticalChangedListener(
- new NavigationBarView.OnVerticalChangedListener() {
- @Override
- public void onVerticalChanged(boolean isVertical) {
- if (mAssistManager != null) {
- mAssistManager.onConfigurationChanged();
- }
- mNotificationPanel.setQsScrimEnabled(!isVertical);
- }
- });
- mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- checkUserAutohide(v, event);
- return false;
- }});
+ createNavigationBarView(context);
}
} catch (RemoteException ex) {
// no window manager? good luck with that
@@ -979,6 +954,35 @@
return mStatusBarView;
}
+ protected void createNavigationBarView(Context context) {
+ // Optionally show app shortcuts in the nav bar "shelf" area.
+ if (shouldShowAppShelf()) {
+ mNavigationBarView = (NavigationBarView) View.inflate(
+ context, R.layout.navigation_bar_with_apps, null);
+ } else {
+ mNavigationBarView = (NavigationBarView) View.inflate(
+ context, R.layout.navigation_bar, null);
+ }
+ mNavigationBarView.setDisabledFlags(mDisabled1);
+ mNavigationBarView.setComponents(mRecents, getComponent(Divider.class));
+ mNavigationBarView.setOnVerticalChangedListener(
+ new NavigationBarView.OnVerticalChangedListener() {
+ @Override
+ public void onVerticalChanged(boolean isVertical) {
+ if (mAssistManager != null) {
+ mAssistManager.onConfigurationChanged();
+ }
+ mNotificationPanel.setQsScrimEnabled(!isVertical);
+ }
+ });
+ mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ checkUserAutohide(v, event);
+ return false;
+ }});
+ }
+
/** Returns true if the app shelf should be shown in the nav bar. */
private boolean shouldShowAppShelf() {
// Allow adb to override the default shelf behavior:
@@ -1086,6 +1090,7 @@
mKeyguardIndicationController.setStatusBarKeyguardViewManager(
mStatusBarKeyguardViewManager);
mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+ mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
}
@@ -1191,7 +1196,7 @@
}
// For small-screen devices (read: phones) that lack hardware navigation buttons
- private void addNavigationBar() {
+ protected void addNavigationBar() {
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
if (mNavigationBarView == null) return;
@@ -2987,6 +2992,7 @@
if (DEBUG) Log.v(TAG, "onReceive: " + intent);
String action = intent.getAction();
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+ getKeyboardShortcuts().dismissKeyboardShortcutsDialog();
if (isCurrentProfile(getSendingUserId())) {
int flags = CommandQueue.FLAG_EXCLUDE_NONE;
String reason = intent.getStringExtra("reason");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index c0887ca..ab37e6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -51,7 +51,6 @@
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
- Resources res = getContext().getResources();
mBarTransitions = new PhoneStatusBarTransitions(this);
}
@@ -102,7 +101,6 @@
@Override
public PanelView selectPanelForTouch(MotionEvent touch) {
- // No double swiping. If either panel is open, nothing else can be pulled down.
return mNotificationPanel.getExpandedHeight() > 0
? null
: mNotificationPanel;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 05f6e57..f14f0d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -31,6 +31,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.RemoteInputController;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
@@ -40,7 +41,7 @@
* which is in turn, reported to this class by the current
* {@link com.android.keyguard.KeyguardViewBase}.
*/
-public class StatusBarKeyguardViewManager {
+public class StatusBarKeyguardViewManager implements RemoteInputController.Callback {
// When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
private static final long HIDE_TIMING_CORRECTION_MS = -3 * 16;
@@ -69,12 +70,15 @@
private KeyguardBouncer mBouncer;
private boolean mShowing;
private boolean mOccluded;
+ private boolean mRemoteInputActive;
private boolean mFirstUpdate = true;
private boolean mLastShowing;
private boolean mLastOccluded;
private boolean mLastBouncerShowing;
private boolean mLastBouncerDismissible;
+ private boolean mLastRemoteInputActive;
+
private OnDismissAction mAfterKeyguardGoneAction;
private boolean mDeviceWillWakeUp;
private boolean mDeferScrimFadeOut;
@@ -199,6 +203,12 @@
mPhoneStatusBar.onScreenTurnedOn();
}
+ @Override
+ public void onRemoteInputActive(boolean active) {
+ mRemoteInputActive = active;
+ updateStates();
+ }
+
public void onScreenTurnedOff() {
mScreenTurnedOn = false;
}
@@ -429,18 +439,21 @@
boolean occluded = mOccluded;
boolean bouncerShowing = mBouncer.isShowing();
boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
+ boolean remoteInputActive = mRemoteInputActive;
- if ((bouncerDismissible || !showing) != (mLastBouncerDismissible || !mLastShowing)
+ if ((bouncerDismissible || !showing || remoteInputActive) !=
+ (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
|| mFirstUpdate) {
- if (bouncerDismissible || !showing) {
+ if (bouncerDismissible || !showing || remoteInputActive) {
mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK);
} else {
mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
}
}
- boolean navBarVisible = (!(showing && !occluded) || bouncerShowing);
- boolean lastNavBarVisible = (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing);
+ boolean navBarVisible = (!(showing && !occluded) || bouncerShowing || remoteInputActive);
+ boolean lastNavBarVisible = (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing
+ || mLastRemoteInputActive);
if (navBarVisible != lastNavBarVisible || mFirstUpdate) {
if (mPhoneStatusBar.getNavigationBarView() != null) {
if (navBarVisible) {
@@ -477,6 +490,7 @@
mLastOccluded = occluded;
mLastBouncerShowing = bouncerShowing;
mLastBouncerDismissible = bouncerDismissible;
+ mLastRemoteInputActive = remoteInputActive;
mPhoneStatusBar.onKeyguardViewManagerStatesUpdated();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index abe51ac..9d2f0de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -30,6 +30,7 @@
import com.android.keyguard.R;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import java.io.FileDescriptor;
@@ -39,7 +40,7 @@
/**
* Encapsulates all logic for the status bar window state management.
*/
-public class StatusBarWindowManager {
+public class StatusBarWindowManager implements RemoteInputController.Callback {
private final Context mContext;
private final WindowManager mWindowManager;
@@ -292,7 +293,8 @@
apply(mCurrentState);
}
- public void setRemoteInputActive(boolean remoteInputActive) {
+ @Override
+ public void onRemoteInputActive(boolean remoteInputActive) {
mCurrentState.remoteInputActive = remoteInputActive;
apply(mCurrentState);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index d5c3113..745f476 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -767,7 +767,7 @@
synchronized (mLock) {
// If we don't have a media button receiver to fall back on
// include non-playing sessions for dispatching
- UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser());
+ UserRecord ur = mUserRecords.get(mCurrentUserId);
boolean useNotPlayingSessions = (ur == null) ||
(ur.mLastMediaButtonReceiver == null
&& ur.mRestoredMediaButtonReceiver == null);
@@ -949,8 +949,7 @@
mKeyEventReceiver);
} else {
// Launch the last PendingIntent we had with priority
- int userId = ActivityManager.getCurrentUser();
- UserRecord user = mUserRecords.get(userId);
+ UserRecord user = mUserRecords.get(mCurrentUserId);
if (user != null && (user.mLastMediaButtonReceiver != null
|| user.mRestoredMediaButtonReceiver != null)) {
if (DEBUG) {
@@ -967,11 +966,11 @@
if (user.mLastMediaButtonReceiver != null) {
user.mLastMediaButtonReceiver.send(getContext(),
needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
- mediaButtonIntent, mKeyEventReceiver, null);
+ mediaButtonIntent, mKeyEventReceiver, mHandler);
} else {
mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
getContext().sendBroadcastAsUser(mediaButtonIntent,
- new UserHandle(userId));
+ new UserHandle(mCurrentUserId));
}
} catch (CanceledException e) {
Log.i(TAG, "Error sending key event to media button receiver "
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 72611b7..4fbc030 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2879,7 +2879,7 @@
} else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) {
if (down) {
if (repeatCount == 0) {
- showKeyboardShortcutsMenu();
+ toggleKeyboardShortcutsMenu();
}
}
} else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
@@ -3311,11 +3311,11 @@
}
}
- private void showKeyboardShortcutsMenu() {
+ private void toggleKeyboardShortcutsMenu() {
try {
IStatusBarService statusbar = getStatusBarService();
if (statusbar != null) {
- statusbar.showKeyboardShortcutsMenu();
+ statusbar.toggleKeyboardShortcutsMenu();
}
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException when showing keyboard shortcuts menu", e);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index fc27170..2d38da5 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -503,10 +503,10 @@
}
@Override
- public void showKeyboardShortcutsMenu() {
+ public void toggleKeyboardShortcutsMenu() {
if (mBar != null) {
try {
- mBar.showKeyboardShortcutsMenu();
+ mBar.toggleKeyboardShortcutsMenu();
} catch (RemoteException ex) {}
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 53edc789..dd58b3c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -129,6 +129,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
+import com.android.server.pm.UserManagerService;
import com.android.server.pm.UserRestrictionsUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -426,6 +427,7 @@
private static final String TAG_USER_RESTRICTIONS = "user-restrictions";
private static final String TAG_SHORT_SUPPORT_MESSAGE = "short-support-message";
private static final String TAG_LONG_SUPPORT_MESSAGE = "long-support-message";
+ private static final String TAG_PARENT_ADMIN = "parent-admin";
final DeviceAdminInfo info;
@@ -478,6 +480,9 @@
boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
boolean requireAutoTime = false; // Can only be set by a device owner.
+ ActiveAdmin parentAdmin;
+ final boolean isParent;
+
static class TrustAgentInfo {
public PersistableBundle options;
TrustAgentInfo(PersistableBundle bundle) {
@@ -515,8 +520,16 @@
String shortSupportMessage = null;
String longSupportMessage = null;
- ActiveAdmin(DeviceAdminInfo _info) {
+ ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
info = _info;
+ isParent = parent;
+ }
+
+ ActiveAdmin getParentActiveAdmin() {
+ if (parentAdmin == null && !isParent) {
+ parentAdmin = new ActiveAdmin(info, /* parent */ true);
+ }
+ return parentAdmin;
}
int getUid() { return info.getActivityInfo().applicationInfo.uid; }
@@ -704,6 +717,11 @@
out.text(longSupportMessage);
out.endTag(null, TAG_LONG_SUPPORT_MESSAGE);
}
+ if (parentAdmin != null) {
+ out.startTag(null, TAG_PARENT_ADMIN);
+ parentAdmin.writeToXml(out);
+ out.endTag(null, TAG_PARENT_ADMIN);
+ }
}
void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -831,6 +849,9 @@
} else {
Log.w(LOG_TAG, "Missing text when loading long support message");
}
+ } else if (TAG_PARENT_ADMIN.equals(tag)) {
+ parentAdmin = new ActiveAdmin(info, /* parent */ true);
+ parentAdmin.readFromXml(parser);
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -2014,7 +2035,7 @@
+ userHandle);
}
if (dai != null) {
- ActiveAdmin ap = new ActiveAdmin(dai);
+ ActiveAdmin ap = new ActiveAdmin(dai, /* parent */ false);
ap.readFromXml(parser);
policy.mAdminMap.put(ap.info.getComponent(), ap);
}
@@ -2405,7 +2426,7 @@
&& getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) {
throw new IllegalArgumentException("Admin is already added");
}
- ActiveAdmin newAdmin = new ActiveAdmin(info);
+ ActiveAdmin newAdmin = new ActiveAdmin(info, /* parent */ false);
policy.mAdminMap.put(adminReceiver, newAdmin);
int replaceIndex = -1;
final int N = policy.mAdminList.size();
@@ -2540,8 +2561,14 @@
}
}
+ private boolean isAdminApiLevelPreN(@NonNull ComponentName who, int userHandle) {
+ DeviceAdminInfo adminInfo = findAdmin(who, userHandle, false);
+ return adminInfo.getActivityInfo().applicationInfo.targetSdkVersion
+ < Build.VERSION_CODES.N;
+ }
+
@Override
- public void setPasswordQuality(ComponentName who, int quality) {
+ public void setPasswordQuality(ComponentName who, int quality, boolean parent) {
if (!mHasFeature) {
return;
}
@@ -2552,6 +2579,9 @@
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(who,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ if (parent) {
+ ap = ap.getParentActiveAdmin();
+ }
if (ap.passwordQuality != quality) {
ap.passwordQuality = quality;
saveSettingsLocked(userHandle);
@@ -2560,7 +2590,7 @@
}
@Override
- public int getPasswordQuality(ComponentName who, int userHandle) {
+ public int getPasswordQuality(ComponentName who, int userHandle, boolean parent) {
if (!mHasFeature) {
return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
@@ -2570,20 +2600,43 @@
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ if (parent && admin != null) {
+ admin = admin.getParentActiveAdmin();
+ }
return admin != null ? admin.passwordQuality : mode;
}
- // Return strictest policy for this user and profiles that are visible from this user.
- List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
- for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
+ if (LockPatternUtils.isSeparateWorkChallengeEnabled() && !parent) {
+ // If a Work Challenge is in use, only return its restrictions.
+ DevicePolicyData policy = getUserDataUnchecked(userHandle);
final int N = policy.mAdminList.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
if (mode < admin.passwordQuality) {
mode = admin.passwordQuality;
}
}
+ } else {
+ // Return strictest policy for this user and profiles that are visible from this
+ // user that do not use a separate work challenge.
+ // TODO: When there are separate parent restrictions the profile should just
+ // obey its own.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ // Only aggregate data for the parent profile plus the non-work challenge
+ // enabled profiles.
+ if (!(userInfo.isManagedProfile()
+ && LockPatternUtils.isSeparateWorkChallengeEnabled())) {
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (mode < admin.passwordQuality) {
+ mode = admin.passwordQuality;
+ }
+ }
+ }
+ }
}
return mode;
}
@@ -3143,7 +3196,7 @@
}
@Override
- public boolean isActivePasswordSufficient(int userHandle) {
+ public boolean isActivePasswordSufficient(int userHandle, boolean parent) {
if (!mHasFeature) {
return true;
}
@@ -3155,8 +3208,14 @@
// This API can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
- getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
- if (policy.mActivePasswordQuality < getPasswordQuality(null, userHandle)
+ ActiveAdmin admin =
+ getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ ComponentName adminComponentName = admin.info.getComponent();
+ // TODO: Include the Admin sdk level check in LockPatternUtils check.
+ ComponentName who = !isAdminApiLevelPreN(adminComponentName, userHandle)
+ && LockPatternUtils.isSeparateWorkChallengeEnabled()
+ ? adminComponentName : null;
+ if (policy.mActivePasswordQuality < getPasswordQuality(who, userHandle, parent)
|| policy.mActivePasswordLength < getPasswordMinimumLength(null, userHandle)) {
return false;
}
@@ -3321,7 +3380,7 @@
}
}
}
- quality = getPasswordQuality(null, userHandle);
+ quality = getPasswordQuality(null, userHandle, false);
if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
int realQuality = LockPatternUtils.computePasswordQuality(password);
if (realQuality < quality
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index d5f384d..d250739 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -24,7 +24,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.graphics.drawable.Icon;
@@ -56,7 +55,6 @@
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
-import java.util.Set;
/**
* SystemService wrapper for the PrintManager implementation. Publishes
@@ -88,11 +86,6 @@
}
class PrintManagerImpl extends IPrintManager.Stub {
- private static final char COMPONENT_NAME_SEPARATOR = ':';
-
- private static final String EXTRA_PRINT_SERVICE_COMPONENT_NAME =
- "EXTRA_PRINT_SERVICE_COMPONENT_NAME";
-
private static final int BACKGROUND_USER_ID = -10;
private final Object mLock = new Object();
@@ -487,7 +480,7 @@
private void registerContentObservers() {
final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
- Settings.Secure.ENABLED_PRINT_SERVICES);
+ Settings.Secure.DISABLED_PRINT_SERVICES);
ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
@@ -511,8 +504,7 @@
private void registerBroadcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
- @Override
- public void onPackageModified(String packageName) {
+ private void updateServices(String packageName) {
if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
synchronized (mLock) {
// A background user/profile's print jobs are running but there is
@@ -520,11 +512,15 @@
// to handle it as the change may affect ongoing print jobs.
boolean servicesChanged = false;
UserState userState = getOrCreateUserStateLocked(getChangingUserId());
- Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
- while (iterator.hasNext()) {
- ComponentName componentName = iterator.next();
- if (packageName.equals(componentName.getPackageName())) {
+
+ List<PrintServiceInfo> installedServices = userState
+ .getInstalledPrintServices();
+ final int numInstalledServices = installedServices.size();
+ for (int i = 0; i < numInstalledServices; i++) {
+ if (installedServices.get(i).getResolveInfo().serviceInfo.packageName
+ .equals(packageName)) {
servicesChanged = true;
+ break;
}
}
if (servicesChanged) {
@@ -534,30 +530,15 @@
}
@Override
+ public void onPackageModified(String packageName) {
+ updateServices(packageName);
+ getOrCreateUserStateLocked(getChangingUserId()).prunePrintServices();
+ }
+
+ @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
- // to handle it as the change may affect ongoing print jobs.
- boolean servicesRemoved = false;
- UserState userState = getOrCreateUserStateLocked(getChangingUserId());
- Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
- while (iterator.hasNext()) {
- ComponentName componentName = iterator.next();
- if (packageName.equals(componentName.getPackageName())) {
- userState.removeApprovedPrintService(componentName);
- iterator.remove();
- servicesRemoved = true;
- }
- }
- if (servicesRemoved) {
- persistComponentNamesToSettingLocked(
- Settings.Secure.ENABLED_PRINT_SERVICES,
- userState.getEnabledServices(), getChangingUserId());
- userState.updateIfNeededLocked();
- }
- }
+ updateServices(packageName);
+ getOrCreateUserStateLocked(getChangingUserId()).prunePrintServices();
}
@Override
@@ -570,10 +551,10 @@
// to handle it as the change may affect ongoing print jobs.
UserState userState = getOrCreateUserStateLocked(getChangingUserId());
boolean stoppedSomePackages = false;
- Iterator<ComponentName> iterator = userState.getEnabledServices()
+ Iterator<PrintServiceInfo> iterator = userState.getEnabledPrintServices()
.iterator();
while (iterator.hasNext()) {
- ComponentName componentName = iterator.next();
+ ComponentName componentName = iterator.next().getComponentName();
String componentPackage = componentName.getPackageName();
for (String stoppedPackage : stoppedPackages) {
if (componentPackage.equals(stoppedPackage)) {
@@ -606,48 +587,11 @@
.queryIntentServicesAsUser(intent, PackageManager.GET_SERVICES,
getChangingUserId());
- if (installedServices == null) {
- return;
- }
-
- // Enable all added services by default
- synchronized (mLock) {
+ if (installedServices != null) {
UserState userState = getOrCreateUserStateLocked(getChangingUserId());
-
- Set<ComponentName> enabledServices = userState.getEnabledServices();
- boolean servicesAdded = false;
-
- final int installedServiceCount = installedServices.size();
- for (int i = 0; i < installedServiceCount; i++) {
- ServiceInfo serviceInfo = installedServices.get(i).serviceInfo;
- ComponentName component = new ComponentName(serviceInfo.packageName,
- serviceInfo.name);
-
- enabledServices.add(component);
- servicesAdded = true;
- }
-
- if (servicesAdded) {
- persistComponentNamesToSettingLocked(
- Settings.Secure.ENABLED_PRINT_SERVICES, enabledServices,
- getChangingUserId());
- userState.updateIfNeededLocked();
- }
+ userState.updateIfNeededLocked();
}
}
-
- private void persistComponentNamesToSettingLocked(String settingName,
- Set<ComponentName> componentNames, int userId) {
- StringBuilder builder = new StringBuilder();
- for (ComponentName componentName : componentNames) {
- if (builder.length() > 0) {
- builder.append(COMPONENT_NAME_SEPARATOR);
- }
- builder.append(componentName.flattenToShortString());
- }
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
- settingName, builder.toString(), userId);
- }
};
// package changes
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index 40a8880..d179b95 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -37,17 +37,18 @@
import android.print.PrintJobId;
import android.print.PrintJobInfo;
import android.print.PrinterId;
+import android.printservice.PrintService;
import android.util.Slog;
import android.util.TimedRemoteCaller;
+import libcore.io.IoUtils;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.TimeoutException;
-import libcore.io.IoUtils;
-
/**
* This represents the remote print spooler as a local object to the
* PrintManagerService. It is responsible to connecting to the remote
@@ -439,26 +440,24 @@
}
/**
- * Connect to the print spooler service and remove an approved print service.
+ * Remove all approved {@link PrintService print services} that are not in the given set.
*
- * @param serviceToRemove The {@link ComponentName} of the service to be removed.
+ * @param servicesToKeep The {@link ComponentName names } of the services to keep
*/
- public final void removeApprovedPrintService(ComponentName serviceToRemove) {
+ public final void pruneApprovedPrintServices(List<ComponentName> servicesToKeep) {
throwIfCalledOnMainThread();
synchronized (mLock) {
throwIfDestroyedLocked();
mCanUnbind = false;
}
try {
- getRemoteInstanceLazy().removeApprovedPrintService(serviceToRemove);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error removing approved print service.", re);
- } catch (TimeoutException te) {
- Slog.e(LOG_TAG, "Error removing approved print service.", te);
+ getRemoteInstanceLazy().pruneApprovedPrintServices(servicesToKeep);
+ } catch (RemoteException|TimeoutException re) {
+ Slog.e(LOG_TAG, "Error pruning approved print services.", re);
} finally {
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
- + "] removing approved print service()");
+ + "] pruneApprovedPrintServices()");
}
synchronized (mLock) {
mCanUnbind = true;
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 63d3301..dcc02a3 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -21,11 +21,9 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
@@ -98,7 +96,7 @@
private final List<PrintServiceInfo> mInstalledServices =
new ArrayList<PrintServiceInfo>();
- private final Set<ComponentName> mEnabledServices =
+ private final Set<ComponentName> mDisabledServices =
new ArraySet<ComponentName>();
private final PrintJobForAppCache mPrintJobForAppCache =
@@ -126,8 +124,15 @@
mLock = lock;
mSpooler = new RemotePrintSpooler(context, userId, this);
mHandler = new UserStateHandler(context.getMainLooper());
+
synchronized (mLock) {
- enableSystemPrintServicesLocked();
+ readInstalledPrintServicesLocked();
+ upgradePersistentStateIfNeeded();
+ readDisabledPrintServicesLocked();
+
+ // Some print services might have gotten installed before the User State came up
+ prunePrintServices();
+
onConfigurationChangedLocked();
}
}
@@ -320,15 +325,6 @@
}
}
- /**
- * Remove an approved print service.
- *
- * @param serviceToRemove The {@link ComponentName} of the service to be removed.
- */
- public void removeApprovedPrintService(ComponentName serviceToRemove) {
- mSpooler.removeApprovedPrintService(serviceToRemove);
- }
-
public void restartPrintJob(PrintJobId printJobId, int appId) {
PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, appId);
if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
@@ -597,13 +593,6 @@
}
}
- public Set<ComponentName> getEnabledServices() {
- synchronized(mLock) {
- throwIfDestroyedLocked();
- return mEnabledServices;
- }
- }
-
public void destroyLocked() {
throwIfDestroyedLocked();
mSpooler.destroy();
@@ -612,7 +601,7 @@
}
mActiveServices.clear();
mInstalledServices.clear();
- mEnabledServices.clear();
+ mDisabledServices.clear();
if (mPrinterDiscoverySession != null) {
mPrinterDiscoverySession.destroyLocked();
mPrinterDiscoverySession = null;
@@ -646,12 +635,12 @@
.append(installedService.getAdvancedOptionsActivityName()).println();
}
- pw.append(prefix).append(tab).append("enabled services:").println();
- for (ComponentName enabledService : mEnabledServices) {
- String enabledServicePrefix = prefix + tab + tab;
- pw.append(enabledServicePrefix).append("service:").println();
- pw.append(enabledServicePrefix).append(tab).append("componentName=")
- .append(enabledService.flattenToString());
+ pw.append(prefix).append(tab).append("disabled services:").println();
+ for (ComponentName disabledService : mDisabledServices) {
+ String disabledServicePrefix = prefix + tab + tab;
+ pw.append(disabledServicePrefix).append("service:").println();
+ pw.append(disabledServicePrefix).append(tab).append("componentName=")
+ .append(disabledService.flattenToString());
pw.println();
}
@@ -679,7 +668,7 @@
private boolean readConfigurationLocked() {
boolean somethingChanged = false;
somethingChanged |= readInstalledPrintServicesLocked();
- somethingChanged |= readEnabledPrintServicesLocked();
+ somethingChanged |= readDisabledPrintServicesLocked();
return somethingChanged;
}
@@ -742,13 +731,50 @@
return false;
}
- private boolean readEnabledPrintServicesLocked() {
- Set<ComponentName> tempEnabledServiceNameSet = new HashSet<ComponentName>();
- readPrintServicesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
- tempEnabledServiceNameSet);
- if (!tempEnabledServiceNameSet.equals(mEnabledServices)) {
- mEnabledServices.clear();
- mEnabledServices.addAll(tempEnabledServiceNameSet);
+ /**
+ * Update persistent state from a previous version of Android.
+ */
+ private void upgradePersistentStateIfNeeded() {
+ String enabledSettingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ENABLED_PRINT_SERVICES, mUserId);
+
+ // Pre N we store the enabled services, in N and later we store the disabled services.
+ // Hence if enabledSettingValue is still set, we need to upgrade.
+ if (enabledSettingValue != null) {
+ Set<ComponentName> enabledServiceNameSet = new HashSet<ComponentName>();
+ readPrintServicesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
+ enabledServiceNameSet);
+
+ ArraySet<ComponentName> disabledServices = new ArraySet<>();
+ final int numInstalledServices = mInstalledServices.size();
+ for (int i = 0; i < numInstalledServices; i++) {
+ ComponentName serviceName = mInstalledServices.get(i).getComponentName();
+ if (!enabledServiceNameSet.contains(serviceName)) {
+ disabledServices.add(serviceName);
+ }
+ }
+
+ writeDisabledPrintServicesLocked(disabledServices);
+
+ // We won't needed ENABLED_PRINT_SERVICES anymore, set to null to prevent upgrade to run
+ // again.
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ENABLED_PRINT_SERVICES, null, mUserId);
+ }
+ }
+
+ /**
+ * Read the set of disabled print services from the secure settings.
+ *
+ * @return true if the state changed.
+ */
+ private boolean readDisabledPrintServicesLocked() {
+ Set<ComponentName> tempDisabledServiceNameSet = new HashSet<ComponentName>();
+ readPrintServicesFromSettingLocked(Settings.Secure.DISABLED_PRINT_SERVICES,
+ tempDisabledServiceNameSet);
+ if (!tempDisabledServiceNameSet.equals(mDisabledServices)) {
+ mDisabledServices.clear();
+ mDisabledServices.addAll(tempDisabledServiceNameSet);
return true;
}
return false;
@@ -774,70 +800,28 @@
}
}
- private void enableSystemPrintServicesLocked() {
- // Load enabled and installed services.
- readEnabledPrintServicesLocked();
- readInstalledPrintServicesLocked();
-
- // Load the system services once enabled on first boot.
- Set<ComponentName> enabledOnFirstBoot = new HashSet<ComponentName>();
- readPrintServicesFromSettingLocked(
- Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES,
- enabledOnFirstBoot);
-
+ /**
+ * Persist the disabled print services to the secure settings.
+ */
+ private void writeDisabledPrintServicesLocked(Set<ComponentName> disabledServices) {
StringBuilder builder = new StringBuilder();
-
- final int serviceCount = mInstalledServices.size();
- for (int i = 0; i < serviceCount; i++) {
- ServiceInfo serviceInfo = mInstalledServices.get(i).getResolveInfo().serviceInfo;
- // Enable system print services if we never did that and are not enabled.
- if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- ComponentName serviceName = new ComponentName(
- serviceInfo.packageName, serviceInfo.name);
- if (!mEnabledServices.contains(serviceName)
- && !enabledOnFirstBoot.contains(serviceName)) {
- if (builder.length() > 0) {
- builder.append(":");
- }
- builder.append(serviceName.flattenToString());
- }
+ for (ComponentName componentName : disabledServices) {
+ if (builder.length() > 0) {
+ builder.append(COMPONENT_NAME_SEPARATOR);
}
- }
-
- // Nothing to be enabled - done.
- if (builder.length() <= 0) {
- return;
- }
-
- String servicesToEnable = builder.toString();
-
- // Update the enabled services setting.
- String enabledServices = Settings.Secure.getStringForUser(
- mContext.getContentResolver(), Settings.Secure.ENABLED_PRINT_SERVICES, mUserId);
- if (TextUtils.isEmpty(enabledServices)) {
- enabledServices = servicesToEnable;
- } else {
- enabledServices = enabledServices + ":" + servicesToEnable;
+ builder.append(componentName.flattenToShortString());
}
Settings.Secure.putStringForUser(mContext.getContentResolver(),
- Settings.Secure.ENABLED_PRINT_SERVICES, enabledServices, mUserId);
-
- // Update the enabled on first boot services setting.
- String enabledOnFirstBootServices = Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES, mUserId);
- if (TextUtils.isEmpty(enabledOnFirstBootServices)) {
- enabledOnFirstBootServices = servicesToEnable;
- } else {
- enabledOnFirstBootServices = enabledOnFirstBootServices + ":" + enabledServices;
- }
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
- Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES,
- enabledOnFirstBootServices, mUserId);
+ Settings.Secure.DISABLED_PRINT_SERVICES, builder.toString(), mUserId);
}
- private void onConfigurationChangedLocked() {
- Set<ComponentName> installedComponents = new ArraySet<ComponentName>();
+ /**
+ * Get the {@link ComponentName names} of the installed print services
+ *
+ * @return The names of the installed print services
+ */
+ private ArrayList<ComponentName> getInstalledComponents() {
+ ArrayList<ComponentName> installedComponents = new ArrayList<ComponentName>();
final int installedCount = mInstalledServices.size();
for (int i = 0; i < installedCount; i++) {
@@ -846,8 +830,37 @@
resolveInfo.serviceInfo.name);
installedComponents.add(serviceName);
+ }
- if (mEnabledServices.contains(serviceName)) {
+ return installedComponents;
+ }
+
+ /**
+ * Prune persistent state if a print service was uninstalled
+ */
+ public void prunePrintServices() {
+ synchronized (mLock) {
+ ArrayList<ComponentName> installedComponents = getInstalledComponents();
+
+ // Remove unnecessary entries from persistent state "disabled services"
+ boolean disabledServicesUninstalled = mDisabledServices.retainAll(installedComponents);
+ if (disabledServicesUninstalled) {
+ writeDisabledPrintServicesLocked(mDisabledServices);
+ }
+
+ // Remove unnecessary entries from persistent state "approved services"
+ mSpooler.pruneApprovedPrintServices(installedComponents);
+ }
+ }
+
+ private void onConfigurationChangedLocked() {
+ ArrayList<ComponentName> installedComponents = getInstalledComponents();
+
+ final int installedCount = installedComponents.size();
+ for (int i = 0; i < installedCount; i++) {
+ ComponentName serviceName = installedComponents.get(i);
+
+ if (!mDisabledServices.contains(serviceName)) {
if (!mActiveServices.containsKey(serviceName)) {
RemotePrintService service = new RemotePrintService(
mContext, serviceName, mUserId, mSpooler, this);
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index e4738f5..5ad3379 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -1095,13 +1095,6 @@
analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
&paletteEntries, &hasTransparency, &color_type, outRows);
- // If the image is a 9-patch, we need to preserve it as a ARGB file to make
- // sure the pixels will not be pre-dithered/clamped until we decide they are
- if (imageInfo.is9Patch && (color_type == PNG_COLOR_TYPE_RGB ||
- color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)) {
- color_type = PNG_COLOR_TYPE_RGB_ALPHA;
- }
-
if (kIsDebug) {
switch (color_type) {
case PNG_COLOR_TYPE_PALETTE:
@@ -1180,18 +1173,11 @@
}
for (int i = 0; i < chunk_count; i++) {
- unknowns[i].location = PNG_HAVE_PLTE;
+ unknowns[i].location = PNG_HAVE_IHDR;
}
png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
chunk_names, chunk_count);
png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count);
-#if PNG_LIBPNG_VER < 10600
- /* Deal with unknown chunk location bug in 1.5.x and earlier */
- png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
- if (imageInfo.haveLayoutBounds) {
- png_set_unknown_chunk_location(write_ptr, write_info, 1, PNG_HAVE_PLTE);
- }
-#endif
}