Merge "Fix broken Ryu default dir test."
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e9a994e..0c0af87 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -306,7 +306,7 @@
* </thead>
*
* <tbody>
- * <tr><th colspan="3" align="left" border="0">{@link android.app.Activity#onCreate onCreate()}</th>
+ * <tr><td colspan="3" align="left" border="0">{@link android.app.Activity#onCreate onCreate()}</td>
* <td>Called when the activity is first created.
* This is where you should do all of your normal static set up:
* create views, bind data to lists, etc. This method also
@@ -318,7 +318,7 @@
* </tr>
*
* <tr><td rowspan="5" style="border-left: none; border-right: none;"> </td>
- * <th colspan="2" align="left" border="0">{@link android.app.Activity#onRestart onRestart()}</th>
+ * <td colspan="2" align="left" border="0">{@link android.app.Activity#onRestart onRestart()}</td>
* <td>Called after your activity has been stopped, prior to it being
* started again.
* <p>Always followed by <code>onStart()</code></td>
@@ -326,7 +326,7 @@
* <td align="center"><code>onStart()</code></td>
* </tr>
*
- * <tr><th colspan="2" align="left" border="0">{@link android.app.Activity#onStart onStart()}</th>
+ * <tr><td colspan="2" align="left" border="0">{@link android.app.Activity#onStart onStart()}</td>
* <td>Called when the activity is becoming visible to the user.
* <p>Followed by <code>onResume()</code> if the activity comes
* to the foreground, or <code>onStop()</code> if it becomes hidden.</td>
@@ -335,7 +335,7 @@
* </tr>
*
* <tr><td rowspan="2" style="border-left: none;"> </td>
- * <th align="left" border="0">{@link android.app.Activity#onResume onResume()}</th>
+ * <td align="left" border="0">{@link android.app.Activity#onResume onResume()}</td>
* <td>Called when the activity will start
* interacting with the user. At this point your activity is at
* the top of the activity stack, with user input going to it.
@@ -344,7 +344,7 @@
* <td align="center"><code>onPause()</code></td>
* </tr>
*
- * <tr><th align="left" border="0">{@link android.app.Activity#onPause onPause()}</th>
+ * <tr><td align="left" border="0">{@link android.app.Activity#onPause onPause()}</td>
* <td>Called when the system is about to start resuming a previous
* activity. This is typically used to commit unsaved changes to
* persistent data, stop animations and other things that may be consuming
@@ -358,7 +358,7 @@
* <code>onStop()</code></td>
* </tr>
*
- * <tr><th colspan="2" align="left" border="0">{@link android.app.Activity#onStop onStop()}</th>
+ * <tr><td colspan="2" align="left" border="0">{@link android.app.Activity#onStop onStop()}</td>
* <td>Called when the activity is no longer visible to the user, because
* another activity has been resumed and is covering this one. This
* may happen either because a new activity is being started, an existing
@@ -372,7 +372,7 @@
* <code>onDestroy()</code></td>
* </tr>
*
- * <tr><th colspan="3" align="left" border="0">{@link android.app.Activity#onDestroy onDestroy()}</th>
+ * <tr><td colspan="3" align="left" border="0">{@link android.app.Activity#onDestroy onDestroy()}</td>
* <td>The final call you receive before your
* activity is destroyed. This can happen either because the
* activity is finishing (someone called {@link Activity#finish} on
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index cf5240b..cee5646 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2371,7 +2371,8 @@
data.enforceInterface(IActivityManager.descriptor);
IUserSwitchObserver observer = IUserSwitchObserver.Stub.asInterface(
data.readStrongBinder());
- registerUserSwitchObserver(observer);
+ String name = data.readString();
+ registerUserSwitchObserver(observer, name);
reply.writeNoException();
return true;
}
@@ -6060,11 +6061,13 @@
return result;
}
- public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException {
+ public void registerUserSwitchObserver(IUserSwitchObserver observer,
+ String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+ data.writeString(name);
mRemote.transact(REGISTER_USER_SWITCH_OBSERVER_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5021a5f..cd94740 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -235,6 +235,7 @@
boolean mSystemThread = false;
boolean mJitEnabled = false;
boolean mSomeActivitiesChanged = false;
+ boolean mUpdatingSystemConfig = false;
// These can be accessed by multiple threads; mPackages is the lock.
// XXX For now we keep around information about all packages we have
@@ -1573,7 +1574,9 @@
case CONFIGURATION_CHANGED:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
+ mUpdatingSystemConfig = true;
handleConfigurationChanged((Configuration)msg.obj, null);
+ mUpdatingSystemConfig = false;
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case CLEAN_UP_CONTEXT:
@@ -4628,7 +4631,14 @@
// onConfigurationChanged
int diff = activity.mCurrentConfig.diff(newConfig);
if (diff != 0) {
- shouldChangeConfig = true;
+ // Always send the task-level config changes. For system-level configuration, if
+ // this activity doesn't handle any of the config changes, then don't bother
+ // calling onConfigurationChanged as we're going to destroy it.
+ if (!mUpdatingSystemConfig
+ || (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
+ || !reportToActivity) {
+ shouldChangeConfig = true;
+ }
}
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 5037e3e..3f7ff0b 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -512,7 +512,8 @@
public int getLaunchedFromUid(IBinder activityToken) throws RemoteException;
public String getLaunchedFromPackage(IBinder activityToken) throws RemoteException;
- public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException;
+ public void registerUserSwitchObserver(IUserSwitchObserver observer,
+ String name) throws RemoteException;
public void unregisterUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException;
public void requestBugReport(int bugreportType) throws RemoteException;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Events.java b/packages/DocumentsUI/src/com/android/documentsui/Events.java
index 27293e1..1162923 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Events.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Events.java
@@ -144,6 +144,10 @@
private static final Pools.SimplePool<MotionInputEvent> sPool = new Pools.SimplePool<>(1);
private MotionEvent mEvent;
+ interface PositionProvider {
+ int get(MotionEvent e);
+ }
+
private int mPosition;
private MotionInputEvent() {
@@ -167,6 +171,19 @@
return instance;
}
+ public static MotionInputEvent obtain(
+ MotionEvent event, PositionProvider positionProvider) {
+ Shared.checkMainLoop();
+
+ MotionInputEvent instance = sPool.acquire();
+ instance = (instance != null ? instance : new MotionInputEvent());
+
+ instance.mEvent = event;
+ instance.mPosition = positionProvider.get(event);
+
+ return instance;
+ }
+
public void recycle() {
Shared.checkMainLoop();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 00f2885..6fcf24c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -49,7 +49,6 @@
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.OnItemTouchListener;
import android.support.v7.widget.RecyclerView.RecyclerListener;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.BidiFormatter;
@@ -61,14 +60,12 @@
import android.view.DragEvent;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
@@ -143,7 +140,7 @@
private Model mModel;
private MultiSelectManager mSelectionManager;
private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
- private ItemEventListener mItemEventListener = new ItemEventListener();
+ private ItemEventListener mItemEventListener;
private SelectionModeListener mSelectionModeListener;
private FocusManager mFocusManager;
@@ -322,6 +319,13 @@
// Make sure this is done after the RecyclerView is set up.
mFocusManager = new FocusManager(context, mRecView, mModel);
+ mItemEventListener = new ItemEventListener(
+ mSelectionManager,
+ mFocusManager,
+ this::handleViewItem,
+ this::deleteDocuments,
+ this::canSelect);
+
final BaseActivity activity = getBaseActivity();
mTuner = activity.createFragmentTuner();
mMenuManager = activity.getMenuManager();
@@ -1395,99 +1399,6 @@
return mSelectionManager.getSelection().contains(modelId);
}
- private class ItemEventListener implements DocumentHolder.EventListener {
- @Override
- public boolean onActivate(DocumentHolder doc) {
- // Toggle selection if we're in selection mode, othewise, view item.
- if (mSelectionManager.hasSelection()) {
- mSelectionManager.toggleSelection(doc.modelId);
- } else {
- handleViewItem(doc.modelId);
- }
- return true;
- }
-
- @Override
- public boolean onSelect(DocumentHolder doc) {
- mSelectionManager.toggleSelection(doc.modelId);
- mSelectionManager.setSelectionRangeBegin(doc.getAdapterPosition());
- return true;
- }
-
- @Override
- public boolean onKey(DocumentHolder doc, int keyCode, KeyEvent event) {
- // Only handle key-down events. This is simpler, consistent with most other UIs, and
- // enables the handling of repeated key events from holding down a key.
- if (event.getAction() != KeyEvent.ACTION_DOWN) {
- return false;
- }
-
- // Ignore tab key events. Those should be handled by the top-level key handler.
- if (keyCode == KeyEvent.KEYCODE_TAB) {
- return false;
- }
-
- if (mFocusManager.handleKey(doc, keyCode, event)) {
- // Handle range selection adjustments. Extending the selection will adjust the
- // bounds of the in-progress range selection. Each time an unshifted navigation
- // event is received, the range selection is restarted.
- if (shouldExtendSelection(doc, event)) {
- if (!mSelectionManager.isRangeSelectionActive()) {
- // Start a range selection if one isn't active
- mSelectionManager.startRangeSelection(doc.getAdapterPosition());
- }
- mSelectionManager.snapRangeSelection(mFocusManager.getFocusPosition());
- } else {
- mSelectionManager.endRangeSelection();
- }
- return true;
- }
-
- // Handle enter key events
- switch (keyCode) {
- case KeyEvent.KEYCODE_ENTER:
- if (event.isShiftPressed()) {
- return onSelect(doc);
- }
- // For non-shifted enter keypresses, fall through.
- case KeyEvent.KEYCODE_DPAD_CENTER:
- case KeyEvent.KEYCODE_BUTTON_A:
- return onActivate(doc);
- case KeyEvent.KEYCODE_FORWARD_DEL:
- // This has to be handled here instead of in a keyboard shortcut, because
- // keyboard shortcuts all have to be modified with the 'Ctrl' key.
- if (mSelectionManager.hasSelection()) {
- Selection selection = mSelectionManager.getSelection(new Selection());
- deleteDocuments(selection);
- }
- // Always handle the key, even if there was nothing to delete. This is a
- // precaution to prevent other handlers from potentially picking up the event
- // and triggering extra behaviours.
- return true;
- }
-
- return false;
- }
-
- private boolean shouldExtendSelection(DocumentHolder doc, KeyEvent event) {
- if (!Events.isNavigationKeyCode(event.getKeyCode()) || !event.isShiftPressed()) {
- return false;
- }
-
- // TODO: Combine this method with onBeforeItemStateChange, as both of them are almost
- // the same, and responsible for the same thing (whether to select or not).
- final Cursor cursor = mModel.getItem(doc.modelId);
- if (cursor == null) {
- Log.w(TAG, "Couldn't obtain cursor for modelId: " + doc.modelId);
- return false;
- }
-
- final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
- return mTuner.canSelectType(docMimeType, docFlags);
- }
- }
-
private final class ModelUpdateListener implements Model.UpdateListener {
@Override
public void onModelUpdate(Model model) {
@@ -1594,68 +1505,26 @@
}
};
- // Previously we listened to events with one class, only to bounce them forward
- // to GestureDetector. We're still doing that here, but with a single class
- // that reduces overall complexity in our glue code.
- private static final class ListeningGestureDetector extends GestureDetector
- implements OnItemTouchListener, OnTouchListener {
+ private boolean canSelect(String modelId) {
- private DragStartHelper mDragHelper;
- private GestureListener mGestureListener;
-
- public ListeningGestureDetector(
- Context context, DragStartHelper dragHelper, GestureListener listener) {
- super(context, listener);
- mDragHelper = dragHelper;
- mGestureListener = listener;
- setOnDoubleTapListener(listener);
- }
-
- @Override
- public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
- if (e.getAction() == MotionEvent.ACTION_DOWN && Events.isMouseEvent(e)) {
- mGestureListener.setLastButtonState(e.getButtonState());
- }
-
- // Detect drag events. When a drag is detected, intercept the rest of the gesture.
- View itemView = rv.findChildViewUnder(e.getX(), e.getY());
- if (itemView != null && mDragHelper.onTouch(itemView, e)) {
- return true;
- }
- // Forward unhandled events to the GestureDetector.
- onTouchEvent(e);
-
+ // TODO: Combine this method with onBeforeItemStateChange, as both of them are almost
+ // the same, and responsible for the same thing (whether to select or not).
+ final Cursor cursor = mModel.getItem(modelId);
+ if (cursor == null) {
+ Log.w(TAG, "Couldn't obtain cursor for modelId: " + modelId);
return false;
}
- @Override
- public void onTouchEvent(RecyclerView rv, MotionEvent e) {
- View itemView = rv.findChildViewUnder(e.getX(), e.getY());
- mDragHelper.onTouch(itemView, e);
- // Note: even though this event is being handled as part of a drag gesture, continue
- // forwarding to the GestureDetector. The detector needs to see the entire cluster of
- // events in order to properly interpret gestures.
- onTouchEvent(e);
- }
-
- @Override
- public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
-
- // For mEmptyView right-click context menu
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
- return mGestureListener.onRightClick(event);
- }
- return false;
- }
+ final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ return mTuner.canSelectType(docMimeType, docFlags);
}
/**
* The gesture listener for items in the list/grid view. Interprets gestures and sends the
* events to the target DocumentHolder, whence they are routed to the appropriate listener.
*/
- private class GestureListener extends GestureDetector.SimpleOnGestureListener {
+ class GestureListener extends GestureDetector.SimpleOnGestureListener {
// From the RecyclerView, we get two events sent to
// ListeningGestureDetector#onInterceptTouchEvent on a mouse click; we first get an
// ACTION_DOWN Event for clicking on the mouse, and then an ACTION_UP event from releasing
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ItemEventListener.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ItemEventListener.java
new file mode 100644
index 0000000..cffba8d
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ItemEventListener.java
@@ -0,0 +1,132 @@
+/*
+ * 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.documentsui.dirlist;
+
+import android.view.KeyEvent;
+
+import com.android.documentsui.Events;
+import com.android.documentsui.dirlist.MultiSelectManager.Selection;
+
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * Handles click/tap/key events on individual DocumentHolders.
+ */
+class ItemEventListener implements DocumentHolder.EventListener {
+ private MultiSelectManager mSelectionManager;
+ private FocusManager mFocusManager;
+
+ private Consumer<String> mViewItemCallback;
+ private Consumer<Selection> mDeleteDocumentsCallback;
+ private Predicate<String> mCanSelectPredicate;
+
+ public ItemEventListener(
+ MultiSelectManager selectionManager,
+ FocusManager focusManager,
+ Consumer<String> viewItemCallback,
+ Consumer<Selection> deleteDocumentsCallback,
+ Predicate<String> canSelectPredicate) {
+
+ mSelectionManager = selectionManager;
+ mFocusManager = focusManager;
+ mViewItemCallback = viewItemCallback;
+ mDeleteDocumentsCallback = deleteDocumentsCallback;
+ mCanSelectPredicate = canSelectPredicate;
+ }
+
+ @Override
+ public boolean onActivate(DocumentHolder doc) {
+ // Toggle selection if we're in selection mode, othewise, view item.
+ if (mSelectionManager.hasSelection()) {
+ mSelectionManager.toggleSelection(doc.modelId);
+ } else {
+ mViewItemCallback.accept(doc.modelId);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onSelect(DocumentHolder doc) {
+ mSelectionManager.toggleSelection(doc.modelId);
+ mSelectionManager.setSelectionRangeBegin(doc.getAdapterPosition());
+ return true;
+ }
+
+ @Override
+ public boolean onKey(DocumentHolder doc, int keyCode, KeyEvent event) {
+ // Only handle key-down events. This is simpler, consistent with most other UIs, and
+ // enables the handling of repeated key events from holding down a key.
+ if (event.getAction() != KeyEvent.ACTION_DOWN) {
+ return false;
+ }
+
+ // Ignore tab key events. Those should be handled by the top-level key handler.
+ if (keyCode == KeyEvent.KEYCODE_TAB) {
+ return false;
+ }
+
+ if (mFocusManager.handleKey(doc, keyCode, event)) {
+ // Handle range selection adjustments. Extending the selection will adjust the
+ // bounds of the in-progress range selection. Each time an unshifted navigation
+ // event is received, the range selection is restarted.
+ if (shouldExtendSelection(doc, event)) {
+ if (!mSelectionManager.isRangeSelectionActive()) {
+ // Start a range selection if one isn't active
+ mSelectionManager.startRangeSelection(doc.getAdapterPosition());
+ }
+ mSelectionManager.snapRangeSelection(mFocusManager.getFocusPosition());
+ } else {
+ mSelectionManager.endRangeSelection();
+ }
+ return true;
+ }
+
+ // Handle enter key events
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_ENTER:
+ if (event.isShiftPressed()) {
+ return onSelect(doc);
+ }
+ // For non-shifted enter keypresses, fall through.
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_BUTTON_A:
+ return onActivate(doc);
+ case KeyEvent.KEYCODE_FORWARD_DEL:
+ // This has to be handled here instead of in a keyboard shortcut, because
+ // keyboard shortcuts all have to be modified with the 'Ctrl' key.
+ if (mSelectionManager.hasSelection()) {
+ Selection selection = mSelectionManager.getSelection(new Selection());
+ mDeleteDocumentsCallback.accept(selection);
+ }
+ // Always handle the key, even if there was nothing to delete. This is a
+ // precaution to prevent other handlers from potentially picking up the event
+ // and triggering extra behaviours.
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean shouldExtendSelection(DocumentHolder doc, KeyEvent event) {
+ if (!Events.isNavigationKeyCode(event.getKeyCode()) || !event.isShiftPressed()) {
+ return false;
+ }
+
+ return mCanSelectPredicate.test(doc.modelId);
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListeningGestureDetector.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListeningGestureDetector.java
new file mode 100644
index 0000000..96e15d9
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListeningGestureDetector.java
@@ -0,0 +1,86 @@
+/*
+ * 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.documentsui.dirlist;
+
+import android.content.Context;
+import android.support.v13.view.DragStartHelper;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.OnItemTouchListener;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+import com.android.documentsui.Events;
+import com.android.documentsui.dirlist.DirectoryFragment.GestureListener;
+
+// Previously we listened to events with one class, only to bounce them forward
+// to GestureDetector. We're still doing that here, but with a single class
+// that reduces overall complexity in our glue code.
+final class ListeningGestureDetector extends GestureDetector
+ implements OnItemTouchListener, OnTouchListener {
+
+ private DragStartHelper mDragHelper;
+ private GestureListener mGestureListener;
+
+ public ListeningGestureDetector(
+ Context context, DragStartHelper dragHelper, GestureListener listener) {
+ super(context, listener);
+ mDragHelper = dragHelper;
+ mGestureListener = listener;
+ setOnDoubleTapListener(listener);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
+ if (e.getAction() == MotionEvent.ACTION_DOWN && Events.isMouseEvent(e)) {
+ mGestureListener.setLastButtonState(e.getButtonState());
+ }
+
+ // Detect drag events. When a drag is detected, intercept the rest of the gesture.
+ View itemView = rv.findChildViewUnder(e.getX(), e.getY());
+ if (itemView != null && mDragHelper.onTouch(itemView, e)) {
+ return true;
+ }
+ // Forward unhandled events to the GestureDetector.
+ onTouchEvent(e);
+
+ return false;
+ }
+
+ @Override
+ public void onTouchEvent(RecyclerView rv, MotionEvent e) {
+ View itemView = rv.findChildViewUnder(e.getX(), e.getY());
+ mDragHelper.onTouch(itemView, e);
+ // Note: even though this event is being handled as part of a drag gesture, continue
+ // forwarding to the GestureDetector. The detector needs to see the entire cluster of
+ // events in order to properly interpret gestures.
+ onTouchEvent(e);
+ }
+
+ @Override
+ public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
+
+ // For mEmptyView right-click context menu
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
+ return mGestureListener.onRightClick(event);
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a8419bf..94d9550 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1040,10 +1040,9 @@
public void onForegroundProfileSwitch(int newProfileId) {
// Ignore.
}
- });
+ }, TAG);
} catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ e.rethrowAsRuntimeException();
}
IntentFilter strongAuthTimeoutFilter = new IntentFilter();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 20c0e1d..3279800 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1087,8 +1087,8 @@
pkgicon = pmUser.getDefaultActivityIcon();
}
- ((ImageView) row.findViewById(R.id.app_icon)).setImageDrawable(pkgicon);
- ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
+ ((ImageView) guts.findViewById(R.id.app_icon)).setImageDrawable(pkgicon);
+ ((TextView) guts.findViewById(R.id.pkgname)).setText(appname);
final View settingsButton = guts.findViewById(R.id.more_settings);
if (appUid >= 0) {
@@ -1104,8 +1104,9 @@
settingsButton.setVisibility(View.GONE);
}
- guts.bindImportance(pmUser, sbn, row, mNotificationData.getImportance(sbn.getKey()));
- row.findViewById(R.id.done).setOnClickListener(new View.OnClickListener() {
+ guts.bindImportance(pmUser, sbn, mNotificationData.getImportance(sbn.getKey()));
+
+ guts.findViewById(R.id.done).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// If the user has security enabled, show challenge if the setting is changed.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index f0195d4..37c7ef2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -173,7 +173,7 @@
}
void bindImportance(final PackageManager pm, final StatusBarNotification sbn,
- final ExpandableNotificationRow row, final int importance) {
+ final int importance) {
mINotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
mStartingUserImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
@@ -191,8 +191,8 @@
// unlikely.
}
- final View importanceSlider = row.findViewById(R.id.importance_slider);
- final View importanceButtons = row.findViewById(R.id.importance_buttons);
+ final View importanceSlider = findViewById(R.id.importance_slider);
+ final View importanceButtons = findViewById(R.id.importance_buttons);
if (mShowSlider) {
bindSlider(importanceSlider, systemApp);
importanceSlider.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 0563f4a5..7187ec2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -147,7 +147,7 @@
// listen for user / profile change.
try {
- ActivityManagerNative.getDefault().registerUserSwitchObserver(mUserSwitchListener);
+ ActivityManagerNative.getDefault().registerUserSwitchObserver(mUserSwitchListener, TAG);
} catch (RemoteException e) {
// Ignore
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3d9502e..9243b3f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -21378,8 +21378,8 @@
}
@Override
- public void registerUserSwitchObserver(IUserSwitchObserver observer) {
- mUserController.registerUserSwitchObserver(observer);
+ public void registerUserSwitchObserver(IUserSwitchObserver observer, String name) {
+ mUserController.registerUserSwitchObserver(observer, name);
}
@Override
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 597b499..152d34d 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -76,6 +76,7 @@
import android.os.UserManagerInternal;
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
+import android.util.ArraySet;
import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
@@ -86,6 +87,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerService;
@@ -151,9 +153,9 @@
= new RemoteCallbackList<>();
/**
- * Currently active user switch.
+ * Currently active user switch callbacks.
*/
- Object mCurUserSwitchCallback;
+ private volatile ArraySet<String> mCurWaitingUserSwitchCallbacks;
private volatile UserManagerService mUserManager;
@@ -1040,7 +1042,8 @@
void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
synchronized (mService) {
- Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
+ Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId
+ + ". Observers that didn't send results: " + mCurWaitingUserSwitchCallbacks);
sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
}
}
@@ -1049,28 +1052,34 @@
Slog.d(TAG, "Dispatch onUserSwitching oldUser #" + oldUserId + " newUser #" + newUserId);
final int observerCount = mUserSwitchObservers.beginBroadcast();
if (observerCount > 0) {
- final IRemoteCallback callback = new IRemoteCallback.Stub() {
- int mCount = 0;
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- synchronized (mService) {
- if (mCurUserSwitchCallback == this) {
- mCount++;
- if (mCount == observerCount) {
- sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
- }
- }
- }
- }
- };
+ final ArraySet<String> curWaitingUserSwitchCallbacks = new ArraySet<>();
synchronized (mService) {
uss.switching = true;
- mCurUserSwitchCallback = callback;
+ mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks;
}
for (int i = 0; i < observerCount; i++) {
try {
- mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(
- newUserId, callback);
+ // Prepend with unique prefix to guarantee that keys are unique
+ final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
+ mCurWaitingUserSwitchCallbacks.add(name);
+ final IRemoteCallback callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ synchronized (mService) {
+ // Early return if this session is no longer valid
+ if (curWaitingUserSwitchCallbacks
+ != mCurWaitingUserSwitchCallbacks) {
+ return;
+ }
+ curWaitingUserSwitchCallbacks.remove(name);
+ // Continue switching if all callbacks have been notified
+ if (curWaitingUserSwitchCallbacks.isEmpty()) {
+ sendContinueUserSwitchLocked(uss, oldUserId, newUserId);
+ }
+ }
+ }
+ };
+ mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(newUserId, callback);
} catch (RemoteException e) {
}
}
@@ -1083,7 +1092,7 @@
}
void sendContinueUserSwitchLocked(UserState uss, int oldUserId, int newUserId) {
- mCurUserSwitchCallback = null;
+ mCurWaitingUserSwitchCallbacks = null;
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(ActivityManagerService.CONTINUE_USER_SWITCH_MSG,
oldUserId, newUserId, uss));
@@ -1248,7 +1257,8 @@
? getCurrentUserIdLocked(): userId;
}
- void registerUserSwitchObserver(IUserSwitchObserver observer) {
+ void registerUserSwitchObserver(IUserSwitchObserver observer, String name) {
+ Preconditions.checkNotNull(name, "Observer name cannot be null");
if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
final String msg = "Permission Denial: registerUserSwitchObserver() from pid="
@@ -1258,8 +1268,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
- mUserSwitchObservers.register(observer);
+ mUserSwitchObservers.register(observer, name);
}
void unregisterUserSwitchObserver(IUserSwitchObserver observer) {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index cc556c7..14e4bc6 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -1013,7 +1013,7 @@
public void onForegroundProfileSwitch(int newProfileId) {
// Ignore.
}
- });
+ }, TAG);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to listen for user switching event" ,e);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8bb3f98..62cdefa 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6763,11 +6763,26 @@
}
}
+ private long getLastModifiedTime(PackageParser.Package pkg, File srcFile) {
+ if (srcFile.isDirectory()) {
+ final File baseFile = new File(pkg.baseCodePath);
+ long maxModifiedTime = baseFile.lastModified();
+ if (pkg.splitCodePaths != null) {
+ for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) {
+ final File splitFile = new File(pkg.splitCodePaths[i]);
+ maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
+ }
+ }
+ return maxModifiedTime;
+ }
+ return srcFile.lastModified();
+ }
+
private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, File srcFile,
final int policyFlags) throws PackageManagerException {
if (ps != null
&& ps.codePath.equals(srcFile)
- && ps.timeStamp == srcFile.lastModified()
+ && ps.timeStamp == getLastModifiedTime(pkg, srcFile)
&& !isCompatSignatureUpdateNeeded(pkg)
&& !isRecoverSignatureUpdateNeeded(pkg)) {
long mSigningKeySetId = ps.keySetData.getProperSigningKeySet();
@@ -8403,7 +8418,7 @@
final String pkgName = pkg.packageName;
- final long scanFileTime = scanFile.lastModified();
+ final long scanFileTime = getLastModifiedTime(pkg, scanFile);
final boolean forceDex = (scanFlags & SCAN_FORCE_DEX) != 0;
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 26d7194..a825efd 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -906,10 +906,9 @@
public void onForegroundProfileSwitch(int newProfileId) {
// Ignore.
}
- });
+ }, TAG);
} catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ e.rethrowAsRuntimeException();
}
}
diff --git a/tools/aapt2/compile/Pseudolocalizer.cpp b/tools/aapt2/compile/Pseudolocalizer.cpp
index eae52d7..767d746 100644
--- a/tools/aapt2/compile/Pseudolocalizer.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer.cpp
@@ -280,13 +280,13 @@
std::u16string chunk;
bool end = false;
chunk.append(&c, 1);
- while (!end && i < I) {
+ while (!end && i + 1 < I) {
++i;
c = s[i];
chunk.append(&c, 1);
if (isPossibleNormalPlaceholderEnd(c)) {
end = true;
- } else if (c == 't') {
+ } else if (i + 1 < I && c == 't') {
++i;
c = s[i];
chunk.append(&c, 1);
diff --git a/tools/aapt2/compile/Pseudolocalizer_test.cpp b/tools/aapt2/compile/Pseudolocalizer_test.cpp
index b0bc2c1..36c8896 100644
--- a/tools/aapt2/compile/Pseudolocalizer_test.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer_test.cpp
@@ -69,7 +69,7 @@
EXPECT_TRUE(simpleHelper("Battery %1d%%",
"[βåţţéŕý »%1d«%% one two]", Pseudolocalizer::Method::kAccent));
-
+ EXPECT_TRUE(simpleHelper("^1 %", "[^1 % one]", Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(compoundHelper("", "", "", "[]", Pseudolocalizer::Method::kAccent));
EXPECT_TRUE(compoundHelper("Hello,", " world", "",
"[Ĥéļļö, ŵöŕļð one two]", Pseudolocalizer::Method::kAccent));
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 5b2093d..45e07a7 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -191,6 +191,9 @@
// Uses library actions.
applicationAction[u"uses-library"];
+ // Meta-data.
+ applicationAction[u"meta-data"] = metaDataAction;
+
// Activity actions.
applicationAction[u"activity"].action(requiredNameIsJavaClassName);
applicationAction[u"activity"][u"intent-filter"] = intentFilterAction;