Merge "Serializing display lists"
diff --git a/api/current.txt b/api/current.txt
index 4157c6f..e9c5727 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21,6 +21,7 @@
field public static final deprecated java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE";
field public static final java.lang.String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES";
field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
+ field public static final java.lang.String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE";
field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
field public static final java.lang.String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
field public static final java.lang.String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
@@ -18114,7 +18115,7 @@
method public boolean importFile(int, java.lang.String);
method public boolean importFile(int, android.os.ParcelFileDescriptor);
method public boolean open(android.hardware.usb.UsbDeviceConnection);
- method public boolean sendObject(int, android.os.ParcelFileDescriptor);
+ method public boolean sendObject(int, int, android.os.ParcelFileDescriptor);
method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo);
}
@@ -28743,6 +28744,49 @@
package android.service.notification {
+ public class Condition implements android.os.Parcelable {
+ ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
+ method public android.service.notification.Condition copy();
+ method public int describeContents();
+ method public static boolean isValidId(android.net.Uri, java.lang.String);
+ method public static android.net.Uri.Builder newId(android.content.Context);
+ method public static java.lang.String relevanceToString(int);
+ method public static java.lang.String stateToString(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.notification.Condition> CREATOR;
+ field public static final int FLAG_RELEVANT_ALWAYS = 2; // 0x2
+ field public static final int FLAG_RELEVANT_NOW = 1; // 0x1
+ field public static final java.lang.String SCHEME = "condition";
+ field public static final int STATE_ERROR = 3; // 0x3
+ field public static final int STATE_FALSE = 0; // 0x0
+ field public static final int STATE_TRUE = 1; // 0x1
+ field public static final int STATE_UNKNOWN = 2; // 0x2
+ field public final int flags;
+ field public final int icon;
+ field public final android.net.Uri id;
+ field public final java.lang.String line1;
+ field public final java.lang.String line2;
+ field public final int state;
+ field public final java.lang.String summary;
+ }
+
+ public abstract class ConditionProviderService extends android.app.Service {
+ ctor public ConditionProviderService();
+ method public final void notifyCondition(android.service.notification.Condition);
+ method public final void notifyConditions(android.service.notification.Condition...);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onConnected();
+ method public abstract void onRequestConditions(int);
+ method public abstract void onSubscribe(android.net.Uri);
+ method public abstract void onUnsubscribe(android.net.Uri);
+ field public static final java.lang.String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
+ field public static final java.lang.String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+ field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
+ field public static final java.lang.String META_DATA_DEFAULT_CONDITION_ID = "android.service.zen.automatic.defaultConditionId";
+ field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
+ }
+
public abstract class NotificationListenerService extends android.app.Service {
ctor public NotificationListenerService();
method public final void cancelAllNotifications();
@@ -30635,6 +30679,7 @@
field public static final java.lang.String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
field public static final java.lang.String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
+ field public static final java.lang.String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
field public static final java.lang.String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final java.lang.String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
field public static final java.lang.String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
diff --git a/api/system-current.txt b/api/system-current.txt
index 33ce543..e234970 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -19627,7 +19627,7 @@
method public boolean importFile(int, java.lang.String);
method public boolean importFile(int, android.os.ParcelFileDescriptor);
method public boolean open(android.hardware.usb.UsbDeviceConnection);
- method public boolean sendObject(int, android.os.ParcelFileDescriptor);
+ method public boolean sendObject(int, int, android.os.ParcelFileDescriptor);
method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo);
}
@@ -30868,6 +30868,11 @@
method public abstract void onRequestConditions(int);
method public abstract void onSubscribe(android.net.Uri);
method public abstract void onUnsubscribe(android.net.Uri);
+ field public static final java.lang.String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
+ field public static final java.lang.String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+ field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
+ field public static final java.lang.String META_DATA_DEFAULT_CONDITION_ID = "android.service.zen.automatic.defaultConditionId";
+ field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
@@ -32905,6 +32910,7 @@
field public static final java.lang.String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
field public static final java.lang.String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
+ field public static final java.lang.String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
field public static final java.lang.String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final java.lang.String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
field public static final java.lang.String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index f9b3419..471fa3b 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -107,7 +107,7 @@
private void runSetDeviceOwner() throws RemoteException {
ComponentName component = parseComponentName(nextArgRequired());
- mDevicePolicyManager.setActiveAdmin(component, true /*refreshing*/, UserHandle.USER_OWNER);
+ mDevicePolicyManager.setActiveAdmin(component, true /*refreshing*/, UserHandle.USER_SYSTEM);
String packageName = component.getPackageName();
try {
@@ -117,7 +117,7 @@
}
} catch (Exception e) {
// Need to remove the admin that we just added.
- mDevicePolicyManager.removeActiveAdmin(component, UserHandle.USER_OWNER);
+ mDevicePolicyManager.removeActiveAdmin(component, UserHandle.USER_SYSTEM);
throw e;
}
System.out.println("Success: Device owner set to package " + packageName);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 68c11ad..3f0a444 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -988,6 +988,7 @@
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: return "ask";
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: return "always";
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER: return "never";
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK : return "always ask";
}
return "Unknown link state: " + state;
}
diff --git a/core/java/android/animation/AnimationHandler.java b/core/java/android/animation/AnimationHandler.java
new file mode 100644
index 0000000..7778c35
--- /dev/null
+++ b/core/java/android/animation/AnimationHandler.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.view.Choreographer;
+
+import java.util.ArrayList;
+
+/**
+ * This custom, static handler handles the timing pulse that is shared by all active
+ * ValueAnimators. This approach ensures that the setting of animation values will happen on the
+ * same thread that animations start on, and that all animations will share the same times for
+ * calculating their values, which makes synchronizing animations possible.
+ *
+ * The handler uses the Choreographer by default for doing periodic callbacks. A custom
+ * AnimationFrameCallbackProvider can be set on the handler to provide timing pulse that
+ * may be independent of UI frame update. This could be useful in testing.
+ *
+ * @hide
+ */
+public class AnimationHandler {
+ /**
+ * Internal per-thread collections used to avoid set collisions as animations start and end
+ * while being processed.
+ * @hide
+ */
+ private final ArrayMap<AnimationFrameCallback, Long> mDelayedCallbackStartTime =
+ new ArrayMap<>();
+ private final ArrayList<AnimationFrameCallback> mAnimationCallbacks =
+ new ArrayList<>();
+ private final ArrayList<AnimationFrameCallback> mCommitCallbacks =
+ new ArrayList<>();
+ private AnimationFrameCallbackProvider mProvider;
+
+ private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ doAnimationFrame(getProvider().getFrameTime());
+ if (mAnimationCallbacks.size() > 0) {
+ getProvider().postFrameCallback(this);
+ }
+ }
+ };
+
+ public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
+ private boolean mListDirty = false;
+
+ public static AnimationHandler getInstance() {
+ if (sAnimatorHandler.get() == null) {
+ sAnimatorHandler.set(new AnimationHandler());
+ }
+ return sAnimatorHandler.get();
+ }
+
+ /**
+ * By default, the Choreographer is used to provide timing for frame callbacks. A custom
+ * provider can be used here to provide different timing pulse.
+ */
+ public void setProvider(AnimationFrameCallbackProvider provider) {
+ if (provider == null) {
+ mProvider = new MyFrameCallbackProvider();
+ } else {
+ mProvider = provider;
+ }
+ }
+
+ private AnimationFrameCallbackProvider getProvider() {
+ if (mProvider == null) {
+ mProvider = new MyFrameCallbackProvider();
+ }
+ return mProvider;
+ }
+
+ /**
+ * Register to get a callback on the next frame after the delay.
+ */
+ public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
+ if (mAnimationCallbacks.size() == 0) {
+ getProvider().postFrameCallback(mFrameCallback);
+ }
+ if (!mAnimationCallbacks.contains(callback)) {
+ mAnimationCallbacks.add(callback);
+ }
+
+ if (delay > 0) {
+ mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
+ }
+ }
+
+ /**
+ * Register to get a one shot callback for frame commit timing. Frame commit timing is the
+ * time *after* traversals are done, as opposed to the animation frame timing, which is
+ * before any traversals. This timing can be used to adjust the start time of an animation
+ * when expensive traversals create big delta between the animation frame timing and the time
+ * that animation is first shown on screen.
+ *
+ * Note this should only be called when the animation has already registered to receive
+ * animation frame callbacks. This callback will be guaranteed to happen *after* the next
+ * animation frame callback.
+ */
+ public void addOneShotCommitCallback(final AnimationFrameCallback callback) {
+ if (!mCommitCallbacks.contains(callback)) {
+ mCommitCallbacks.add(callback);
+ }
+ }
+
+ /**
+ * Removes the given callback from the list, so it will no longer be called for frame related
+ * timing.
+ */
+ public void removeCallback(AnimationFrameCallback callback) {
+ mCommitCallbacks.remove(callback);
+ mDelayedCallbackStartTime.remove(callback);
+ int id = mAnimationCallbacks.indexOf(callback);
+ if (id >= 0) {
+ mAnimationCallbacks.set(id, null);
+ mListDirty = true;
+ }
+ }
+
+ private void doAnimationFrame(long frameTime) {
+ int size = mAnimationCallbacks.size();
+ long currentTime = SystemClock.uptimeMillis();
+ for (int i = 0; i < size; i++) {
+ final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
+ if (callback == null) {
+ continue;
+ }
+ if (isCallbackDue(callback, currentTime)) {
+ callback.doAnimationFrame(frameTime);
+ if (mCommitCallbacks.contains(callback)) {
+ getProvider().postCommitCallback(new Runnable() {
+ @Override
+ public void run() {
+ commitAnimationFrame(callback, getProvider().getFrameTime());
+ }
+ });
+ }
+ }
+ }
+ cleanUpList();
+ }
+
+ private void commitAnimationFrame(AnimationFrameCallback callback, long frameTime) {
+ if (!mDelayedCallbackStartTime.containsKey(callback) &&
+ mCommitCallbacks.contains(callback)) {
+ callback.commitAnimationFrame(frameTime);
+ mCommitCallbacks.remove(callback);
+ }
+ }
+
+ /**
+ * Remove the callbacks from mDelayedCallbackStartTime once they have passed the initial delay
+ * so that they can start getting frame callbacks.
+ *
+ * @return true if they have passed the initial delay or have no delay, false otherwise.
+ */
+ private boolean isCallbackDue(AnimationFrameCallback callback, long currentTime) {
+ Long startTime = mDelayedCallbackStartTime.get(callback);
+ if (startTime == null) {
+ return true;
+ }
+ if (startTime < currentTime) {
+ mDelayedCallbackStartTime.remove(callback);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Return the number of callbacks that have registered for frame callbacks.
+ */
+ public static int getAnimationCount() {
+ AnimationHandler handler = sAnimatorHandler.get();
+ if (handler == null) {
+ return 0;
+ }
+ return handler.getCallbackSize();
+ }
+
+ public static void setFrameDelay(long delay) {
+ getInstance().getProvider().setFrameDelay(delay);
+ }
+
+ public static long getFrameDelay() {
+ return getInstance().getProvider().getFrameDelay();
+ }
+
+ void autoCancelBasedOn(ObjectAnimator objectAnimator) {
+ for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
+ AnimationFrameCallback cb = mAnimationCallbacks.get(i);
+ if (cb == null) {
+ continue;
+ }
+ if (objectAnimator.shouldAutoCancel(cb)) {
+ ((Animator) mAnimationCallbacks.get(i)).cancel();
+ }
+ }
+ }
+
+ private void cleanUpList() {
+ if (mListDirty) {
+ for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
+ if (mAnimationCallbacks.get(i) == null) {
+ mAnimationCallbacks.remove(i);
+ }
+ }
+ mListDirty = false;
+ }
+ }
+
+ private int getCallbackSize() {
+ int count = 0;
+ int size = mAnimationCallbacks.size();
+ for (int i = size - 1; i >= 0; i--) {
+ if (mAnimationCallbacks.get(i) != null) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Default provider of timing pulse that uses Choreographer for frame callbacks.
+ */
+ private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
+
+ final Choreographer mChoreographer = Choreographer.getInstance();
+
+ @Override
+ public void postFrameCallback(Choreographer.FrameCallback callback) {
+ mChoreographer.postFrameCallback(callback);
+ }
+
+ @Override
+ public void postCommitCallback(Runnable runnable) {
+ mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
+ }
+
+ @Override
+ public long getFrameTime() {
+ return mChoreographer.getFrameTime();
+ }
+
+ @Override
+ public long getFrameDelay() {
+ return Choreographer.getFrameDelay();
+ }
+
+ @Override
+ public void setFrameDelay(long delay) {
+ Choreographer.setFrameDelay(delay);
+ }
+ }
+
+ /**
+ * Callbacks that receives notifications for animation timing and frame commit timing.
+ */
+ interface AnimationFrameCallback {
+ /**
+ * Run animation based on the frame time.
+ * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time
+ * base.
+ */
+ void doAnimationFrame(long frameTime);
+
+ /**
+ * This notifies the callback of frame commit time. Frame commit time is the time after
+ * traversals happen, as opposed to the normal animation frame time that is before
+ * traversals. This is used to compensate expensive traversals that happen as the
+ * animation starts. When traversals take a long time to complete, the rendering of the
+ * initial frame will be delayed (by a long time). But since the startTime of the
+ * animation is set before the traversal, by the time of next frame, a lot of time would
+ * have passed since startTime was set, the animation will consequently skip a few frames
+ * to respect the new frameTime. By having the commit time, we can adjust the start time to
+ * when the first frame was drawn (after any expensive traversals) so that no frames
+ * will be skipped.
+ *
+ * @param frameTime The frame time after traversals happen, if any, in the
+ * {@link SystemClock#uptimeMillis()} time base.
+ */
+ void commitAnimationFrame(long frameTime);
+ }
+
+ /**
+ * The intention for having this interface is to increase the testability of ValueAnimator.
+ * Specifically, we can have a custom implementation of the interface below and provide
+ * timing pulse without using Choreographer. That way we could use any arbitrary interval for
+ * our timing pulse in the tests.
+ *
+ * @hide
+ */
+ public interface AnimationFrameCallbackProvider {
+ void postFrameCallback(Choreographer.FrameCallback callback);
+ void postCommitCallback(Runnable runnable);
+ long getFrameTime();
+ long getFrameDelay();
+ void setFrameDelay(long delay);
+ }
+}
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index f9333739..26c886e 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -25,6 +25,7 @@
import android.util.Property;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
/**
* This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
@@ -809,37 +810,7 @@
@Override
public void start() {
- // See if any of the current active/pending animators need to be canceled
- AnimationHandler handler = sAnimationHandler.get();
- if (handler != null) {
- int numAnims = handler.mAnimations.size();
- for (int i = numAnims - 1; i >= 0; i--) {
- if (handler.mAnimations.get(i) instanceof ObjectAnimator) {
- ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);
- if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
- anim.cancel();
- }
- }
- }
- numAnims = handler.mPendingAnimations.size();
- for (int i = numAnims - 1; i >= 0; i--) {
- if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {
- ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);
- if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
- anim.cancel();
- }
- }
- }
- numAnims = handler.mDelayedAnims.size();
- for (int i = numAnims - 1; i >= 0; i--) {
- if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {
- ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);
- if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
- anim.cancel();
- }
- }
- }
- }
+ AnimationHandler.getInstance().autoCancelBasedOn(this);
if (DBG) {
Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
for (int i = 0; i < mValues.length; ++i) {
@@ -852,6 +823,20 @@
super.start();
}
+ boolean shouldAutoCancel(AnimationHandler.AnimationFrameCallback anim) {
+ if (anim == null) {
+ return false;
+ }
+
+ if (anim instanceof ObjectAnimator) {
+ ObjectAnimator objAnim = (ObjectAnimator) anim;
+ if (objAnim.mAutoCancel && hasSameTargetAndProperties(objAnim)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* This function is called immediately before processing the first animation
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
diff --git a/core/java/android/animation/TimeAnimator.java b/core/java/android/animation/TimeAnimator.java
index 1ba68df..113a21f 100644
--- a/core/java/android/animation/TimeAnimator.java
+++ b/core/java/android/animation/TimeAnimator.java
@@ -37,7 +37,7 @@
}
@Override
- boolean animationFrame(long currentTime) {
+ boolean animateBasedOnTime(long currentTime) {
if (mListener != null) {
long totalTime = currentTime - mStartTime;
long deltaTime = (mPreviousTime < 0) ? 0 : (currentTime - mPreviousTime);
@@ -52,7 +52,7 @@
long currentTime = AnimationUtils.currentAnimationTimeMillis();
mStartTime = Math.max(mStartTime, currentTime - playTime);
mStartTimeCommitted = true; // do not allow start time to be compensated for jank
- animationFrame(currentTime);
+ animateBasedOnTime(currentTime);
}
/**
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 35a8816..da9a8c8 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -21,7 +21,6 @@
import android.os.Trace;
import android.util.AndroidRuntimeException;
import android.util.Log;
-import android.view.Choreographer;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
@@ -64,7 +63,7 @@
* </div>
*/
@SuppressWarnings("unchecked")
-public class ValueAnimator extends Animator {
+public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {
private static final String TAG = "ValueAnimator";
private static final boolean DEBUG = false;
@@ -74,14 +73,6 @@
private static float sDurationScale = 1.0f;
/**
- * Values used with internal variable mPlayingState to indicate the current state of an
- * animation.
- */
- static final int STOPPED = 0; // Not yet playing
- static final int RUNNING = 1; // Playing normally
- static final int SEEKED = 2; // Seeked to some time value
-
- /**
* Internal variables
* NOTE: This object implements the clone() method, making a deep copy of any referenced
* objects. As other non-trivial fields are added to this class, make sure to add logic
@@ -130,15 +121,6 @@
*/
private boolean mResumed = false;
-
- // The static sAnimationHandler processes the internal timing loop on which all animations
- // are based
- /**
- * @hide
- */
- protected static ThreadLocal<AnimationHandler> sAnimationHandler =
- new ThreadLocal<AnimationHandler>();
-
// The time interpolator to be used if none is set on the animation
private static final TimeInterpolator sDefaultInterpolator =
new AccelerateDecelerateInterpolator();
@@ -170,25 +152,9 @@
private float mCurrentFraction = 0f;
/**
- * Tracks whether a startDelay'd animation has begun playing through the startDelay.
+ * Tracks the time (in milliseconds) when the last frame arrived.
*/
- private boolean mStartedDelay = false;
-
- /**
- * Tracks the time at which the animation began playing through its startDelay. This is
- * different from the mStartTime variable, which is used to track when the animation became
- * active (which is when the startDelay expired and the animation was added to the active
- * animations list).
- */
- private long mDelayStartTime;
-
- /**
- * Flag that represents the current state of the animation. Used to figure out when to start
- * an animation (if state == STOPPED). Also used to end an animation that
- * has been cancel()'d or end()'d since the last animation frame. Possible values are
- * STOPPED, RUNNING, SEEKED.
- */
- int mPlayingState = STOPPED;
+ private long mLastFrameTime = 0;
/**
* Additional playing state to indicate whether an animator has been start()'d. There is
@@ -219,16 +185,21 @@
*/
boolean mInitialized = false;
+ /**
+ * Flag that tracks whether animation has been requested to end.
+ */
+ private boolean mAnimationEndRequested = false;
+
//
// Backing variables
//
// How long the animation should last in ms
- private long mDuration = (long)(300 * sDurationScale);
private long mUnscaledDuration = 300;
+ private long mDuration = (long)(mUnscaledDuration * sDurationScale);
// The amount of time in ms to delay starting the animation after start() is called
- private long mStartDelay = 0;
+ long mStartDelay = 0;
private long mUnscaledStartDelay = 0;
// The number of times the animation will repeat. The default is 0, which means the animation
@@ -285,7 +256,6 @@
*/
public static final int INFINITE = -1;
-
/**
* @hide
*/
@@ -539,7 +509,6 @@
}
}
-
/**
* Sets the length of the animation. The default duration is 300 milliseconds.
*
@@ -644,9 +613,8 @@
long currentTime = AnimationUtils.currentAnimationTimeMillis();
mStartTime = currentTime - seekTime;
mStartTimeCommitted = true; // do not allow start time to be compensated for jank
- if (mPlayingState != RUNNING) {
+ if (!mRunning) {
mSeekFraction = fraction;
- mPlayingState = SEEKED;
}
if (mPlayingBackwards) {
fraction = 1f - fraction;
@@ -662,168 +630,13 @@
* @return The current position in time of the animation.
*/
public long getCurrentPlayTime() {
- if (!mInitialized || mPlayingState == STOPPED) {
+ if (!mInitialized || !mStarted) {
return 0;
}
return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
}
/**
- * This custom, static handler handles the timing pulse that is shared by
- * all active animations. This approach ensures that the setting of animation
- * values will happen on the UI thread and that all animations will share
- * the same times for calculating their values, which makes synchronizing
- * animations possible.
- *
- * The handler uses the Choreographer for executing periodic callbacks.
- *
- * @hide
- */
- @SuppressWarnings("unchecked")
- protected static class AnimationHandler {
- // The per-thread list of all active animations
- /** @hide */
- protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
-
- // Used in doAnimationFrame() to avoid concurrent modifications of mAnimations
- private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
-
- // The per-thread set of animations to be started on the next animation frame
- /** @hide */
- protected final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
-
- /**
- * Internal per-thread collections used to avoid set collisions as animations start and end
- * while being processed.
- * @hide
- */
- protected final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>();
- private final ArrayList<ValueAnimator> mEndingAnims = new ArrayList<ValueAnimator>();
- private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>();
-
- private final Choreographer mChoreographer;
- private boolean mAnimationScheduled;
- private long mLastFrameTime;
-
- private AnimationHandler() {
- mChoreographer = Choreographer.getInstance();
- }
-
- /**
- * Start animating on the next frame.
- */
- public void start() {
- scheduleAnimation();
- }
-
- void doAnimationFrame(long frameTime) {
- mLastFrameTime = frameTime;
-
- // mPendingAnimations holds any animations that have requested to be started
- // We're going to clear mPendingAnimations, but starting animation may
- // cause more to be added to the pending list (for example, if one animation
- // starting triggers another starting). So we loop until mPendingAnimations
- // is empty.
- while (mPendingAnimations.size() > 0) {
- ArrayList<ValueAnimator> pendingCopy =
- (ArrayList<ValueAnimator>) mPendingAnimations.clone();
- mPendingAnimations.clear();
- int count = pendingCopy.size();
- for (int i = 0; i < count; ++i) {
- ValueAnimator anim = pendingCopy.get(i);
- // If the animation has a startDelay, place it on the delayed list
- if (anim.mStartDelay == 0) {
- anim.startAnimation(this);
- } else {
- mDelayedAnims.add(anim);
- }
- }
- }
-
- // Next, process animations currently sitting on the delayed queue, adding
- // them to the active animations if they are ready
- int numDelayedAnims = mDelayedAnims.size();
- for (int i = 0; i < numDelayedAnims; ++i) {
- ValueAnimator anim = mDelayedAnims.get(i);
- if (anim.delayedAnimationFrame(frameTime)) {
- mReadyAnims.add(anim);
- }
- }
- int numReadyAnims = mReadyAnims.size();
- if (numReadyAnims > 0) {
- for (int i = 0; i < numReadyAnims; ++i) {
- ValueAnimator anim = mReadyAnims.get(i);
- anim.startAnimation(this);
- anim.mRunning = true;
- mDelayedAnims.remove(anim);
- }
- mReadyAnims.clear();
- }
-
- // Now process all active animations. The return value from animationFrame()
- // tells the handler whether it should now be ended
- int numAnims = mAnimations.size();
- for (int i = 0; i < numAnims; ++i) {
- mTmpAnimations.add(mAnimations.get(i));
- }
- for (int i = 0; i < numAnims; ++i) {
- ValueAnimator anim = mTmpAnimations.get(i);
- if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
- mEndingAnims.add(anim);
- }
- }
- mTmpAnimations.clear();
- if (mEndingAnims.size() > 0) {
- for (int i = 0; i < mEndingAnims.size(); ++i) {
- mEndingAnims.get(i).endAnimation(this);
- }
- mEndingAnims.clear();
- }
-
- // Schedule final commit for the frame.
- mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);
-
- // If there are still active or delayed animations, schedule a future call to
- // onAnimate to process the next frame of the animations.
- if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
- scheduleAnimation();
- }
- }
-
- void commitAnimationFrame(long frameTime) {
- final long adjustment = frameTime - mLastFrameTime;
- final int numAnims = mAnimations.size();
- for (int i = 0; i < numAnims; ++i) {
- mAnimations.get(i).commitAnimationFrame(adjustment);
- }
- }
-
- private void scheduleAnimation() {
- if (!mAnimationScheduled) {
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
- mAnimationScheduled = true;
- }
- }
-
- // Called by the Choreographer.
- final Runnable mAnimate = new Runnable() {
- @Override
- public void run() {
- mAnimationScheduled = false;
- doAnimationFrame(mChoreographer.getFrameTime());
- }
- };
-
- // Called by the Choreographer.
- final Runnable mCommit = new Runnable() {
- @Override
- public void run() {
- commitAnimationFrame(mChoreographer.getFrameTime());
- }
- };
- }
-
- /**
* The amount of time, in milliseconds, to delay starting the animation after
* {@link #start()} is called.
*
@@ -859,7 +672,7 @@
* @return the requested time between frames, in milliseconds
*/
public static long getFrameDelay() {
- return Choreographer.getFrameDelay();
+ return AnimationHandler.getInstance().getFrameDelay();
}
/**
@@ -875,7 +688,7 @@
* @param frameDelay the requested time between frames, in milliseconds
*/
public static void setFrameDelay(long frameDelay) {
- Choreographer.setFrameDelay(frameDelay);
+ AnimationHandler.getInstance().setFrameDelay(frameDelay);
}
/**
@@ -1104,24 +917,13 @@
mPlayingBackwards = (mCurrentIteration % 2) != 0;
}
}
- int prevPlayingState = mPlayingState;
- mPlayingState = STOPPED;
mStarted = true;
- mStartedDelay = false;
mPaused = false;
+ mRunning = false;
+ mAnimationEndRequested = false;
updateScaledDuration(); // in case the scale factor has changed since creation time
- AnimationHandler animationHandler = getOrCreateAnimationHandler();
- animationHandler.mPendingAnimations.add(this);
- if (mStartDelay == 0) {
- // This sets the initial value of the animation, prior to actually starting it running
- if (prevPlayingState != SEEKED) {
- setCurrentPlayTime(0);
- }
- mPlayingState = STOPPED;
- mRunning = true;
- notifyStartListeners();
- }
- animationHandler.start();
+ AnimationHandler animationHandler = AnimationHandler.getInstance();
+ animationHandler.addAnimationFrameCallback(this, mStartDelay);
}
@Override
@@ -1131,41 +933,48 @@
@Override
public void cancel() {
+ if (Looper.myLooper() == null) {
+ throw new AndroidRuntimeException("Animators may only be run on Looper threads");
+ }
+
+ // If end has already been requested, through a previous end() or cancel() call, no-op
+ // until animation starts again.
+ if (mAnimationEndRequested) {
+ return;
+ }
+
// Only cancel if the animation is actually running or has been started and is about
// to run
- AnimationHandler handler = getOrCreateAnimationHandler();
- if (mPlayingState != STOPPED
- || handler.mPendingAnimations.contains(this)
- || handler.mDelayedAnims.contains(this)) {
- // Only notify listeners if the animator has actually started
- if ((mStarted || mRunning) && mListeners != null) {
- if (!mRunning) {
- // If it's not yet running, then start listeners weren't called. Call them now.
- notifyStartListeners();
- }
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
- for (AnimatorListener listener : tmpListeners) {
- listener.onAnimationCancel(this);
- }
+ // Only notify listeners if the animator has actually started
+ if ((mStarted || mRunning) && mListeners != null) {
+ if (!mRunning) {
+ // If it's not yet running, then start listeners weren't called. Call them now.
+ notifyStartListeners();
}
- endAnimation(handler);
+ ArrayList<AnimatorListener> tmpListeners =
+ (ArrayList<AnimatorListener>) mListeners.clone();
+ for (AnimatorListener listener : tmpListeners) {
+ listener.onAnimationCancel(this);
+ }
}
+ endAnimation();
+
}
@Override
public void end() {
- AnimationHandler handler = getOrCreateAnimationHandler();
- if (!handler.mAnimations.contains(this) && !handler.mPendingAnimations.contains(this)) {
+ if (Looper.myLooper() == null) {
+ throw new AndroidRuntimeException("Animators may only be run on Looper threads");
+ }
+ if (!mRunning) {
// Special case if the animation has not yet started; get it ready for ending
- mStartedDelay = false;
- startAnimation(handler);
+ startAnimation();
mStarted = true;
} else if (!mInitialized) {
initAnimation();
}
animateValue(mPlayingBackwards ? 0f : 1f);
- endAnimation(handler);
+ endAnimation();
}
@Override
@@ -1188,7 +997,7 @@
@Override
public boolean isRunning() {
- return (mPlayingState == RUNNING || mRunning);
+ return mRunning;
}
@Override
@@ -1206,7 +1015,7 @@
@Override
public void reverse() {
mPlayingBackwards = !mPlayingBackwards;
- if (mPlayingState == RUNNING) {
+ if (mRunning) {
long currentTime = AnimationUtils.currentAnimationTimeMillis();
long currentPlayTime = currentTime - mStartTime;
long timeLeft = mDuration - currentPlayTime;
@@ -1231,13 +1040,15 @@
/**
* Called internally to end an animation by removing it from the animations list. Must be
* called on the UI thread.
- * @hide
*/
- protected void endAnimation(AnimationHandler handler) {
- handler.mAnimations.remove(this);
- handler.mPendingAnimations.remove(this);
- handler.mDelayedAnims.remove(this);
- mPlayingState = STOPPED;
+ private void endAnimation() {
+ if (mAnimationEndRequested) {
+ return;
+ }
+ AnimationHandler handler = AnimationHandler.getInstance();
+ handler.removeCallback(this);
+
+ mAnimationEndRequested = true;
mPaused = false;
if ((mStarted || mRunning) && mListeners != null) {
if (!mRunning) {
@@ -1267,16 +1078,14 @@
* Called internally to start an animation by adding it to the active animations list. Must be
* called on the UI thread.
*/
- private void startAnimation(AnimationHandler handler) {
+ private void startAnimation() {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
initAnimation();
- handler.mAnimations.add(this);
- if (mStartDelay > 0 && mListeners != null) {
- // Listeners were already notified in start() if startDelay is 0; this is
- // just for delayed animations
+ mRunning = true;
+ if (mListeners != null) {
notifyStartListeners();
}
}
@@ -1288,53 +1097,16 @@
return "animator";
}
-
- /**
- * Internal function called to process an animation frame on an animation that is currently
- * sleeping through its <code>startDelay</code> phase. The return value indicates whether it
- * should be woken up and put on the active animations queue.
- *
- * @param currentTime The current animation time, used to calculate whether the animation
- * has exceeded its <code>startDelay</code> and should be started.
- * @return True if the animation's <code>startDelay</code> has been exceeded and the animation
- * should be added to the set of active animations.
- */
- private boolean delayedAnimationFrame(long currentTime) {
- if (!mStartedDelay) {
- mStartedDelay = true;
- mDelayStartTime = currentTime;
- }
- if (mPaused) {
- if (mPauseTime < 0) {
- mPauseTime = currentTime;
- }
- return false;
- } else if (mResumed) {
- mResumed = false;
- if (mPauseTime > 0) {
- // Offset by the duration that the animation was paused
- mDelayStartTime += (currentTime - mPauseTime);
- }
- }
- long deltaTime = currentTime - mDelayStartTime;
- if (deltaTime > mStartDelay) {
- // startDelay ended - start the anim and record the mStartTime appropriately
- mStartTime = mDelayStartTime + mStartDelay;
- mStartTimeCommitted = true; // do not allow start time to be compensated for jank
- mPlayingState = RUNNING;
- return true;
- }
- return false;
- }
-
/**
* Applies an adjustment to the animation to compensate for jank between when
* the animation first ran and when the frame was drawn.
+ * @hide
*/
- void commitAnimationFrame(long adjustment) {
+ public void commitAnimationFrame(long frameTime) {
if (!mStartTimeCommitted) {
mStartTimeCommitted = true;
- if (mPlayingState == RUNNING && adjustment > 0) {
+ long adjustment = frameTime - mLastFrameTime;
+ if (adjustment > 0) {
mStartTime += adjustment;
if (DEBUG) {
Log.d(TAG, "Adjusted start time by " + adjustment + " ms: " + toString());
@@ -1353,13 +1125,11 @@
*
* @param currentTime The current time, as tracked by the static timing handler
* @return true if the animation's duration, including any repetitions due to
- * <code>repeatCount</code>, has been exceeded and the animation should be ended.
+ * <code>repeatCount</code> has been exceeded and the animation should be ended.
*/
- boolean animationFrame(long currentTime) {
+ boolean animateBasedOnTime(long currentTime) {
boolean done = false;
- switch (mPlayingState) {
- case RUNNING:
- case SEEKED:
+ if (mRunning) {
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
if (mDuration == 0 && mRepeatCount != INFINITE) {
// Skip to the end
@@ -1394,9 +1164,7 @@
fraction = 1f - fraction;
}
animateValue(fraction);
- break;
}
-
return done;
}
@@ -1405,10 +1173,15 @@
*
* @param frameTime The frame time.
* @return true if the animation has ended.
+ * @hide
*/
- final boolean doAnimationFrame(long frameTime) {
- if (mPlayingState == STOPPED) {
- mPlayingState = RUNNING;
+ public final void doAnimationFrame(long frameTime) {
+ mLastFrameTime = frameTime;
+ AnimationHandler handler = AnimationHandler.getInstance();
+ if (!mRunning) {
+ // First frame
+ handler.addOneShotCommitCallback(this);
+ startAnimation();
if (mSeekFraction < 0) {
mStartTime = frameTime;
} else {
@@ -1422,7 +1195,7 @@
if (mPauseTime < 0) {
mPauseTime = frameTime;
}
- return false;
+ return;
} else if (mResumed) {
mResumed = false;
if (mPauseTime > 0) {
@@ -1430,13 +1203,18 @@
mStartTime += (frameTime - mPauseTime);
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
+ handler.addOneShotCommitCallback(this);
}
// The frame time might be before the start time during the first frame of
// an animation. The "current time" must always be on or after the start
// time to avoid animating frames at negative time intervals. In practice, this
// is very rare and only happens when seeking backwards.
final long currentTime = Math.max(frameTime, mStartTime);
- return animationFrame(currentTime);
+ boolean finished = animateBasedOnTime(currentTime);
+
+ if (finished) {
+ endAnimation();
+ }
}
/**
@@ -1488,8 +1266,6 @@
anim.mReversing = false;
anim.mCurrentIteration = 0;
anim.mInitialized = false;
- anim.mPlayingState = STOPPED;
- anim.mStartedDelay = false;
anim.mStarted = false;
anim.mRunning = false;
anim.mPaused = false;
@@ -1497,9 +1273,10 @@
anim.mStartListenersCalled = false;
anim.mStartTime = 0;
anim.mStartTimeCommitted = false;
+ anim.mAnimationEndRequested = false;
anim.mPauseTime = 0;
+ anim.mLastFrameTime = 0;
anim.mCurrentFraction = 0;
- anim.mDelayStartTime = 0;
PropertyValuesHolder[] oldValues = mValues;
if (oldValues != null) {
@@ -1540,32 +1317,7 @@
* @hide
*/
public static int getCurrentAnimationsCount() {
- AnimationHandler handler = sAnimationHandler.get();
- return handler != null ? handler.mAnimations.size() : 0;
- }
-
- /**
- * Clear all animations on this thread, without canceling or ending them.
- * This should be used with caution.
- *
- * @hide
- */
- public static void clearAllAnimations() {
- AnimationHandler handler = sAnimationHandler.get();
- if (handler != null) {
- handler.mAnimations.clear();
- handler.mPendingAnimations.clear();
- handler.mDelayedAnims.clear();
- }
- }
-
- private static AnimationHandler getOrCreateAnimationHandler() {
- AnimationHandler handler = sAnimationHandler.get();
- if (handler == null) {
- handler = new AnimationHandler();
- sAnimationHandler.set(handler);
- }
- return handler;
+ return AnimationHandler.getAnimationCount();
}
@Override
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index fe79629..412e3cd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1727,7 +1727,8 @@
String[] libDirs, int displayId, Configuration overrideConfiguration,
LoadedApk pkgInfo) {
return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
- displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo());
+ displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(),
+ pkgInfo.getClassLoader());
}
final Handler getHandler() {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 235f294..fca5567 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1838,7 +1838,7 @@
resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),
packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,
- overrideConfiguration, compatInfo);
+ overrideConfiguration, compatInfo, packageInfo.getClassLoader());
}
}
mResources = resources;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 2117597..1605b3e 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -157,7 +157,8 @@
*/
Resources getTopLevelResources(String resDir, String[] splitResDirs,
String[] overlayDirs, String[] libDirs, int displayId,
- Configuration overrideConfiguration, CompatibilityInfo compatInfo) {
+ Configuration overrideConfiguration, CompatibilityInfo compatInfo,
+ ClassLoader classLoader) {
final float scale = compatInfo.applicationScale;
Configuration overrideConfigCopy = (overrideConfiguration != null)
? new Configuration(overrideConfiguration) : null;
@@ -237,7 +238,7 @@
} else {
config = getConfiguration();
}
- r = new Resources(assets, dm, config, compatInfo);
+ r = new Resources(assets, dm, config, compatInfo, classLoader);
if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale);
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 7718a36..9ea2ba2 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -401,7 +401,19 @@
activeSet.add(canonicalJournalPath);
if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(TAG_XML_PARSER, "...automatically generated "
- + canonicalJournalPath + ". Ignore if nonexistant.");
+ + canonicalJournalPath + ". Ignore if nonexistent.");
+ }
+ }
+
+ // Special case for sharedpref files (not dirs) also add ".xml" suffix file.
+ if ("sharedpref".equals(domainFromXml) && !canonicalFile.isDirectory() &&
+ !canonicalFile.getCanonicalPath().endsWith(".xml")) {
+ final String canonicalXmlPath =
+ canonicalFile.getCanonicalPath() + ".xml";
+ activeSet.add(canonicalXmlPath);
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "...automatically generated "
+ + canonicalXmlPath + ". Ignore if nonexistent.");
}
}
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 83092a9..a59f429 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -273,10 +273,10 @@
public static final int FLAG_RESUME_WHILE_PAUSING = 0x4000;
/**
* @hide Bit in {@link #flags}: If set, this component will only be seen
- * by the primary user. Only works with broadcast receivers. Set from the
- * android.R.attr#primaryUserOnly attribute.
+ * by the system user. Only works with broadcast receivers. Set from the
+ * android.R.attr#systemUserOnly attribute.
*/
- public static final int FLAG_PRIMARY_USER_ONLY = 0x20000000;
+ public static final int FLAG_SYSTEM_USER_ONLY = 0x20000000;
/**
* Bit in {@link #flags}: If set, a single instance of the receiver will
* run for all users on the device. Set from the
diff --git a/core/java/android/content/pm/AppsQueryHelper.java b/core/java/android/content/pm/AppsQueryHelper.java
new file mode 100644
index 0000000..56b3173
--- /dev/null
+++ b/core/java/android/content/pm/AppsQueryHelper.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.pm;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class for querying installed applications using multiple criteria.
+ *
+ * @hide
+ */
+public class AppsQueryHelper {
+
+ /**
+ * Return apps without launcher icon
+ */
+ public static int GET_NON_LAUNCHABLE_APPS = 1;
+
+ /**
+ * Return apps with {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission
+ */
+ public static int GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM = 1 << 1;
+
+ private final Context mContext;
+ private List<ApplicationInfo> mAllApps;
+
+ public AppsQueryHelper(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Return a List of all packages that satisfy a specified criteria.
+ * @param flags search flags. Use any combination of {@link #GET_NON_LAUNCHABLE_APPS},
+ * {@link #GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM}
+ * @param systemAppsOnly if true, only system apps will be returned
+ * @param user user, whose apps are queried
+ */
+ public List<String> queryApps(int flags, boolean systemAppsOnly, UserHandle user) {
+ boolean nonLaunchableApps = (flags & GET_NON_LAUNCHABLE_APPS) > 0;
+ boolean interactAcrossUsers = (flags & GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM) > 0;
+ if (mAllApps == null) {
+ mAllApps = getAllApps(user.getIdentifier());
+ }
+
+ List<String> result = new ArrayList<>();
+ if (flags == 0) {
+ final int allAppsSize = mAllApps.size();
+ for (int i = 0; i < allAppsSize; i++) {
+ final ApplicationInfo appInfo = mAllApps.get(i);
+ if (systemAppsOnly && !appInfo.isSystemApp()) {
+ continue;
+ }
+ result.add(appInfo.packageName);
+ }
+ return result;
+ }
+
+ if (nonLaunchableApps) {
+ Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
+ final List<ResolveInfo> resolveInfos = queryIntentActivitiesAsUser(intent,
+ user.getIdentifier());
+
+ ArraySet<String> appsWithLaunchers = new ArraySet<>();
+ final int resolveInfosSize = resolveInfos.size();
+ for (int i = 0; i < resolveInfosSize; i++) {
+ appsWithLaunchers.add(resolveInfos.get(i).activityInfo.packageName);
+ }
+ final int allAppsSize = mAllApps.size();
+ for (int i = 0; i < allAppsSize; i++) {
+ final ApplicationInfo appInfo = mAllApps.get(i);
+ if (systemAppsOnly && !appInfo.isSystemApp()) {
+ continue;
+ }
+ final String packageName = appInfo.packageName;
+ if (!appsWithLaunchers.contains(packageName)) {
+ result.add(packageName);
+ }
+ }
+ }
+
+ if (interactAcrossUsers) {
+ final List<PackageInfo> packagesHoldingPermissions = getPackagesHoldingPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS, user.getIdentifier());
+ final int packagesHoldingPermissionsSize = packagesHoldingPermissions.size();
+ for (int i = 0; i < packagesHoldingPermissionsSize; i++) {
+ PackageInfo packageInfo = packagesHoldingPermissions.get(i);
+ if (systemAppsOnly && !packageInfo.applicationInfo.isSystemApp()) {
+ continue;
+ }
+ if (!result.contains(packageInfo.packageName)) {
+ result.add(packageInfo.packageName);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @VisibleForTesting
+ @SuppressWarnings("unchecked")
+ protected List<ApplicationInfo> getAllApps(int userId) {
+ try {
+ return AppGlobals.getPackageManager().getInstalledApplications(
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS, userId).getList();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Package manager has died", e);
+ }
+ }
+
+ @VisibleForTesting
+ protected List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int userId) {
+ return mContext.getPackageManager()
+ .queryIntentActivitiesAsUser(intent, PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_UNINSTALLED_PACKAGES, userId);
+ }
+
+ @VisibleForTesting
+ @SuppressWarnings("unchecked")
+ protected List<PackageInfo> getPackagesHoldingPermission(String perm, int userId) {
+ try {
+ return AppGlobals.getPackageManager().getPackagesHoldingPermissions(new String[]{perm},
+ 0, userId).getList();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Package manager has died", e);
+ }
+ }
+}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 502f735..968f9b2 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3138,8 +3138,8 @@
a.info.flags |= ActivityInfo.FLAG_IMMERSIVE;
}
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_primaryUserOnly, false)) {
- a.info.flags |= ActivityInfo.FLAG_PRIMARY_USER_ONLY;
+ if (sa.getBoolean(R.styleable.AndroidManifestActivity_systemUserOnly, false)) {
+ a.info.flags |= ActivityInfo.FLAG_SYSTEM_USER_ONLY;
}
if (!receiver) {
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 38f971a..2aae1de 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -129,6 +129,15 @@
}
/**
+ * Returns true if the user is a split system user.
+ * <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
+ * the method always returns false.
+ */
+ public boolean isSystemOnly() {
+ return id == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser();
+ }
+
+ /**
* @return true if this user can be switched to.
**/
public boolean supportsSwitchTo() {
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ae92108..e29bd2c 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -20,6 +20,7 @@
import android.annotation.ColorInt;
import android.annotation.StyleRes;
import android.annotation.StyleableRes;
+import android.graphics.drawable.DrawableInflater;
import android.icu.text.PluralRules;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.XmlUtils;
@@ -142,6 +143,9 @@
private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
new ConfigurationBoundResourceCache<>(this);
+ /** Used to inflate drawable objects from XML. */
+ private DrawableInflater mDrawableInflater;
+
private TypedValue mTmpValue = new TypedValue();
private boolean mPreloading;
@@ -150,6 +154,7 @@
private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[4];
final AssetManager mAssets;
+ final ClassLoader mClassLoader;
final DisplayMetrics mMetrics = new DisplayMetrics();
private final Configuration mConfiguration = new Configuration();
@@ -204,6 +209,17 @@
}
/**
+ * @return the inflater used to create drawable objects
+ * @hide Pending API finalization.
+ */
+ public final DrawableInflater getDrawableInflater() {
+ if (mDrawableInflater == null) {
+ mDrawableInflater = new DrawableInflater(this, mClassLoader);
+ }
+ return mDrawableInflater;
+ }
+
+ /**
* Used by AnimatorInflater.
*
* @hide
@@ -245,7 +261,7 @@
* selecting/computing resource values (optional).
*/
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
- this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
+ this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
}
/**
@@ -257,11 +273,15 @@
* @param config Desired device configuration to consider when
* selecting/computing resource values (optional).
* @param compatInfo this resource's compatibility info. Must not be null.
+ * @param classLoader class loader for the package used to load custom
+ * resource classes, may be {@code null} to use system
+ * class loader
* @hide
*/
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
- CompatibilityInfo compatInfo) {
+ CompatibilityInfo compatInfo, @Nullable ClassLoader classLoader) {
mAssets = assets;
+ mClassLoader = classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader;
mMetrics.setToDefaults();
if (compatInfo != null) {
mCompatibilityInfo = compatInfo;
@@ -2803,6 +2823,7 @@
private Resources() {
mAssets = AssetManager.getSystem();
+ mClassLoader = ClassLoader.getSystemClassLoader();
// NOTE: Intentionally leaving this uninitialized (all values set
// to zero), so that anyone who tries to do something that requires
// metrics will get a very wrong value.
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 4a71aa0..20b0be1 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -480,8 +480,11 @@
* the configurations in the tables below are also guaranteed for creating a reprocessable
* capture session if the camera device supports YUV reprocessing or PRIVATE reprocessing.
* However, not all output targets used to create a reprocessable session may be used in a
- * {@link CaptureRequest} simultaneously. The guaranteed output targets that can be included
- * in a {@link CaptureRequest} simultaneously are listed in the tables under
+ * {@link CaptureRequest} simultaneously. For devices that support only 1 output target in a
+ * reprocess {@link CaptureRequest}, submitting a reprocess {@link CaptureRequest} with multiple
+ * output targets will result in a {@link CaptureFailure}. For devices that support multiple
+ * output targets in a reprocess {@link CaptureRequest}, the guaranteed output targets that can
+ * be included in a {@link CaptureRequest} simultaneously are listed in the tables under
* {@link #createCaptureSession createCaptureSession}. For example, with a FULL-capability
* ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} {@code == }
* {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) device that supports PRIVATE
@@ -532,8 +535,6 @@
* <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code RECORD}</td> <td></td><td id="rb"></td> <td>High-resolution ZSL in-app video processing with regular preview.</td> </tr>
* <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Maximum-resolution ZSL in-app processing with regular preview.</td> </tr>
* <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Maximum-resolution two-input ZSL in-app processing.</td> </tr>
- * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code RECORD}</td> <td>{@code JPEG}</td><td id="rb">{@code RECORD}</td> <td>High-resolution ZSL in-app video processing and video snapshot with regular preview.</td> </tr>
- * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution two-input ZSL in-app processing with regular preview.</td> </tr>
* <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>ZSL still capture and in-app processing.</td> </tr>
* </table><br>
* </p>
@@ -711,7 +712,9 @@
* provide input images to camera device via {@link android.media.ImageWriter#queueInputImage}.
* The application must use the capture result of one of those output images to create a
* reprocess capture request so that the camera device can use the information to achieve
- * optimal reprocess image quality.
+ * optimal reprocess image quality. For camera devices that support only 1 output
+ * {@link Surface}, submitting a reprocess {@link CaptureRequest} with multiple
+ * output targets will result in a {@link CaptureFailure}.
*
* @param inputResult The capture result of the output image or one of the output images used
* to generate the reprocess input image for this capture request.
diff --git a/core/java/android/hardware/location/ActivityRecognitionHardware.java b/core/java/android/hardware/location/ActivityRecognitionHardware.java
index 5d3953a..8acd1ff 100644
--- a/core/java/android/hardware/location/ActivityRecognitionHardware.java
+++ b/core/java/android/hardware/location/ActivityRecognitionHardware.java
@@ -30,20 +30,34 @@
* @hide
*/
public class ActivityRecognitionHardware extends IActivityRecognitionHardware.Stub {
- private static final String TAG = "ActivityRecognitionHardware";
+ private static final String TAG = "ActivityRecognitionHW";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
+ private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
+ + HARDWARE_PERMISSION + "' not granted to access ActivityRecognitionHardware";
+
private static final int INVALID_ACTIVITY_TYPE = -1;
private static final int NATIVE_SUCCESS_RESULT = 0;
+ private static final int EVENT_TYPE_DISABLED = 0;
+ private static final int EVENT_TYPE_ENABLED = 1;
- private static ActivityRecognitionHardware sSingletonInstance = null;
+ /**
+ * Contains the number of supported Event Types.
+ *
+ * NOTE: increment this counter every time a new EVENT_TYPE_ is added to
+ * com.android.location.provider.ActivityRecognitionProvider
+ */
+ private static final int EVENT_TYPE_COUNT = 3;
+
+ private static ActivityRecognitionHardware sSingletonInstance;
private static final Object sSingletonInstanceLock = new Object();
private final Context mContext;
+ private final int mSupportedActivitiesCount;
private final String[] mSupportedActivities;
-
- private final RemoteCallbackList<IActivityRecognitionHardwareSink> mSinks =
- new RemoteCallbackList<IActivityRecognitionHardwareSink>();
+ private final int[][] mSupportedActivitiesEnabledEvents;
+ private final SinkList mSinks = new SinkList();
private static class Event {
public int activity;
@@ -56,6 +70,8 @@
mContext = context;
mSupportedActivities = fetchSupportedActivities();
+ mSupportedActivitiesCount = mSupportedActivities.length;
+ mSupportedActivitiesEnabledEvents = new int[mSupportedActivitiesCount][EVENT_TYPE_COUNT];
}
public static ActivityRecognitionHardware getInstance(Context context) {
@@ -107,7 +123,11 @@
}
int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs);
- return result == NATIVE_SUCCESS_RESULT;
+ if (result == NATIVE_SUCCESS_RESULT) {
+ mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_ENABLED;
+ return true;
+ }
+ return false;
}
@Override
@@ -120,7 +140,11 @@
}
int result = nativeDisableActivityEvent(activityType, eventType);
- return result == NATIVE_SUCCESS_RESULT;
+ if (result == NATIVE_SUCCESS_RESULT) {
+ mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
+ return true;
+ }
+ return false;
}
@Override
@@ -135,7 +159,7 @@
*/
private void onActivityChanged(Event[] events) {
if (events == null || events.length == 0) {
- Log.d(TAG, "No events to broadcast for onActivityChanged.");
+ if (DEBUG) Log.d(TAG, "No events to broadcast for onActivityChanged.");
return;
}
@@ -161,7 +185,6 @@
}
}
mSinks.finishBroadcast();
-
}
private String getActivityName(int activityType) {
@@ -193,10 +216,7 @@
}
private void checkPermissions() {
- String message = String.format(
- "Permission '%s' not granted to access ActivityRecognitionHardware",
- HARDWARE_PERMISSION);
- mContext.enforceCallingPermission(HARDWARE_PERMISSION, message);
+ mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
}
private String[] fetchSupportedActivities() {
@@ -208,6 +228,39 @@
return new String[0];
}
+ private class SinkList extends RemoteCallbackList<IActivityRecognitionHardwareSink> {
+ @Override
+ public void onCallbackDied(IActivityRecognitionHardwareSink callback) {
+ int callbackCount = mSinks.getRegisteredCallbackCount();
+ if (DEBUG) Log.d(TAG, "RegisteredCallbackCount: " + callbackCount);
+ if (callbackCount != 0) {
+ return;
+ }
+ // currently there is only one client for this, so if all its sinks have died, we clean
+ // up after them, this ensures that the AR HAL is not out of sink
+ for (int activity = 0; activity < mSupportedActivitiesCount; ++activity) {
+ for (int event = 0; event < EVENT_TYPE_COUNT; ++event) {
+ disableActivityEventIfEnabled(activity, event);
+ }
+ }
+ }
+
+ private void disableActivityEventIfEnabled(int activityType, int eventType) {
+ if (mSupportedActivitiesEnabledEvents[activityType][eventType] != EVENT_TYPE_ENABLED) {
+ return;
+ }
+
+ int result = nativeDisableActivityEvent(activityType, eventType);
+ mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
+ String message = String.format(
+ "DisableActivityEvent: activityType=%d, eventType=%d, result=%d",
+ activityType,
+ eventType,
+ result);
+ Log.e(TAG, message);
+ }
+ }
+
// native bindings
static { nativeClassInit(); }
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 3b3ee52..a23a6cb 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -70,6 +70,8 @@
* <li> {@link #USB_FUNCTION_MIDI} boolean extra indicating whether the
* MIDI function is enabled
* </ul>
+ * If the sticky intent has not been found, that indicates USB is disconnected,
+ * USB is not configued, MTP function is enabled, and all the other functions are disabled.
*
* {@hide}
*/
@@ -592,7 +594,7 @@
/** @hide */
public static String addFunction(String functions, String function) {
- if ("none".equals(functions)) {
+ if (USB_FUNCTION_NONE.equals(functions)) {
return function;
}
if (!containsFunction(functions, function)) {
@@ -613,7 +615,7 @@
}
}
if (split.length == 1 && split[0] == null) {
- return "none";
+ return USB_FUNCTION_NONE;
}
StringBuilder builder = new StringBuilder();
for (int i = 0; i < split.length; i++) {
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index 29daf35..ec76b8a 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -94,8 +94,9 @@
PackageManager pm = context.getPackageManager();
// Only apps installed under the primary user of the device can be scorers.
+ // TODO: http://b/23422763
List<ResolveInfo> receivers =
- pm.queryBroadcastReceivers(SCORE_INTENT, 0 /* flags */, UserHandle.USER_OWNER);
+ pm.queryBroadcastReceivers(SCORE_INTENT, 0 /* flags */, UserHandle.USER_SYSTEM);
for (ResolveInfo receiver : receivers) {
// This field is a misnomer, see android.content.pm.ResolveInfo#activityInfo
final ActivityInfo receiverInfo = receiver.activityInfo;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 80169a9..4ad9d6d 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -169,7 +169,7 @@
/**
* Current version of checkin data format.
*/
- static final String CHECKIN_VERSION = "14";
+ static final String CHECKIN_VERSION = "15";
/**
* Old version, we hit 9 and ran out of room, need to remove.
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 80bdbf1..2c7bf65 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -16,7 +16,6 @@
package android.service.notification;
-import android.annotation.SystemApi;
import android.content.Context;
import android.net.Uri;
import android.os.Parcel;
@@ -26,10 +25,7 @@
/**
* Condition information from condition providers.
- *
- * @hide
*/
-@SystemApi
public class Condition implements Parcelable {
public static final String SCHEME = "condition";
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 03ee726..2a8fb2c 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -17,9 +17,9 @@
package android.service.notification;
import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
import android.app.INotificationManager;
import android.app.Service;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -33,7 +33,10 @@
* A service that provides conditions about boolean state.
* <p>To extend this class, you must declare the service in your manifest file with
* the {@link android.Manifest.permission#BIND_CONDITION_PROVIDER_SERVICE} permission
- * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action. If you want users to be
+ * able to create and update conditions for this service to monitor, include the
+ * {@link #META_DATA_RULE_TYPE}, {@link #META_DATA_DEFAULT_CONDITION_ID}, and
+ * {@link #META_DATA_CONFIGURATION_ACTIVITY} tags. For example:</p>
* <pre>
* <service android:name=".MyConditionProvider"
* android:label="@string/service_name"
@@ -41,11 +44,21 @@
* <intent-filter>
* <action android:name="android.service.notification.ConditionProviderService" />
* </intent-filter>
+ * <meta-data
+ * android:name="android.service.zen.automatic.ruleType"
+ * android:value="@string/my_condition_rule">
+ * </meta-data>
+ * <meta-data
+ * android:name="android.service.zen.automatic.defaultConditionId"
+ * android:value="condition://com.my.package/mycondition">
+ * </meta-data>
+ * <meta-data
+ * android:name="android.service.zen.automatic.configurationActivity"
+ * android:value="com.my.package/.MyConditionConfigurationActivity">
+ * </meta-data>
* </service></pre>
*
- * @hide
*/
-@SystemApi
public abstract class ConditionProviderService extends Service {
private final String TAG = ConditionProviderService.class.getSimpleName()
+ "[" + getClass().getSimpleName() + "]";
@@ -62,9 +75,63 @@
public static final String SERVICE_INTERFACE
= "android.service.notification.ConditionProviderService";
+ /**
+ * The name of the {@code meta-data} tag containing a localized name of the type of zen rules
+ * provided by this service.
+ */
+ public static final String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
+
+ /**
+ * The name of the {@code meta-data} tag containing a default Condition {@link Uri} that can
+ * be parsed by this service.
+ */
+ public static final String META_DATA_DEFAULT_CONDITION_ID =
+ "android.service.zen.automatic.defaultConditionId";
+
+ /**
+ * The name of the {@code meta-data} tag containing the {@link ComponentName} of an activity
+ * that allows users to configure the conditions provided by this service.
+ */
+ public static final String META_DATA_CONFIGURATION_ACTIVITY =
+ "android.service.zen.automatic.configurationActivity";
+
+ /**
+ * A condition {@link Uri} extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}. If the
+ * condition Uri is modified by that activity, it must be included in the result Intent extras
+ * in order to be persisted.
+ */
+ public static final String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
+
+ /**
+ * A String rule name extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}. This extra is
+ * informative only, and will be ignored if included in the result Intent extras.
+ */
+ public static final String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+
+ /**
+ * Called when this service is connected.
+ */
abstract public void onConnected();
+
+ /**
+ * Called when the system wants to know the state of Conditions managed by this provider.
+ *
+ * Implementations should evaluate the state of all subscribed conditions, and provide updates
+ * by calling {@link #notifyCondition(Condition)} or {@link #notifyConditions(Condition...)}.
+ * @param relevance
+ */
abstract public void onRequestConditions(int relevance);
+
+ /**
+ * Called by the system when there is a new {@link Condition} to be managed by this provider.
+ * @param conditionId the Uri describing the criteria of the condition.
+ */
abstract public void onSubscribe(Uri conditionId);
+
+ /**
+ * Called by the system when a {@link Condition} has been deleted.
+ * @param conditionId the Uri describing the criteria of the deleted condition.
+ */
abstract public void onUnsubscribe(Uri conditionId);
private final INotificationManager getNotificationInterface() {
@@ -75,11 +142,19 @@
return mNoMan;
}
+ /**
+ * Informs the notification manager that the state of a Condition has changed.
+ * @param condition the condition that has changed.
+ */
public final void notifyCondition(Condition condition) {
if (condition == null) return;
notifyConditions(new Condition[]{ condition });
}
+ /**
+ * Informs the notification manager that the state of one or more Conditions has changed.
+ * @param conditions the changed conditions.
+ */
public final void notifyConditions(Condition... conditions) {
if (!isBound() || conditions == null) return;
try {
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index cf937e1..f9387b3 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1115,13 +1115,18 @@
*/
public int getOffsetForHorizontal(int line, float horiz) {
// TODO: use Paint.getOffsetForAdvance to avoid binary search
- int max = getLineEnd(line) - 1;
- int min = getLineStart(line);
+ final int lineEndOffset = getLineEnd(line);
+ final int max;
+ if (line == getLineCount() - 1) {
+ max = lineEndOffset;
+ } else {
+ max = mPaint.getTextRunCursor(mText, 0, mText.length(),
+ isRtlCharAt(lineEndOffset) ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR,
+ lineEndOffset, Paint.CURSOR_BEFORE);
+ }
+ final int min = getLineStart(line);
Directions dirs = getLineDirections(line);
- if (line == getLineCount() - 1)
- max++;
-
int best = min;
float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz);
diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java
index ce50091..ea0873d 100644
--- a/core/java/android/view/ContextThemeWrapper.java
+++ b/core/java/android/view/ContextThemeWrapper.java
@@ -19,12 +19,13 @@
import android.annotation.StyleRes;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
/**
- * A ContextWrapper that allows you to modify the theme from what is in the
- * wrapped context.
+ * A context wrapper that allows you to modify or replace the theme of the
+ * wrapped context.
*/
public class ContextThemeWrapper extends ContextWrapper {
private int mThemeResource;
@@ -33,15 +34,42 @@
private Configuration mOverrideConfiguration;
private Resources mResources;
+ /**
+ * Creates a new context wrapper with no theme and no base context.
+ * <p>
+ * <stong>Note:</strong> A base context <strong>must</strong> be attached
+ * using {@link #attachBaseContext(Context)} before calling any other
+ * method on the newly constructed context wrapper.
+ */
public ContextThemeWrapper() {
super(null);
}
+ /**
+ * Creates a new context wrapper with the specified theme.
+ * <p>
+ * The specified theme will be applied on top of the base context's theme.
+ * Any attributes not explicitly defined in the theme identified by
+ * <var>themeResId</var> will retain their original values.
+ *
+ * @param base the base context
+ * @param themeResId the resource ID of the theme to be applied on top of
+ * the base context's theme
+ */
public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
super(base);
mThemeResource = themeResId;
}
+ /**
+ * Creates a new context wrapper with the specified theme.
+ * <p>
+ * Unlike {@link #ContextThemeWrapper(Context, int)}, the theme passed to
+ * this constructor will completely replace the base context's theme.
+ *
+ * @param base the base context
+ * @param theme the theme against which resources should be inflated
+ */
public ContextThemeWrapper(Context base, Resources.Theme theme) {
super(base);
mTheme = theme;
@@ -60,11 +88,12 @@
* information.
*
* <p>This method can only be called once, and must be called before any
- * calls to {@link #getResources()} are made.
+ * calls to {@link #getResources()} or {@link #getAssets()} are made.
*/
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
if (mResources != null) {
- throw new IllegalStateException("getResources() has already been called");
+ throw new IllegalStateException(
+ "getResources() or getAssets() has already been called");
}
if (mOverrideConfiguration != null) {
throw new IllegalStateException("Override configuration has already been set");
@@ -73,20 +102,25 @@
}
@Override
- public Resources getResources() {
- if (mResources != null) {
- return mResources;
- }
- if (mOverrideConfiguration == null) {
- mResources = super.getResources();
- return mResources;
- } else {
- Context resc = createConfigurationContext(mOverrideConfiguration);
- mResources = resc.getResources();
- return mResources;
- }
+ public AssetManager getAssets() {
+ // Ensure we're returning assets with the correct configuration.
+ return getResources().getAssets();
}
-
+
+ @Override
+ public Resources getResources() {
+ if (mResources == null) {
+ if (mOverrideConfiguration == null) {
+ mResources = super.getResources();
+ } else {
+ final Context resContext = createConfigurationContext(mOverrideConfiguration);
+ mResources = resContext.getResources();
+ }
+ }
+
+ return mResources;
+ }
+
@Override
public void setTheme(int resid) {
if (mThemeResource != resid) {
@@ -94,14 +128,15 @@
initializeTheme();
}
}
-
+
/** @hide */
@Override
public int getThemeResId() {
return mThemeResource;
}
- @Override public Resources.Theme getTheme() {
+ @Override
+ public Resources.Theme getTheme() {
if (mTheme != null) {
return mTheme;
}
@@ -113,7 +148,8 @@
return mTheme;
}
- @Override public Object getSystemService(String name) {
+ @Override
+ public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
@@ -122,27 +158,27 @@
}
return getBaseContext().getSystemService(name);
}
-
+
/**
* Called by {@link #setTheme} and {@link #getTheme} to apply a theme
- * resource to the current Theme object. Can override to change the
- * default (simple) behavior. This method will not be called in multiple
+ * resource to the current Theme object. May be overridden to change the
+ * default (simple) behavior. This method will not be called in multiple
* threads simultaneously.
*
- * @param theme The Theme object being modified.
- * @param resid The theme style resource being applied to <var>theme</var>.
- * @param first Set to true if this is the first time a style is being
- * applied to <var>theme</var>.
+ * @param theme the theme being modified
+ * @param resId the style resource being applied to <var>theme</var>
+ * @param first {@code true} if this is the first time a style is being
+ * applied to <var>theme</var>
*/
- protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
- theme.applyStyle(resid, true);
+ protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) {
+ theme.applyStyle(resId, true);
}
private void initializeTheme() {
final boolean first = mTheme == null;
if (first) {
mTheme = getResources().newTheme();
- Resources.Theme theme = getBaseContext().getTheme();
+ final Resources.Theme theme = getBaseContext().getTheme();
if (theme != null) {
mTheme.setTo(theme);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b17f88f..16d9bf3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -576,6 +576,53 @@
* often used as a convenience to store data related to views in the views
* themselves rather than by putting them in a separate structure.
* </p>
+ * <p>
+ * Tags may be specified with character sequence values in layout XML as either
+ * a single tag using the {@link android.R.styleable#View_tag android:tag}
+ * attribute or multiple tags using the {@code <tag>} child element:
+ * <pre>
+ * <View ...
+ * android:tag="@string/mytag_value" />
+ * <View ...>
+ * <tag android:id="@+id/mytag"
+ * android:value="@string/mytag_value" />
+ * </View>
+ * </pre>
+ * </p>
+ * <p>
+ * Tags may also be specified with arbitrary objects from code using
+ * {@link #setTag(Object)} or {@link #setTag(int, Object)}.
+ * </p>
+ *
+ * <a name="Themes"></a>
+ * <h3>Themes</h3>
+ * <p>
+ * By default, Views are created using the theme of the Context object supplied
+ * to their constructor; however, a different theme may be specified by using
+ * the {@link android.R.styleable#View_theme android:theme} attribute in layout
+ * XML or by passing a {@link ContextThemeWrapper} to the constructor from
+ * code.
+ * </p>
+ * <p>
+ * When the {@link android.R.styleable#View_theme android:theme} attribute is
+ * used in XML, the specified theme is applied on top of the inflation
+ * context's theme (see {@link LayoutInflater}) and used for the view itself as
+ * well as any child elements.
+ * </p>
+ * <p>
+ * In the following example, both views will be created using the Material dark
+ * color scheme; however, because an overlay theme is used which only defines a
+ * subset of attributes, the value of
+ * {@link android.R.styleable#Theme_colorAccent android:colorAccent} defined on
+ * the inflation context's theme (e.g. the Activity theme) will be preserved.
+ * <pre>
+ * <LinearLayout
+ * ...
+ * android:theme="@android:theme/ThemeOverlay.Material.Dark">
+ * <View ...>
+ * </LinearLayout>
+ * </pre>
+ * </p>
*
* <a name="Properties"></a>
* <h3>Properties</h3>
@@ -699,6 +746,7 @@
* @attr ref android.R.styleable#View_translationY
* @attr ref android.R.styleable#View_translationZ
* @attr ref android.R.styleable#View_visibility
+ * @attr ref android.R.styleable#View_theme
*
* @see android.view.ViewGroup
*/
@@ -753,6 +801,11 @@
private static boolean sIgnoreMeasureCache = false;
/**
+ * Ignore an optimization that skips unnecessary EXACTLY layout passes.
+ */
+ private static boolean sAlwaysRemeasureExactly = false;
+
+ /**
* This view does not want keystrokes. Use with TAKES_FOCUS_MASK when
* calling setFlags.
*/
@@ -3817,6 +3870,11 @@
// specifically apps that use some popular open source libraries.
sUseZeroUnspecifiedMeasureSpec = targetSdkVersion < M;
+ // Old versions of the platform would give different results from
+ // LinearLayout measurement passes using EXACTLY and non-EXACTLY
+ // modes, so we always need to run an additional EXACTLY pass.
+ sAlwaysRemeasureExactly = targetSdkVersion <= M;
+
sCompatibilityDone = true;
}
}
@@ -18772,17 +18830,27 @@
long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
- if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
- widthMeasureSpec != mOldWidthMeasureSpec ||
- heightMeasureSpec != mOldHeightMeasureSpec) {
+ final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
+ // Optimize layout by avoiding an extra EXACTLY pass when the view is
+ // already measured as the correct size. In API 23 and below, this
+ // extra pass is required to make LinearLayout re-distribute weight.
+ final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec
+ || heightMeasureSpec != mOldHeightMeasureSpec;
+ final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
+ && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
+ final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
+ && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
+ final boolean needsLayout = specChanged
+ && (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
+
+ if (forceLayout || needsLayout) {
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
- int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
- mMeasureCache.indexOfKey(key);
+ int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 7b4640b..a6d9932 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -120,6 +120,13 @@
}
/**
+ * An interface to be notified about hardware keyboard status.
+ */
+ public interface OnHardKeyboardStatusChangeListener {
+ public void onHardKeyboardStatusChange(boolean available);
+ }
+
+ /**
* Request that the window manager call
* {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager}
* within a surface transaction at a later time.
@@ -231,4 +238,29 @@
* @param listener The listener to register.
*/
public abstract void registerAppTransitionListener(AppTransitionListener listener);
+
+ /**
+ * Retrieves a height of input method window.
+ */
+ public abstract int getInputMethodWindowVisibleHeight();
+
+ /**
+ * Saves last input method window for transition.
+ *
+ * Note that it is assumed that this method is called only by InputMethodManagerService.
+ */
+ public abstract void saveLastInputMethodWindowForTransition();
+
+ /**
+ * Returns true when the hardware keyboard is available.
+ */
+ public abstract boolean isHardKeyboardAvailable();
+
+ /**
+ * Sets the callback listener for hardware keyboard status changes.
+ *
+ * @param listener The listener to set.
+ */
+ public abstract void setOnHardKeyboardStatusChangeListener(
+ OnHardKeyboardStatusChangeListener listener);
}
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 6962711..0cc1b25 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -612,7 +612,7 @@
View listItem = view;
try {
View v;
- while (!(v = (View) listItem.getParent()).equals(this)) {
+ while ((v = (View) listItem.getParent()) != null && !v.equals(this)) {
listItem = v;
}
} catch (ClassCastException e) {
@@ -620,11 +620,13 @@
return INVALID_POSITION;
}
- // Search the children for the list item
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- if (getChildAt(i).equals(listItem)) {
- return mFirstPosition + i;
+ if (listItem != null) {
+ // Search the children for the list item
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ if (getChildAt(i).equals(listItem)) {
+ return mFirstPosition + i;
+ }
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 150b407..fddc40f 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -107,6 +107,7 @@
import android.widget.TextView.Drawables;
import android.widget.TextView.OnEditorActionListener;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.Preconditions;
@@ -2632,7 +2633,8 @@
}
}
- private class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener {
+ @VisibleForTesting
+ public class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener {
private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
private static final int ADD_TO_DICTIONARY = -1;
private static final int DELETE_TEXT = -2;
@@ -2798,6 +2800,11 @@
return suggestionSpans;
}
+ @VisibleForTesting
+ public ViewGroup getContentViewForTesting() {
+ return mContentView;
+ }
+
@Override
public void show() {
if (!(mTextView.getText() instanceof Editable)) return;
@@ -5103,6 +5110,11 @@
return 0 <= start && start <= end && end <= text.length();
}
+ @VisibleForTesting
+ public SuggestionsPopupWindow getSuggestionsPopupWindowForTesting() {
+ return mSuggestionsPopupWindow;
+ }
+
/**
* An InputFilter that monitors text input to maintain undo history. It does not modify the
* text being typed (and hence always returns null from the filter() method).
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 20fe61d..b53af0c 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -72,6 +72,8 @@
*/
@RemoteView
public class ImageView extends View {
+ private static final String LOG_TAG = "ImageView";
+
// settable by the client
private Uri mUri;
private int mResource = 0;
@@ -87,7 +89,7 @@
private boolean mHasColorFilter = false;
private Xfermode mXfermode;
private int mAlpha = 255;
- private int mViewAlphaScale = 256;
+ private final int mViewAlphaScale = 256;
private boolean mColorMod = false;
private Drawable mDrawable = null;
@@ -105,8 +107,8 @@
private Matrix mDrawMatrix = null;
// Avoid allocations...
- private RectF mTempSrc = new RectF();
- private RectF mTempDst = new RectF();
+ private final RectF mTempSrc = new RectF();
+ private final RectF mTempDst = new RectF();
private boolean mCropToPadding;
@@ -116,6 +118,9 @@
// AdjustViewBounds behavior will be in compatibility mode for older apps.
private boolean mAdjustViewBoundsCompat = false;
+ /** Whether to pass Resources when creating the source from a stream. */
+ private boolean mUseCorrectStreamDensity;
+
private static final ScaleType[] sScaleTypeArray = {
ScaleType.MATRIX,
ScaleType.FIT_XY,
@@ -147,30 +152,21 @@
initImageView();
final TypedArray a = context.obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.ImageView, defStyleAttr, defStyleRes);
+ attrs, R.styleable.ImageView, defStyleAttr, defStyleRes);
- Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src);
+ final Drawable d = a.getDrawable(R.styleable.ImageView_src);
if (d != null) {
setImageDrawable(d);
}
- mBaselineAlignBottom = a.getBoolean(
- com.android.internal.R.styleable.ImageView_baselineAlignBottom, false);
+ mBaselineAlignBottom = a.getBoolean(R.styleable.ImageView_baselineAlignBottom, false);
+ mBaseline = a.getDimensionPixelSize(R.styleable.ImageView_baseline, -1);
- mBaseline = a.getDimensionPixelSize(
- com.android.internal.R.styleable.ImageView_baseline, -1);
+ setAdjustViewBounds(a.getBoolean(R.styleable.ImageView_adjustViewBounds, false));
+ setMaxWidth(a.getDimensionPixelSize(R.styleable.ImageView_maxWidth, Integer.MAX_VALUE));
+ setMaxHeight(a.getDimensionPixelSize(R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
- setAdjustViewBounds(
- a.getBoolean(com.android.internal.R.styleable.ImageView_adjustViewBounds,
- false));
-
- setMaxWidth(a.getDimensionPixelSize(
- com.android.internal.R.styleable.ImageView_maxWidth, Integer.MAX_VALUE));
-
- setMaxHeight(a.getDimensionPixelSize(
- com.android.internal.R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
-
- final int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1);
+ final int index = a.getInt(R.styleable.ImageView_scaleType, -1);
if (index >= 0) {
setScaleType(sScaleTypeArray[index]);
}
@@ -193,24 +189,26 @@
applyImageTint();
- final int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255);
+ final int alpha = a.getInt(R.styleable.ImageView_drawableAlpha, 255);
if (alpha != 255) {
- setAlpha(alpha);
+ setImageAlpha(alpha);
}
mCropToPadding = a.getBoolean(
- com.android.internal.R.styleable.ImageView_cropToPadding, false);
-
+ R.styleable.ImageView_cropToPadding, false);
+
a.recycle();
//need inflate syntax/reader for matrix
}
private void initImageView() {
- mMatrix = new Matrix();
- mScaleType = ScaleType.FIT_CENTER;
- mAdjustViewBoundsCompat = mContext.getApplicationInfo().targetSdkVersion <=
- Build.VERSION_CODES.JELLY_BEAN_MR1;
+ mMatrix = new Matrix();
+ mScaleType = ScaleType.FIT_CENTER;
+
+ final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+ mAdjustViewBoundsCompat = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
+ mUseCorrectStreamDensity = targetSdkVersion > Build.VERSION_CODES.M;
}
@Override
@@ -258,7 +256,8 @@
@Override
public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
super.onPopulateAccessibilityEventInternal(event);
- CharSequence contentDescription = getContentDescription();
+
+ final CharSequence contentDescription = getContentDescription();
if (!TextUtils.isEmpty(contentDescription)) {
event.getText().add(contentDescription);
}
@@ -269,7 +268,7 @@
* to preserve the aspect ratio of its drawable
*
* @return whether to adjust the bounds of this view
- * to presrve the original aspect ratio of the drawable
+ * to preserve the original aspect ratio of the drawable
*
* @see #setAdjustViewBounds(boolean)
*
@@ -291,7 +290,7 @@
*
* @param adjustViewBounds Whether to adjust the bounds of this view
* to preserve the original aspect ratio of the drawable.
- *
+ *
* @see #getAdjustViewBounds()
*
* @attr ref android.R.styleable#ImageView_adjustViewBounds
@@ -323,14 +322,14 @@
* of 100 x 100 while preserving the original aspect ratio, do the following: 1) set
* adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width
* layout params to WRAP_CONTENT.
- *
+ *
* <p>
* Note that this view could be still smaller than 100 x 100 using this approach if the original
* image is small. To set an image to a fixed size, specify that size in the layout params and
* then use {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine how to fit
* the image within the bounds.
* </p>
- *
+ *
* @param maxWidth maximum width for this view
*
* @see #getMaxWidth()
@@ -361,14 +360,14 @@
* maximum of 100 x 100 while preserving the original aspect ratio, do the following: 1) set
* adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width
* layout params to WRAP_CONTENT.
- *
+ *
* <p>
* Note that this view could be still smaller than 100 x 100 using this approach if the original
* image is small. To set an image to a fixed size, specify that size in the layout params and
* then use {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine how to fit
* the image within the bounds.
* </p>
- *
+ *
* @param maxHeight maximum height for this view
*
* @see #getMaxHeight()
@@ -436,9 +435,7 @@
*/
@android.view.RemotableViewMethod
public void setImageURI(@Nullable Uri uri) {
- if (mResource != 0 ||
- (mUri != uri &&
- (uri == null || mUri == null || !uri.equals(mUri)))) {
+ if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) {
updateDrawable(null);
mResource = 0;
mUri = uri;
@@ -457,7 +454,7 @@
/**
* Sets a drawable as the content of this ImageView.
- *
+ *
* @param drawable the Drawable to set, or {@code null} to clear the
* content
*/
@@ -577,7 +574,7 @@
/**
* Sets a Bitmap as the content of this ImageView.
- *
+ *
* @param bm The bitmap to set
*/
@android.view.RemotableViewMethod
@@ -609,7 +606,7 @@
}
/**
- * Sets the image level, when it is constructed from a
+ * Sets the image level, when it is constructed from a
* {@link android.graphics.drawable.LevelListDrawable}.
*
* @param level The new level for the image.
@@ -675,7 +672,7 @@
* From XML, use this syntax: <code>android:scaleType="centerInside"</code>.
*/
CENTER_INSIDE (7);
-
+
ScaleType(int ni) {
nativeInt = ni;
}
@@ -685,9 +682,9 @@
/**
* Controls how the image should be resized or moved to match the size
* of this ImageView.
- *
+ *
* @param scaleType The desired scaling mode.
- *
+ *
* @attr ref android.R.styleable#ImageView_scaleType
*/
public void setScaleType(ScaleType scaleType) {
@@ -698,13 +695,13 @@
if (mScaleType != scaleType) {
mScaleType = scaleType;
- setWillNotCacheDrawing(mScaleType == ScaleType.CENTER);
+ setWillNotCacheDrawing(mScaleType == ScaleType.CENTER);
requestLayout();
invalidate();
}
}
-
+
/**
* Return the current scale type in use by this ImageView.
*
@@ -734,7 +731,7 @@
* Adds a transformation {@link Matrix} that is applied
* to the view's drawable when it is drawn. Allows custom scaling,
* translation, and perspective distortion.
- *
+ *
* @param matrix the transformation parameters in matrix form
*/
public void setImageMatrix(Matrix matrix) {
@@ -787,8 +784,8 @@
return;
}
- Resources rsrc = getResources();
- if (rsrc == null) {
+ final Resources res = getResources();
+ if (res == null) {
return;
}
@@ -798,12 +795,12 @@
try {
d = mContext.getDrawable(mResource);
} catch (Exception e) {
- Log.w("ImageView", "Unable to find resource: " + mResource, e);
+ Log.w(LOG_TAG, "Unable to find resource: " + mResource, e);
// Don't try again.
mUri = null;
}
} else if (mUri != null) {
- String scheme = mUri.getScheme();
+ final String scheme = mUri.getScheme();
if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
try {
// Load drawable through Resources, to get the source density information
@@ -811,31 +808,32 @@
mContext.getContentResolver().getResourceId(mUri);
d = r.r.getDrawable(r.id, mContext.getTheme());
} catch (Exception e) {
- Log.w("ImageView", "Unable to open content: " + mUri, e);
+ Log.w(LOG_TAG, "Unable to open content: " + mUri, e);
}
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
|| ContentResolver.SCHEME_FILE.equals(scheme)) {
InputStream stream = null;
try {
stream = mContext.getContentResolver().openInputStream(mUri);
- d = Drawable.createFromStream(stream, null);
+ d = Drawable.createFromResourceStream(
+ mUseCorrectStreamDensity ? res : null, null, stream, null);
} catch (Exception e) {
- Log.w("ImageView", "Unable to open content: " + mUri, e);
+ Log.w(LOG_TAG, "Unable to open content: " + mUri, e);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
- Log.w("ImageView", "Unable to close content: " + mUri, e);
+ Log.w(LOG_TAG, "Unable to close content: " + mUri, e);
}
}
}
- } else {
+ } else {
d = Drawable.createFromPath(mUri.toString());
}
-
+
if (d == null) {
- System.out.println("resolveUri failed on bad bitmap uri: " + mUri);
+ Log.w(LOG_TAG, "resolveUri failed on bad bitmap uri: " + mUri);
// Don't try again.
mUri = null;
}
@@ -890,7 +888,7 @@
}
private void resizeFromDrawable() {
- Drawable d = mDrawable;
+ final Drawable d = mDrawable;
if (d != null) {
int w = d.getIntrinsicWidth();
if (w < 0) w = mDrawableWidth;
@@ -923,23 +921,23 @@
private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st) {
// ScaleToFit enum to their corresponding Matrix.ScaleToFit values
return sS2FArray[st.nativeInt - 1];
- }
+ }
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
resolveUri();
int w;
int h;
-
+
// Desired aspect ratio of the view's contents (not including padding)
float desiredAspect = 0.0f;
-
+
// We are allowed to change the view's width
boolean resizeWidth = false;
-
+
// We are allowed to change the view's height
boolean resizeHeight = false;
-
+
final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
@@ -959,15 +957,15 @@
if (mAdjustViewBounds) {
resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
-
+
desiredAspect = (float) w / (float) h;
}
}
-
- int pleft = mPaddingLeft;
- int pright = mPaddingRight;
- int ptop = mPaddingTop;
- int pbottom = mPaddingBottom;
+
+ final int pleft = mPaddingLeft;
+ final int pright = mPaddingRight;
+ final int ptop = mPaddingTop;
+ final int pbottom = mPaddingBottom;
int widthSize;
int heightSize;
@@ -975,7 +973,7 @@
if (resizeWidth || resizeHeight) {
/* If we get here, it means we want to resize to match the
drawables aspect ratio, and we have the freedom to change at
- least one dimension.
+ least one dimension.
*/
// Get the max possible width given our constraints
@@ -986,13 +984,13 @@
if (desiredAspect != 0.0f) {
// See what our actual aspect ratio is
- float actualAspect = (float)(widthSize - pleft - pright) /
+ final float actualAspect = (float)(widthSize - pleft - pright) /
(heightSize - ptop - pbottom);
-
+
if (Math.abs(actualAspect - desiredAspect) > 0.0000001) {
-
+
boolean done = false;
-
+
// Try adjusting width to be proportional to height
if (resizeWidth) {
int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) +
@@ -1006,9 +1004,9 @@
if (newWidth <= widthSize) {
widthSize = newWidth;
done = true;
- }
+ }
}
-
+
// Try adjusting height to be proportional to width
if (!done && resizeHeight) {
int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) +
@@ -1033,7 +1031,7 @@
*/
w += pleft + pright;
h += ptop + pbottom;
-
+
w = Math.max(w, getSuggestedMinimumWidth());
h = Math.max(h, getSuggestedMinimumHeight());
@@ -1047,8 +1045,8 @@
private int resolveAdjustedSize(int desiredSize, int maxSize,
int measureSpec) {
int result = desiredSize;
- int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);
+ final int specMode = MeasureSpec.getMode(measureSpec);
+ final int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
/* Parent says we can be as big as we want. Just don't be larger
@@ -1057,8 +1055,8 @@
result = Math.min(desiredSize, maxSize);
break;
case MeasureSpec.AT_MOST:
- // Parent says we can be as big as we want, up to specSize.
- // Don't be larger than specSize, and don't be larger than
+ // Parent says we can be as big as we want, up to specSize.
+ // Don't be larger than specSize, and don't be larger than
// the max size imposed on ourselves.
result = Math.min(Math.min(desiredSize, specSize), maxSize);
break;
@@ -1072,7 +1070,7 @@
@Override
protected boolean setFrame(int l, int t, int r, int b) {
- boolean changed = super.setFrame(l, t, r, b);
+ final boolean changed = super.setFrame(l, t, r, b);
mHaveFrame = true;
configureBounds();
return changed;
@@ -1083,14 +1081,14 @@
return;
}
- int dwidth = mDrawableWidth;
- int dheight = mDrawableHeight;
+ final int dwidth = mDrawableWidth;
+ final int dheight = mDrawableHeight;
- int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
- int vheight = getHeight() - mPaddingTop - mPaddingBottom;
+ final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
+ final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
- boolean fits = (dwidth < 0 || vwidth == dwidth) &&
- (dheight < 0 || vheight == dheight);
+ final boolean fits = (dwidth < 0 || vwidth == dwidth)
+ && (dheight < 0 || vheight == dheight);
if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
/* If the drawable has no intrinsic size, or we're told to
@@ -1125,7 +1123,7 @@
float dx = 0, dy = 0;
if (dwidth * vheight > vwidth * dheight) {
- scale = (float) vheight / (float) dheight;
+ scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
@@ -1139,14 +1137,14 @@
float scale;
float dx;
float dy;
-
+
if (dwidth <= vwidth && dheight <= vheight) {
scale = 1.0f;
} else {
scale = Math.min((float) vwidth / (float) dwidth,
(float) vheight / (float) dheight);
}
-
+
dx = Math.round((vwidth - dwidth * scale) * 0.5f);
dy = Math.round((vheight - dheight * scale) * 0.5f);
@@ -1156,7 +1154,7 @@
// Generate the required transform.
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight);
-
+
mDrawMatrix = mMatrix;
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
}
@@ -1166,7 +1164,8 @@
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
- Drawable d = mDrawable;
+
+ final Drawable d = mDrawable;
if (d != null && d.isStateful()) {
d.setState(getDrawableState());
}
@@ -1213,9 +1212,9 @@
if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) {
mDrawable.draw(canvas);
} else {
- int saveCount = canvas.getSaveCount();
+ final int saveCount = canvas.getSaveCount();
canvas.save();
-
+
if (mCropToPadding) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
@@ -1223,7 +1222,7 @@
scrollX + mRight - mLeft - mPaddingRight,
scrollY + mBottom - mTop - mPaddingBottom);
}
-
+
canvas.translate(mPaddingLeft, mPaddingTop);
if (mDrawMatrix != null) {
@@ -1258,7 +1257,7 @@
*
* @param baseline The baseline to use, or -1 if none is to be provided.
*
- * @see #setBaseline(int)
+ * @see #setBaseline(int)
* @attr ref android.R.styleable#ImageView_baseline
*/
public void setBaseline(int baseline) {
@@ -1295,11 +1294,11 @@
/**
* Set a tinting option for the image.
- *
+ *
* @param color Color tint to apply.
* @param mode How to apply the color. The standard mode is
* {@link PorterDuff.Mode#SRC_ATOP}
- *
+ *
* @attr ref android.R.styleable#ImageView_tint
*/
public final void setColorFilter(int color, PorterDuff.Mode mode) {
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index b5e08ca..ad939be 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -25,6 +25,7 @@
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
@@ -99,6 +100,13 @@
public static final int SHOW_DIVIDER_END = 4;
/**
+ * Compatibility check. Old versions of the platform would give different
+ * results from measurement passes using EXACTLY and non-EXACTLY modes,
+ * even when the resulting size was the same.
+ */
+ private final boolean mAllowInconsistentMeasurement;
+
+ /**
* Whether the children of this layout are baseline aligned. Only applicable
* if {@link #mOrientation} is horizontal.
*/
@@ -231,6 +239,9 @@
mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE);
mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0);
+ final int version = context.getApplicationInfo().targetSdkVersion;
+ mAllowInconsistentMeasurement = version <= Build.VERSION_CODES.M;
+
a.recycle();
}
@@ -699,6 +710,7 @@
final boolean useLargestChild = mUseLargestChild;
int largestChildHeight = Integer.MIN_VALUE;
+ int consumedExcessSpace = 0;
// See how tall everyone is. Also remember max width.
for (int i = 0; i < count; ++i) {
@@ -718,26 +730,25 @@
mTotalLength += mDividerHeight;
}
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
totalWeight += lp.weight;
-
- if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
- // Optimization: don't bother measuring children who are going to use
- // leftover space. These views will get measured again down below if
- // there is any leftover space.
+
+ final boolean useExcessSpace = lp.height == 0 && lp.weight > 0;
+ if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) {
+ // Optimization: don't bother measuring children who are only
+ // laid out using excess space. These views will get measured
+ // later if we have space to distribute.
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
skippedMeasure = true;
} else {
- int oldHeight = Integer.MIN_VALUE;
-
- if (lp.height == 0 && lp.weight > 0) {
- // heightMode is either UNSPECIFIED or AT_MOST, and this
- // child wanted to stretch to fill available space.
- // Translate that to WRAP_CONTENT so that it does not end up
- // with a height of 0
- oldHeight = 0;
+ if (useExcessSpace) {
+ // The heightMode is either UNSPECIFIED or AT_MOST, and
+ // this child is only laid out using excess space. Measure
+ // using WRAP_CONTENT so that we can find out the view's
+ // optimal height. We'll restore the original height of 0
+ // after measurement.
lp.height = LayoutParams.WRAP_CONTENT;
}
@@ -745,15 +756,19 @@
// previous children have given a weight, then we allow it to
// use all available space (and we will shrink things later
// if needed).
- measureChildBeforeLayout(
- child, i, widthMeasureSpec, 0, heightMeasureSpec,
- totalWeight == 0 ? mTotalLength : 0);
-
- if (oldHeight != Integer.MIN_VALUE) {
- lp.height = oldHeight;
- }
+ final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
+ measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
+ heightMeasureSpec, usedHeight);
final int childHeight = child.getMeasuredHeight();
+ if (useExcessSpace) {
+ // Restore the original height and record how much space
+ // we've allocated to excess-only children so that we can
+ // match the behavior of EXACTLY measurement.
+ lp.height = 0;
+ consumedExcessSpace += childHeight;
+ }
+
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));
@@ -857,52 +872,42 @@
// Either expand children with weight to take up available space or
// shrink them if they extend beyond our current bounds. If we skipped
// measurement on any children, we need to measure them now.
- int delta = heightSize - mTotalLength;
+ final int delta = heightSize - mTotalLength
+ + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
- float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
+ final float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
-
- if (child.getVisibility() == View.GONE) {
+ if (child == null || child.getVisibility() == View.GONE) {
continue;
}
-
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
-
- float childExtra = lp.weight;
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final float childExtra = lp.weight;
if (childExtra > 0) {
- // Child said it could absorb extra space -- give him his share
- int share = (int) (childExtra * delta / weightSum);
- weightSum -= childExtra;
- delta -= share;
-
- final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
- mPaddingLeft + mPaddingRight +
- lp.leftMargin + lp.rightMargin, lp.width);
-
- // TODO: Use a field like lp.isMeasured to figure out if this
- // child has been previously measured
- if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
- // child was measured once already above...
- // base new measurement on stored values
- int childHeight = child.getMeasuredHeight() + share;
- if (childHeight < 0) {
- childHeight = 0;
- }
-
- child.measure(childWidthMeasureSpec,
- MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
+ final int share = (int) (childExtra * delta / weightSum);
+ final int childHeight;
+ if (lp.height == 0 && (!mAllowInconsistentMeasurement
+ || heightMode == MeasureSpec.EXACTLY)) {
+ // This child needs to be laid out from scratch using
+ // only its share of excess space.
+ childHeight = share;
} else {
- // child was skipped in the loop above.
- // Measure for this first time here
- child.measure(childWidthMeasureSpec,
- MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
- MeasureSpec.EXACTLY));
+ // This child had some intrinsic height to which we
+ // need to add its share of excess space.
+ childHeight = child.getMeasuredHeight() + share;
}
+ final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ Math.max(0, childHeight), MeasureSpec.EXACTLY);
+ final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
+ mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin,
+ lp.width);
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+
// Child may now not fit in vertical dimension.
childState = combineMeasuredStates(childState, child.getMeasuredState()
& (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
@@ -1043,6 +1048,7 @@
final boolean isExactly = widthMode == MeasureSpec.EXACTLY;
int largestChildWidth = Integer.MIN_VALUE;
+ int usedExcessSpace = 0;
// See how wide everyone is. Also remember max height.
for (int i = 0; i < count; ++i) {
@@ -1062,15 +1068,15 @@
mTotalLength += mDividerWidth;
}
- final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
- child.getLayoutParams();
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
totalWeight += lp.weight;
-
- if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) {
- // Optimization: don't bother measuring children who are going to use
- // leftover space. These views will get measured again down below if
- // there is any leftover space.
+
+ final boolean useExcessSpace = lp.width == 0 && lp.weight > 0;
+ if (widthMode == MeasureSpec.EXACTLY && useExcessSpace) {
+ // Optimization: don't bother measuring children who are only
+ // laid out using excess space. These views will get measured
+ // later if we have space to distribute.
if (isExactly) {
mTotalLength += lp.leftMargin + lp.rightMargin;
} else {
@@ -1094,14 +1100,12 @@
skippedMeasure = true;
}
} else {
- int oldWidth = Integer.MIN_VALUE;
-
- if (lp.width == 0 && lp.weight > 0) {
- // widthMode is either UNSPECIFIED or AT_MOST, and this
- // child
- // wanted to stretch to fill available space. Translate that to
- // WRAP_CONTENT so that it does not end up with a width of 0
- oldWidth = 0;
+ if (useExcessSpace) {
+ // The widthMode is either UNSPECIFIED or AT_MOST, and
+ // this child is only laid out using excess space. Measure
+ // using WRAP_CONTENT so that we can find out the view's
+ // optimal width. We'll restore the original width of 0
+ // after measurement.
lp.width = LayoutParams.WRAP_CONTENT;
}
@@ -1109,22 +1113,26 @@
// previous children have given a weight, then we allow it to
// use all available space (and we will shrink things later
// if needed).
- measureChildBeforeLayout(child, i, widthMeasureSpec,
- totalWeight == 0 ? mTotalLength : 0,
+ final int usedWidth = totalWeight == 0 ? mTotalLength : 0;
+ measureChildBeforeLayout(child, i, widthMeasureSpec, usedWidth,
heightMeasureSpec, 0);
- if (oldWidth != Integer.MIN_VALUE) {
- lp.width = oldWidth;
+ final int childWidth = child.getMeasuredWidth();
+ if (useExcessSpace) {
+ // Restore the original width and record how much space
+ // we've allocated to excess-only children so that we can
+ // match the behavior of EXACTLY measurement.
+ lp.width = 0;
+ usedExcessSpace += childWidth;
}
- final int childWidth = child.getMeasuredWidth();
if (isExactly) {
- mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
- getNextLocationOffset(child);
+ mTotalLength += childWidth + lp.leftMargin + lp.rightMargin
+ + getNextLocationOffset(child);
} else {
final int totalLength = mTotalLength;
- mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
- lp.rightMargin + getNextLocationOffset(child));
+ mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin
+ + lp.rightMargin + getNextLocationOffset(child));
}
if (useLargestChild) {
@@ -1242,9 +1250,10 @@
// Either expand children with weight to take up available space or
// shrink them if they extend beyond our current bounds. If we skipped
// measurement on any children, we need to measure them now.
- int delta = widthSize - mTotalLength;
+ final int delta = widthSize - mTotalLength
+ + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace);
if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
- float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
+ final float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
@@ -1254,45 +1263,32 @@
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
-
if (child == null || child.getVisibility() == View.GONE) {
continue;
}
-
- final LinearLayout.LayoutParams lp =
- (LinearLayout.LayoutParams) child.getLayoutParams();
- float childExtra = lp.weight;
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final float childExtra = lp.weight;
if (childExtra > 0) {
- // Child said it could absorb extra space -- give him his share
- int share = (int) (childExtra * delta / weightSum);
- weightSum -= childExtra;
- delta -= share;
+ final int share = (int) (childExtra * delta / weightSum);
+ final int childWidth;
+ if (lp.width == 0 && (!mAllowInconsistentMeasurement
+ || widthMode == MeasureSpec.EXACTLY)) {
+ // This child needs to be laid out from scratch using
+ // only its share of excess space.
+ childWidth = share;
+ } else {
+ // This child had some intrinsic width to which we
+ // need to add its share of excess space.
+ childWidth = child.getMeasuredWidth() + share;
+ }
- final int childHeightMeasureSpec = getChildMeasureSpec(
- heightMeasureSpec,
+ final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ Math.max(0, childWidth), MeasureSpec.EXACTLY);
+ final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin,
lp.height);
-
- // TODO: Use a field like lp.isMeasured to figure out if this
- // child has been previously measured
- if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) {
- // child was measured once already above ... base new measurement
- // on stored values
- int childWidth = child.getMeasuredWidth() + share;
- if (childWidth < 0) {
- childWidth = 0;
- }
-
- child.measure(
- MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
- childHeightMeasureSpec);
- } else {
- // child was skipped in the loop above. Measure for this first time here
- child.measure(MeasureSpec.makeMeasureSpec(
- share > 0 ? share : 0, MeasureSpec.EXACTLY),
- childHeightMeasureSpec);
- }
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
// Child may now not fit in horizontal dimension.
childState = combineMeasuredStates(childState,
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 088adbb..ad2b4a7 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -816,7 +816,15 @@
mSearchButton.setVisibility(visCollapsed);
updateSubmitButton(hasText);
mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE);
- mCollapsedIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
+
+ final int iconVisibility;
+ if (mCollapsedIcon.getDrawable() == null || mIconifiedByDefault) {
+ iconVisibility = GONE;
+ } else {
+ iconVisibility = VISIBLE;
+ }
+ mCollapsedIcon.setVisibility(iconVisibility);
+
updateCloseButton();
updateVoiceButton(!hasText);
updateSubmitArea();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ce66eeb..7a64377 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -139,6 +139,7 @@
import android.view.textservice.TextServicesManager;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastMath;
import com.android.internal.widget.EditableInputConnection;
@@ -1695,6 +1696,15 @@
throw new UnsupportedOperationException("not implemented");
}
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public final Editor getEditorForTesting() {
+ return mEditor;
+ }
+
/**
* Associate an {@link android.content.UndoManager} with this TextView. Once
* done, all edit operations on the TextView will result in appropriate
@@ -8755,12 +8765,9 @@
public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
super.onPopulateAccessibilityEventInternal(event);
- final boolean isPassword = hasPasswordTransformationMethod();
- if (!isPassword || shouldSpeakPasswordsForAccessibility()) {
- final CharSequence text = getTextForAccessibility();
- if (!TextUtils.isEmpty(text)) {
- event.getText().add(text);
- }
+ final CharSequence text = getTextForAccessibility();
+ if (!TextUtils.isEmpty(text)) {
+ event.getText().add(text);
}
}
@@ -8911,10 +8918,7 @@
final boolean isPassword = hasPasswordTransformationMethod();
info.setPassword(isPassword);
-
- if (!isPassword || shouldSpeakPasswordsForAccessibility()) {
- info.setText(getTextForAccessibility());
- }
+ info.setText(getTextForAccessibility());
if (mBufferType == BufferType.EDITABLE) {
info.setEditable(true);
@@ -9146,18 +9150,30 @@
}
/**
- * Gets the text reported for accessibility purposes.
+ * Returns the text that should be exposed to accessibility services.
+ * <p>
+ * This approximates what is displayed visually. If the user has specified
+ * that accessibility services should speak passwords, this method will
+ * bypass any password transformation method and return unobscured text.
*
- * @return The accessibility text.
- *
- * @hide
+ * @return the text that should be exposed to accessibility services, may
+ * be {@code null} if no text is set
*/
- public CharSequence getTextForAccessibility() {
- CharSequence text = getText();
- if (TextUtils.isEmpty(text)) {
- text = getHint();
+ @Nullable
+ private CharSequence getTextForAccessibility() {
+ // If the text is empty, we must be showing the hint text.
+ if (TextUtils.isEmpty(mText)) {
+ return mHint;
}
- return text;
+
+ // Check whether we need to bypass the transformation
+ // method and expose unobscured text.
+ if (hasPasswordTransformationMethod() && shouldSpeakPasswordsForAccessibility()) {
+ return mText;
+ }
+
+ // Otherwise, speak whatever text is being displayed.
+ return mTransformed;
}
void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
index c1b184e..9d12803 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -23,6 +23,7 @@
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.view.ActionMode;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -467,6 +468,9 @@
public boolean onKeyShortcut(int keyCode, KeyEvent event) {
Menu menu = mDecorToolbar.getMenu();
if (menu != null) {
+ final KeyCharacterMap kmap = KeyCharacterMap.load(
+ event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
+ menu.setQwertyMode(kmap.getKeyboardType() != KeyCharacterMap.NUMERIC);
menu.performShortcut(keyCode, event, 0);
}
// This action bar always returns true for handling keyboard shortcuts.
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 1e41e63..ab3ec98 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -71,5 +71,10 @@
void showAssistDisclosure();
void startAssist(in Bundle args);
+
+ /**
+ * Notifies the status bar that a camera launch gesture has been detected.
+ */
+ void onCameraLaunchGestureDetected();
}
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 229407e..6816646 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -29,6 +29,7 @@
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
+import java.util.Iterator;
import java.util.HashMap;
import java.util.Vector;
@@ -1864,6 +1865,20 @@
}
/**
+ * Removes a message from the deferred messages queue.
+ */
+ protected final void removeDeferredMessages(int what) {
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
+
+ Iterator<Message> iterator = smh.mDeferredMessages.iterator();
+ while (iterator.hasNext()) {
+ Message msg = iterator.next();
+ if (msg.what == what) iterator.remove();
+ }
+ }
+
+ /**
* Validate that the message was sent by
* {@link StateMachine#quit} or {@link StateMachine#quitNow}.
* */
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 1995694..0579ce7d 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1284,6 +1284,12 @@
*/
public static final int SOME_AUTH_REQUIRED_AFTER_USER_REQUEST = 0x4;
+ /**
+ * Strong authentication is required because the user has been locked out after too many
+ * attempts.
+ */
+ public static final int STRONG_AUTH_REQUIRED_AFTER_LOCKOUT = 0x8;
+
public static final int DEFAULT = STRONG_AUTH_REQUIRED_AFTER_BOOT;
private static final int ALLOWING_FINGERPRINT = SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 92812f8..372d934 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -124,6 +124,10 @@
// input device we are listening to.
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
+ if (!mShowDecor) {
+ // When there is no decor we should not react to anything.
+ return false;
+ }
// A drag action is started if we aren't dragging already and the starting event is
// either a left mouse button or any other input device.
if (!mDragging &&
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
index e537942..ae99f0b 100644
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -32,6 +32,7 @@
#include "HarfBuzzNGFaceSkia.h"
+#include <stdlib.h>
#include <cutils/log.h>
#include <SkPaint.h>
#include <SkPath.h>
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index aa79d70..17d2a5e 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -158,7 +158,7 @@
sp<GraphicBuffer> buffer(wrapper->buffer);
- Rect rect;
+ Rect rect(Rect::EMPTY_RECT);
if (dirtyRect) {
rect.left = GET_INT(dirtyRect, gRectClassInfo.left);
rect.top = GET_INT(dirtyRect, gRectClassInfo.top);
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index abd2409..4a311d31 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -292,7 +292,7 @@
return 0;
}
- Rect dirtyRect;
+ Rect dirtyRect(Rect::EMPTY_RECT);
Rect* dirtyRectPtr = NULL;
if (dirtyRectObj) {
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 7e05793..beb83b1 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -142,7 +142,7 @@
ANativeWindow_Buffer buffer;
- Rect rect;
+ Rect rect(Rect::EMPTY_RECT);
if (dirtyRect) {
rect.left = GET_INT(dirtyRect, gRectClassInfo.left);
rect.top = GET_INT(dirtyRect, gRectClassInfo.top);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fefd5a7..b0621e9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2567,10 +2567,11 @@
<permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE"
android:protectionLevel="signature" />
- <!-- @SystemApi Must be required by a {@link
+ <!-- Must be required by a {@link
android.service.notification.ConditionProviderService},
to ensure that only the system can bind to it.
- @hide -->
+ <p>Protection level: signature
+ -->
<permission android:name="android.permission.BIND_CONDITION_PROVIDER_SERVICE"
android:protectionLevel="signature" />
@@ -2804,7 +2805,7 @@
</activity>
<receiver android:name="com.android.server.BootReceiver"
- android:primaryUserOnly="true">
+ android:systemUserOnly="true">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
diff --git a/core/res/res/drawable-hdpi/sim_dark_blue.9.png b/core/res/res/drawable-hdpi/sim_dark_blue.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sim_dark_green.9.png b/core/res/res/drawable-hdpi/sim_dark_green.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sim_dark_orange.9.png b/core/res/res/drawable-hdpi/sim_dark_orange.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sim_dark_purple.9.png b/core/res/res/drawable-hdpi/sim_dark_purple.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sim_light_blue.9.png b/core/res/res/drawable-hdpi/sim_light_blue.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sim_light_green.9.png b/core/res/res/drawable-hdpi/sim_light_green.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sim_light_orange.9.png b/core/res/res/drawable-hdpi/sim_light_orange.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sim_light_purple.9.png b/core/res/res/drawable-hdpi/sim_light_purple.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sim_dark_blue.9.png b/core/res/res/drawable-mdpi/sim_dark_blue.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sim_dark_green.9.png b/core/res/res/drawable-mdpi/sim_dark_green.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sim_dark_orange.9.png b/core/res/res/drawable-mdpi/sim_dark_orange.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sim_dark_purple.9.png b/core/res/res/drawable-mdpi/sim_dark_purple.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sim_light_blue.9.png b/core/res/res/drawable-mdpi/sim_light_blue.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sim_light_green.9.png b/core/res/res/drawable-mdpi/sim_light_green.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sim_light_orange.9.png b/core/res/res/drawable-mdpi/sim_light_orange.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sim_light_purple.9.png b/core/res/res/drawable-mdpi/sim_light_purple.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_audio_notification_am_alpha.png b/core/res/res/drawable-xxhdpi/ic_audio_notification_am_alpha.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_car_mode.png b/core/res/res/drawable-xxhdpi/stat_notify_car_mode.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_disk_full.png b/core/res/res/drawable-xxhdpi/stat_notify_disk_full.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_email_generic.png b/core/res/res/drawable-xxhdpi/stat_notify_email_generic.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_gmail.png b/core/res/res/drawable-xxhdpi/stat_notify_gmail.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_more.png b/core/res/res/drawable-xxhdpi/stat_notify_more.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_sdcard.png b/core/res/res/drawable-xxhdpi/stat_notify_sdcard.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_sdcard_prepare.png b/core/res/res/drawable-xxhdpi/stat_notify_sdcard_prepare.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_sdcard_usb.png b/core/res/res/drawable-xxhdpi/stat_notify_sdcard_usb.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_sim_toolkit.png b/core/res/res/drawable-xxhdpi/stat_notify_sim_toolkit.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_sync.png b/core/res/res/drawable-xxhdpi/stat_notify_sync.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_sync_anim0.png b/core/res/res/drawable-xxhdpi/stat_notify_sync_anim0.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_sync_error.png b/core/res/res/drawable-xxhdpi/stat_notify_sync_error.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_voicemail.png b/core/res/res/drawable-xxhdpi/stat_notify_voicemail.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_data_bluetooth.png b/core/res/res/drawable-xxhdpi/stat_sys_data_bluetooth.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_data_usb.png b/core/res/res/drawable-xxhdpi/stat_sys_data_usb.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim0.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim0.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim1.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim1.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim2.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim2.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim3.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim3.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim4.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim4.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_download_anim5.png b/core/res/res/drawable-xxhdpi/stat_sys_download_anim5.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_gps_on.png b/core/res/res/drawable-xxhdpi/stat_sys_gps_on.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_throttled.png b/core/res/res/drawable-xxhdpi/stat_sys_throttled.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim0.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim0.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim3.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim3.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim4.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim4.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim5.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim5.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_warning.png b/core/res/res/drawable-xxhdpi/stat_sys_warning.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable/edit_text_material.xml b/core/res/res/drawable/edit_text_material.xml
index 38ac567..901b3dc 100644
--- a/core/res/res/drawable/edit_text_material.xml
+++ b/core/res/res/drawable/edit_text_material.xml
@@ -22,8 +22,7 @@
<selector>
<item android:state_enabled="false">
<nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
+ android:tint="?attr/colorControlNormal" />
</item>
<item android:state_pressed="false" android:state_focused="false">
<nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
diff --git a/core/res/res/layout/non_client_decor_dark.xml b/core/res/res/layout/non_client_decor_dark.xml
index d1e2974..112f4b7 100644
--- a/core/res/res/layout/non_client_decor_dark.xml
+++ b/core/res/res/layout/non_client_decor_dark.xml
@@ -26,7 +26,9 @@
android:layout_width="match_parent"
android:layout_gravity="end"
android:layout_height="wrap_content"
- android:background="@drawable/non_client_decor_title" >
+ android:background="@drawable/non_client_decor_title"
+ android:focusable="false"
+ android:descendantFocusability="blocksDescendants" >
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
@@ -40,7 +42,7 @@
android:layout_gravity="center_vertical|end"
android:contentDescription="@string/maximize_button_text"
android:background="@drawable/decor_maximize_button_dark" />
- <Button
+ <Button
android:id="@+id/close_window"
android:layout_width="32dp"
android:layout_height="32dp"
diff --git a/core/res/res/layout/non_client_decor_light.xml b/core/res/res/layout/non_client_decor_light.xml
index f7c3fcd..5dd79c7 100644
--- a/core/res/res/layout/non_client_decor_light.xml
+++ b/core/res/res/layout/non_client_decor_light.xml
@@ -26,7 +26,9 @@
android:layout_width="match_parent"
android:layout_gravity="end"
android:layout_height="wrap_content"
- android:background="@drawable/non_client_decor_title" >
+ android:background="@drawable/non_client_decor_title"
+ android:focusable="false"
+ android:descendantFocusability="blocksDescendants" >
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
@@ -40,7 +42,7 @@
android:layout_gravity="center_vertical|end"
android:contentDescription="@string/maximize_button_text"
android:background="@drawable/decor_maximize_button_light" />
- <Button
+ <Button
android:id="@+id/close_window"
android:layout_width="32dp"
android:layout_height="32dp"
diff --git a/core/res/res/values-af-watch/strings.xml b/core/res/res/values-af-watch/strings.xml
index c81848a..9dc98a1 100644
--- a/core/res/res/values-af-watch/strings.xml
+++ b/core/res/res/values-af-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Program <xliff:g id="NUMBER_0">%1$d</xliff:g> van <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-am-watch/strings.xml b/core/res/res/values-am-watch/strings.xml
index 95188b6..ebe6ae0 100644
--- a/core/res/res/values-am-watch/strings.xml
+++ b/core/res/res/values-am-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g> መተግበሪያ ከ<xliff:g id="NUMBER_1">%2$d</xliff:g>።"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ar-watch/strings.xml b/core/res/res/values-ar-watch/strings.xml
index 8cbb0a5..374b665 100644
--- a/core/res/res/values-ar-watch/strings.xml
+++ b/core/res/res/values-ar-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"التطبيق <xliff:g id="NUMBER_0">%1$d</xliff:g> من <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-az-rAZ-watch/strings.xml b/core/res/res/values-az-rAZ-watch/strings.xml
index 7e4a762..cdc6a3d 100644
--- a/core/res/res/values-az-rAZ-watch/strings.xml
+++ b/core/res/res/values-az-rAZ-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Tətbiq <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bg-watch/strings.xml b/core/res/res/values-bg-watch/strings.xml
index 1f1d921..a31f8d8 100644
--- a/core/res/res/values-bg-watch/strings.xml
+++ b/core/res/res/values-bg-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Прилож. <xliff:g id="NUMBER_0">%1$d</xliff:g> от <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bn-rBD-watch/strings.xml b/core/res/res/values-bn-rBD-watch/strings.xml
index b934841..4ce6091 100644
--- a/core/res/res/values-bn-rBD-watch/strings.xml
+++ b/core/res/res/values-bn-rBD-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g>টির মধ্যে <xliff:g id="NUMBER_0">%1$d</xliff:g>টি অ্যাপ্লিকেশান"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ca-watch/strings.xml b/core/res/res/values-ca-watch/strings.xml
index b44703e..8f626e5 100644
--- a/core/res/res/values-ca-watch/strings.xml
+++ b/core/res/res/values-ca-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplicació <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-cs-watch/strings.xml b/core/res/res/values-cs-watch/strings.xml
index 89c9dee..34f8864 100644
--- a/core/res/res/values-cs-watch/strings.xml
+++ b/core/res/res/values-cs-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikace <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-da-watch/strings.xml b/core/res/res/values-da-watch/strings.xml
index f1daf30..efc4cf2 100644
--- a/core/res/res/values-da-watch/strings.xml
+++ b/core/res/res/values-da-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> af <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-de-watch/strings.xml b/core/res/res/values-de-watch/strings.xml
index 52a21ba..99b3336 100644
--- a/core/res/res/values-de-watch/strings.xml
+++ b/core/res/res/values-de-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> von <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-el-watch/strings.xml b/core/res/res/values-el-watch/strings.xml
index 81a7451..c159d6f 100644
--- a/core/res/res/values-el-watch/strings.xml
+++ b/core/res/res/values-el-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Εφαρμογή <xliff:g id="NUMBER_0">%1$d</xliff:g> από <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rAU-watch/strings.xml b/core/res/res/values-en-rAU-watch/strings.xml
index 6734cd3..6102d4e4 100644
--- a/core/res/res/values-en-rAU-watch/strings.xml
+++ b/core/res/res/values-en-rAU-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> of <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rGB-watch/strings.xml b/core/res/res/values-en-rGB-watch/strings.xml
index 6734cd3..6102d4e4 100644
--- a/core/res/res/values-en-rGB-watch/strings.xml
+++ b/core/res/res/values-en-rGB-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> of <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rIN-watch/strings.xml b/core/res/res/values-en-rIN-watch/strings.xml
index 6734cd3..6102d4e4 100644
--- a/core/res/res/values-en-rIN-watch/strings.xml
+++ b/core/res/res/values-en-rIN-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> of <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-es-rUS-watch/strings.xml b/core/res/res/values-es-rUS-watch/strings.xml
index 763b24d..76664ef 100644
--- a/core/res/res/values-es-rUS-watch/strings.xml
+++ b/core/res/res/values-es-rUS-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-es-watch/strings.xml b/core/res/res/values-es-watch/strings.xml
index d9ea0fe..002bfce 100644
--- a/core/res/res/values-es-watch/strings.xml
+++ b/core/res/res/values-es-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-et-rEE-watch/strings.xml b/core/res/res/values-et-rEE-watch/strings.xml
index 0adb487..a1d29a8 100644
--- a/core/res/res/values-et-rEE-watch/strings.xml
+++ b/core/res/res/values-et-rEE-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Rakendus <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-eu-rES-watch/strings.xml b/core/res/res/values-eu-rES-watch/strings.xml
index 9c13ef9..aa67602 100644
--- a/core/res/res/values-eu-rES-watch/strings.xml
+++ b/core/res/res/values-eu-rES-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g> aplikaz."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fa-watch/strings.xml b/core/res/res/values-fa-watch/strings.xml
index a33d7ec..243ccad 100644
--- a/core/res/res/values-fa-watch/strings.xml
+++ b/core/res/res/values-fa-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"برنامه <xliff:g id="NUMBER_0">%1$d</xliff:g> از <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fi-watch/strings.xml b/core/res/res/values-fi-watch/strings.xml
index 89782a5..abe5ebb 100644
--- a/core/res/res/values-fi-watch/strings.xml
+++ b/core/res/res/values-fi-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Sovellus <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fr-rCA-watch/strings.xml b/core/res/res/values-fr-rCA-watch/strings.xml
index ea9c1c2..197b18a 100644
--- a/core/res/res/values-fr-rCA-watch/strings.xml
+++ b/core/res/res/values-fr-rCA-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Appli <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fr-watch/strings.xml b/core/res/res/values-fr-watch/strings.xml
index 7e616cd..b853188 100644
--- a/core/res/res/values-fr-watch/strings.xml
+++ b/core/res/res/values-fr-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Appli <xliff:g id="NUMBER_0">%1$d</xliff:g> sur <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-gl-rES-watch/strings.xml b/core/res/res/values-gl-rES-watch/strings.xml
index d9ea0fe..002bfce 100644
--- a/core/res/res/values-gl-rES-watch/strings.xml
+++ b/core/res/res/values-gl-rES-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplicación <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-gu-rIN-watch/strings.xml b/core/res/res/values-gu-rIN-watch/strings.xml
index 3e50335..f607966 100644
--- a/core/res/res/values-gu-rIN-watch/strings.xml
+++ b/core/res/res/values-gu-rIN-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> માંથી <xliff:g id="NUMBER_0">%1$d</xliff:g> એપ્લિકેશન."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hi-watch/strings.xml b/core/res/res/values-hi-watch/strings.xml
index 148dab4..ead4d60 100644
--- a/core/res/res/values-hi-watch/strings.xml
+++ b/core/res/res/values-hi-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> में से <xliff:g id="NUMBER_0">%1$d</xliff:g> ऐप."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hr-watch/strings.xml b/core/res/res/values-hr-watch/strings.xml
index 4b88ac7..7e65f07 100644
--- a/core/res/res/values-hr-watch/strings.xml
+++ b/core/res/res/values-hr-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikacija <xliff:g id="NUMBER_0">%1$d</xliff:g> od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hu-watch/strings.xml b/core/res/res/values-hu-watch/strings.xml
index 9f2e97f..aa72468 100644
--- a/core/res/res/values-hu-watch/strings.xml
+++ b/core/res/res/values-hu-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>. alkalmazás"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hy-rAM-watch/strings.xml b/core/res/res/values-hy-rAM-watch/strings.xml
index 265268e..28617cb 100644
--- a/core/res/res/values-hy-rAM-watch/strings.xml
+++ b/core/res/res/values-hy-rAM-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Հավելված <xliff:g id="NUMBER_0">%1$d</xliff:g>՝ <xliff:g id="NUMBER_1">%2$d</xliff:g>-ից:"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-in-watch/strings.xml b/core/res/res/values-in-watch/strings.xml
index 947a7f1..b0b22e7 100644
--- a/core/res/res/values-in-watch/strings.xml
+++ b/core/res/res/values-in-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikasi <xliff:g id="NUMBER_0">%1$d</xliff:g> dari <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-is-rIS-watch/strings.xml b/core/res/res/values-is-rIS-watch/strings.xml
index cb6da5c..ab82c1c 100644
--- a/core/res/res/values-is-rIS-watch/strings.xml
+++ b/core/res/res/values-is-rIS-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Forrit <xliff:g id="NUMBER_0">%1$d</xliff:g> af <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-it-watch/strings.xml b/core/res/res/values-it-watch/strings.xml
index a042221..111c2a2 100644
--- a/core/res/res/values-it-watch/strings.xml
+++ b/core/res/res/values-it-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> di <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-iw-watch/strings.xml b/core/res/res/values-iw-watch/strings.xml
index 64b194d..d0b4943 100644
--- a/core/res/res/values-iw-watch/strings.xml
+++ b/core/res/res/values-iw-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"אפליקציה <xliff:g id="NUMBER_0">%1$d</xliff:g> מתוך <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ja-watch/strings.xml b/core/res/res/values-ja-watch/strings.xml
index b3c6d97..ec1257ef 100644
--- a/core/res/res/values-ja-watch/strings.xml
+++ b/core/res/res/values-ja-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"アプリ<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index e43a19c..545b963 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1024,7 +1024,7 @@
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USBを写真転送に使用"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USBをMIDIに使用"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"USBアクセサリを接続しました"</string>
- <string name="usb_notification_message" msgid="7347368030849048437">"タップするとその他のオプションが表示されます。"</string>
+ <string name="usb_notification_message" msgid="7347368030849048437">"タップしてその他のオプションを表示"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USBデバッグが接続されました"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"タップしてUSBデバッグを無効化"</string>
<string name="select_input_method" msgid="8547250819326693584">"キーボードの変更"</string>
diff --git a/core/res/res/values-ka-rGE-watch/strings.xml b/core/res/res/values-ka-rGE-watch/strings.xml
index 4fe6d11..5aa9aaf 100644
--- a/core/res/res/values-ka-rGE-watch/strings.xml
+++ b/core/res/res/values-ka-rGE-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"აპი <xliff:g id="NUMBER_0">%1$d</xliff:g>, სულ <xliff:g id="NUMBER_1">%2$d</xliff:g>-დან."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-kk-rKZ-watch/strings.xml b/core/res/res/values-kk-rKZ-watch/strings.xml
index 583eb19..e913230 100644
--- a/core/res/res/values-kk-rKZ-watch/strings.xml
+++ b/core/res/res/values-kk-rKZ-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g> бағдарлама."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-km-rKH-watch/strings.xml b/core/res/res/values-km-rKH-watch/strings.xml
index 2b7e12f..01731f3 100644
--- a/core/res/res/values-km-rKH-watch/strings.xml
+++ b/core/res/res/values-km-rKH-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"កម្មវិធី <xliff:g id="NUMBER_0">%1$d</xliff:g> នៃ <xliff:g id="NUMBER_1">%2$d</xliff:g>។"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-kn-rIN-watch/strings.xml b/core/res/res/values-kn-rIN-watch/strings.xml
index e01cee1..ea010b3 100644
--- a/core/res/res/values-kn-rIN-watch/strings.xml
+++ b/core/res/res/values-kn-rIN-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="NUMBER_0">%1$d</xliff:g> ಅಪ್ಲಿಕೇಶನ್."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ko-watch/strings.xml b/core/res/res/values-ko-watch/strings.xml
index df3288b..985f401 100644
--- a/core/res/res/values-ko-watch/strings.xml
+++ b/core/res/res/values-ko-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"앱 <xliff:g id="NUMBER_1">%2$d</xliff:g>개 중 <xliff:g id="NUMBER_0">%1$d</xliff:g>개"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ky-rKG-watch/strings.xml b/core/res/res/values-ky-rKG-watch/strings.xml
index 3f167ac..3c708961 100644
--- a/core/res/res/values-ky-rKG-watch/strings.xml
+++ b/core/res/res/values-ky-rKG-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ичинен <xliff:g id="NUMBER_0">%1$d</xliff:g> колднм."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lo-rLA-watch/strings.xml b/core/res/res/values-lo-rLA-watch/strings.xml
index a8c9cd2..d7e97fb 100644
--- a/core/res/res/values-lo-rLA-watch/strings.xml
+++ b/core/res/res/values-lo-rLA-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"ແອັບ <xliff:g id="NUMBER_0">%1$d</xliff:g> ໃນ <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lt-watch/strings.xml b/core/res/res/values-lt-watch/strings.xml
index ed8ccdb..bf32040 100644
--- a/core/res/res/values-lt-watch/strings.xml
+++ b/core/res/res/values-lt-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g> programa iš <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lv-watch/strings.xml b/core/res/res/values-lv-watch/strings.xml
index a0d051e..3a7fd95 100644
--- a/core/res/res/values-lv-watch/strings.xml
+++ b/core/res/res/values-lv-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>. lietotne no <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mk-rMK-watch/strings.xml b/core/res/res/values-mk-rMK-watch/strings.xml
index b4eb51a..5e67a61 100644
--- a/core/res/res/values-mk-rMK-watch/strings.xml
+++ b/core/res/res/values-mk-rMK-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Апликац. <xliff:g id="NUMBER_0">%1$d</xliff:g> од <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ml-rIN-watch/strings.xml b/core/res/res/values-ml-rIN-watch/strings.xml
index 079c42f..c10769e 100644
--- a/core/res/res/values-ml-rIN-watch/strings.xml
+++ b/core/res/res/values-ml-rIN-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g> അപ്ലിക്കേഷൻ."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mn-rMN-watch/strings.xml b/core/res/res/values-mn-rMN-watch/strings.xml
index 49f829b..7b2e21b 100644
--- a/core/res/res/values-mn-rMN-watch/strings.xml
+++ b/core/res/res/values-mn-rMN-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-ны <xliff:g id="NUMBER_1">%2$d</xliff:g> апп."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mr-rIN-watch/strings.xml b/core/res/res/values-mr-rIN-watch/strings.xml
index 49fa7d9..11af412 100644
--- a/core/res/res/values-mr-rIN-watch/strings.xml
+++ b/core/res/res/values-mr-rIN-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> पैकी <xliff:g id="NUMBER_0">%1$d</xliff:g> अॅप"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ms-rMY-watch/strings.xml b/core/res/res/values-ms-rMY-watch/strings.xml
index 148f518..d8245ed 100644
--- a/core/res/res/values-ms-rMY-watch/strings.xml
+++ b/core/res/res/values-ms-rMY-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Apl <xliff:g id="NUMBER_0">%1$d</xliff:g> daripada <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-my-rMM-watch/strings.xml b/core/res/res/values-my-rMM-watch/strings.xml
index ec89e53..34bd033 100644
--- a/core/res/res/values-my-rMM-watch/strings.xml
+++ b/core/res/res/values-my-rMM-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g>၏ <xliff:g id="NUMBER_0">%1$d</xliff:g> အသေးစားဆော့ဝဲ"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-nb-watch/strings.xml b/core/res/res/values-nb-watch/strings.xml
index 3bd7fa5..aba8abb 100644
--- a/core/res/res/values-nb-watch/strings.xml
+++ b/core/res/res/values-nb-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> av <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ne-rNP-watch/strings.xml b/core/res/res/values-ne-rNP-watch/strings.xml
index e2453c89..fe331bd 100644
--- a/core/res/res/values-ne-rNP-watch/strings.xml
+++ b/core/res/res/values-ne-rNP-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g> को <xliff:g id="NUMBER_0">%1$d</xliff:g> अनुप्रयोग।"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-nl-watch/strings.xml b/core/res/res/values-nl-watch/strings.xml
index 989fa27..bed8a11 100644
--- a/core/res/res/values-nl-watch/strings.xml
+++ b/core/res/res/values-nl-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> van <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pa-rIN-watch/strings.xml b/core/res/res/values-pa-rIN-watch/strings.xml
index b30daa4..addaa6b 100644
--- a/core/res/res/values-pa-rIN-watch/strings.xml
+++ b/core/res/res/values-pa-rIN-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"ਐਪ <xliff:g id="NUMBER_1">%2$d</xliff:g> ਦਾ <xliff:g id="NUMBER_0">%1$d</xliff:g>"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pl-watch/strings.xml b/core/res/res/values-pl-watch/strings.xml
index d9608f3..f266d0a 100644
--- a/core/res/res/values-pl-watch/strings.xml
+++ b/core/res/res/values-pl-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikacja <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt-rBR-watch/strings.xml b/core/res/res/values-pt-rBR-watch/strings.xml
index 120e4a5..0eb0da3 100644
--- a/core/res/res/values-pt-rBR-watch/strings.xml
+++ b/core/res/res/values-pt-rBR-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt-rPT-watch/strings.xml b/core/res/res/values-pt-rPT-watch/strings.xml
index 69d2c0a..1654fc3 100644
--- a/core/res/res/values-pt-rPT-watch/strings.xml
+++ b/core/res/res/values-pt-rPT-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplicação <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt-watch/strings.xml b/core/res/res/values-pt-watch/strings.xml
index 120e4a5..0eb0da3 100644
--- a/core/res/res/values-pt-watch/strings.xml
+++ b/core/res/res/values-pt-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> de <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ro-watch/strings.xml b/core/res/res/values-ro-watch/strings.xml
index 95c8ec3..dc3c9065 100644
--- a/core/res/res/values-ro-watch/strings.xml
+++ b/core/res/res/values-ro-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplic. <xliff:g id="NUMBER_0">%1$d</xliff:g> din <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ru-watch/strings.xml b/core/res/res/values-ru-watch/strings.xml
index f32f63b..cd536c5 100644
--- a/core/res/res/values-ru-watch/strings.xml
+++ b/core/res/res/values-ru-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Приложение <xliff:g id="NUMBER_0">%1$d</xliff:g> из <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-si-rLK-watch/strings.xml b/core/res/res/values-si-rLK-watch/strings.xml
index b55687f..4adfbb5 100644
--- a/core/res/res/values-si-rLK-watch/strings.xml
+++ b/core/res/res/values-si-rLK-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g> හි <xliff:g id="NUMBER_1">%2$d</xliff:g> යෙදුම."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sk-watch/strings.xml b/core/res/res/values-sk-watch/strings.xml
index 601c016..24bed89 100644
--- a/core/res/res/values-sk-watch/strings.xml
+++ b/core/res/res/values-sk-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikácia <xliff:g id="NUMBER_0">%1$d</xliff:g> z <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sl-watch/strings.xml b/core/res/res/values-sl-watch/strings.xml
index bf1190f..5bc0ea5 100644
--- a/core/res/res/values-sl-watch/strings.xml
+++ b/core/res/res/values-sl-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_0">%1$d</xliff:g>. aplikac. od <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sq-rAL-watch/strings.xml b/core/res/res/values-sq-rAL-watch/strings.xml
index d1637ba..1b0bdaf 100644
--- a/core/res/res/values-sq-rAL-watch/strings.xml
+++ b/core/res/res/values-sq-rAL-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Aplikacioni <xliff:g id="NUMBER_0">%1$d</xliff:g> nga <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sr-watch/strings.xml b/core/res/res/values-sr-watch/strings.xml
index 484977e..ae70ab1 100644
--- a/core/res/res/values-sr-watch/strings.xml
+++ b/core/res/res/values-sr-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Апликација <xliff:g id="NUMBER_0">%1$d</xliff:g> од <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sv-watch/strings.xml b/core/res/res/values-sv-watch/strings.xml
index 3bd7fa5..aba8abb 100644
--- a/core/res/res/values-sv-watch/strings.xml
+++ b/core/res/res/values-sv-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> av <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sw-watch/strings.xml b/core/res/res/values-sw-watch/strings.xml
index 5a8c72e..929022f 100644
--- a/core/res/res/values-sw-watch/strings.xml
+++ b/core/res/res/values-sw-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Programu ya <xliff:g id="NUMBER_0">%1$d</xliff:g> kati ya <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ta-rIN-watch/strings.xml b/core/res/res/values-ta-rIN-watch/strings.xml
index 4c05cab..23accbc 100644
--- a/core/res/res/values-ta-rIN-watch/strings.xml
+++ b/core/res/res/values-ta-rIN-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"பயன்பாடு: <xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-te-rIN-watch/strings.xml b/core/res/res/values-te-rIN-watch/strings.xml
index 6b4b900..0843471 100644
--- a/core/res/res/values-te-rIN-watch/strings.xml
+++ b/core/res/res/values-te-rIN-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g>లో <xliff:g id="NUMBER_0">%1$d</xliff:g>వ అనువర్తనం."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-th-watch/strings.xml b/core/res/res/values-th-watch/strings.xml
index 568b083..9cc21b0 100644
--- a/core/res/res/values-th-watch/strings.xml
+++ b/core/res/res/values-th-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"แอป <xliff:g id="NUMBER_0">%1$d</xliff:g> จาก <xliff:g id="NUMBER_1">%2$d</xliff:g> แอป"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-tl-watch/strings.xml b/core/res/res/values-tl-watch/strings.xml
index bb0dc4b..3fba2dc 100644
--- a/core/res/res/values-tl-watch/strings.xml
+++ b/core/res/res/values-tl-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"App <xliff:g id="NUMBER_0">%1$d</xliff:g> ng <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-tr-watch/strings.xml b/core/res/res/values-tr-watch/strings.xml
index d9c3923..b34ad74 100644
--- a/core/res/res/values-tr-watch/strings.xml
+++ b/core/res/res/values-tr-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Uygulama <xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-uk-watch/strings.xml b/core/res/res/values-uk-watch/strings.xml
index 02e6466..4141b08 100644
--- a/core/res/res/values-uk-watch/strings.xml
+++ b/core/res/res/values-uk-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Додаток <xliff:g id="NUMBER_0">%1$d</xliff:g> з <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ur-rPK-watch/strings.xml b/core/res/res/values-ur-rPK-watch/strings.xml
index 0fd24c9..f90af0b 100644
--- a/core/res/res/values-ur-rPK-watch/strings.xml
+++ b/core/res/res/values-ur-rPK-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"ایپ <xliff:g id="NUMBER_0">%1$d</xliff:g> از <xliff:g id="NUMBER_1">%2$d</xliff:g>۔"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-uz-rUZ-watch/strings.xml b/core/res/res/values-uz-rUZ-watch/strings.xml
index 7abd603..42fe54a 100644
--- a/core/res/res/values-uz-rUZ-watch/strings.xml
+++ b/core/res/res/values-uz-rUZ-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"<xliff:g id="NUMBER_1">%2$d</xliff:g>dan <xliff:g id="NUMBER_0">%1$d</xliff:g> ilova."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-vi-watch/strings.xml b/core/res/res/values-vi-watch/strings.xml
index f419bdf..634d890 100644
--- a/core/res/res/values-vi-watch/strings.xml
+++ b/core/res/res/values-vi-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Ứng dụng <xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rCN-watch/strings.xml b/core/res/res/values-zh-rCN-watch/strings.xml
index 9c9e49d..8864701 100644
--- a/core/res/res/values-zh-rCN-watch/strings.xml
+++ b/core/res/res/values-zh-rCN-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"应用:<xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g>。"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rHK-watch/strings.xml b/core/res/res/values-zh-rHK-watch/strings.xml
index 3b5fb8e..356e853 100644
--- a/core/res/res/values-zh-rHK-watch/strings.xml
+++ b/core/res/res/values-zh-rHK-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"應用程式 (<xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>)"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rTW-watch/strings.xml b/core/res/res/values-zh-rTW-watch/strings.xml
index 87f3abb..20ef6ba 100644
--- a/core/res/res/values-zh-rTW-watch/strings.xml
+++ b/core/res/res/values-zh-rTW-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"應用程式 <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>。"</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zu-watch/strings.xml b/core/res/res/values-zu-watch/strings.xml
index acd153b..22b8af7 100644
--- a/core/res/res/values-zu-watch/strings.xml
+++ b/core/res/res/values-zu-watch/strings.xml
@@ -21,4 +21,48 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="android_upgrading_apk" msgid="1090732262010398759">"Uhlelo lokusebenza olungu-<xliff:g id="NUMBER_0">%1$d</xliff:g> kokungu-<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+ <!-- no translation found for permgrouplab_sensors (202675452368612754) -->
+ <skip />
+ <!-- no translation found for permgrouplab_contactswear (2340286500790908344) -->
+ <skip />
+ <!-- no translation found for permgrouplab_locationwear (6275317222482780209) -->
+ <skip />
+ <!-- no translation found for permgrouplab_calendarwear (441900844045065081) -->
+ <skip />
+ <!-- no translation found for permgrouplab_smswear (6849506550342974220) -->
+ <skip />
+ <!-- no translation found for permgrouplab_storagewear (1003807594193602313) -->
+ <skip />
+ <!-- no translation found for permgrouplab_microphonewear (1047561180980891136) -->
+ <skip />
+ <!-- no translation found for permgrouplab_camerawear (4543951283103407017) -->
+ <skip />
+ <!-- no translation found for permgrouplab_phonewear (134365036753766126) -->
+ <skip />
+ <!-- no translation found for permgrouplab_sensorswear (1429324744329327663) -->
+ <skip />
+ <!-- no translation found for permlab_statusBarServicewear (2469402818964691034) -->
+ <skip />
+ <!-- no translation found for permlab_bodySensorswear (7857941041202791873) -->
+ <skip />
+ <!-- no translation found for permlab_accessFineLocationwear (5584423486924377563) -->
+ <skip />
+ <!-- no translation found for permlab_accessCoarseLocationwear (5880746016230166090) -->
+ <skip />
+ <!-- no translation found for permlab_sim_communicationwear (1899198085342781874) -->
+ <skip />
+ <!-- no translation found for permlab_createNetworkSocketswear (6467042386273822913) -->
+ <skip />
+ <!-- no translation found for permlab_manageProfileAndDeviceOwnerswear (7313340516937821847) -->
+ <skip />
+ <!-- no translation found for permlab_changeWimaxStatewear (3828470843939853744) -->
+ <skip />
+ <!-- no translation found for permlab_handoverStatuswear (4835786819716499249) -->
+ <skip />
+ <!-- no translation found for permlab_route_media_outputwear (8737024341474587192) -->
+ <skip />
+ <!-- no translation found for permlab_readInstallSessionswear (9059478058685861989) -->
+ <skip />
+ <!-- no translation found for permlab_requestInstallPackageswear (4982025836783539503) -->
+ <skip />
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 1a2600d..4531f75 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1816,9 +1816,9 @@
<attr name="uiOptions" />
<attr name="parentActivityName" />
<attr name="singleUser" />
- <!-- @hide This broacast receiver will only receive broadcasts for the
- primary user. Can only be used with receivers. -->
- <attr name="primaryUserOnly" format="boolean" />
+ <!-- @hide This broadcast receiver or activity will only receive broadcasts for the
+ system user-->
+ <attr name="systemUserOnly" format="boolean" />
<attr name="persistableMode" />
<attr name="allowEmbedded" />
<attr name="documentLaunchMode" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2c940ae..ef4e261 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2029,10 +2029,6 @@
IMS service implementation will do both.i.e.hold followed by merge. -->
<bool name="skipHoldBeforeMerge">true</bool>
- <!-- Flag indicating emergency calls will always use IMS irrespective of the state of
- the IMS connection -->
- <bool name="useImsAlwaysForEmergencyCall">true</bool>
-
<!-- Flag indicating whether the IMS service can be turned off. If false then
the service will not be turned-off completely (the ImsManager.turnOffIms() will
be disabled) but individual Features can be disabled using ImsConfig.setFeatureValue() -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a55258c..e04c743 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2138,7 +2138,6 @@
<java-symbol type="bool" name="config_carrier_vt_available" />
<java-symbol type="bool" name="config_device_wfc_ims_available" />
<java-symbol type="bool" name="config_carrier_wfc_ims_available" />
- <java-symbol type="bool" name="useImsAlwaysForEmergencyCall" />
<java-symbol type="attr" name="touchscreenBlocksFocus" />
<java-symbol type="layout" name="resolver_list_with_default" />
<java-symbol type="string" name="whichApplicationNamed" />
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index 03ae9dc..d99ee80 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -387,6 +387,63 @@
}
@SmallTest
+ public void testEnd() throws Throwable {
+ final MyListener l1 = new MyListener();
+ final MyListener l2 = new MyListener();
+ a1.addListener(l1);
+ a2.addListener(l2);
+ a1.addListener(new MyListener() {
+ @Override
+ public void onAnimationEnd(Animator anim) {
+ anim.cancel();
+ }
+ });
+ a2.addListener(new MyListener() {
+ @Override
+ public void onAnimationCancel(Animator anim) {
+ anim.end();
+ }
+ });
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(l1.cancelCalled);
+ assertFalse(l1.endCalled);
+ assertFalse(l2.cancelCalled);
+ assertFalse(l2.endCalled);
+ a1.start();
+ a2.start();
+ }
+ });
+ Thread.sleep(POLL_INTERVAL);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ a1.end();
+ a2.cancel();
+ }
+ });
+ Thread.sleep(POLL_INTERVAL);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // Calling cancel from onAnimationEnd will be ignored.
+ assertFalse(l1.cancelCalled);
+ assertTrue(l1.endCalled);
+ assertTrue(l2.cancelCalled);
+ assertTrue(l2.endCalled);
+
+ float value1 = (Float) a1.getAnimatedValue();
+ int value2 = (Integer) a2.getAnimatedValue();
+ assertEquals(A1_END_VALUE, value1);
+ assertEquals(A2_END_VALUE, value2);
+ }
+ });
+
+ }
+
+ @SmallTest
public void testEndValue() throws Throwable {
final MyListener l1 = new MyListener();
a1.addListener(l1);
diff --git a/core/tests/coretests/src/android/app/backup/FullBackupTest.java b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
index 8c9c63c..c3afbf6 100644
--- a/core/tests/coretests/src/android/app/backup/FullBackupTest.java
+++ b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
@@ -114,12 +114,14 @@
public void testparseBackupSchemeFromXml_lotsOfIncludesAndExcludes() throws Exception {
mXpp.setInput(new StringReader(
"<full-backup-content>" +
- "<exclude path=\"exclude1.txt\" domain=\"file\"/>" +
+ "<exclude path=\"exclude1.txt\" domain=\"file\"/>" +
"<include path=\"include1.txt\" domain=\"file\"/>" +
"<exclude path=\"exclude2.txt\" domain=\"database\"/>" +
"<include path=\"include2.txt\" domain=\"database\"/>" +
- "<exclude path=\"exclude3.txt\" domain=\"sharedpref\"/>" +
- "<include path=\"include3.txt\" domain=\"sharedpref\"/>" +
+ "<exclude path=\"exclude3\" domain=\"sharedpref\"/>" +
+ "<include path=\"include3\" domain=\"sharedpref\"/>" +
+ "<exclude path=\"exclude4.xml\" domain=\"sharedpref\"/>" +
+ "<include path=\"include4.xml\" domain=\"sharedpref\"/>" +
"</full-backup-content>"));
@@ -146,16 +148,27 @@
"include2.txt-journal")
.getCanonicalPath()));
- Set<String> sharedPrefDomainIncludes = includeMap.get(FullBackup.SHAREDPREFS_TREE_TOKEN);
+ List<String> sharedPrefDomainIncludes = new ArrayList<String>(
+ includeMap.get(FullBackup.SHAREDPREFS_TREE_TOKEN));
+ Collections.sort(sharedPrefDomainIncludes);
+
assertEquals("Didn't find expected sharedpref domain include.",
- 1, sharedPrefDomainIncludes.size());
+ 3, sharedPrefDomainIncludes.size());
assertEquals("Invalid path parsed for <include/>",
- new File(mContext.getSharedPrefsFile("foo").getParentFile(), "include3.txt")
+ new File(mContext.getSharedPrefsFile("foo").getParentFile(), "include3")
.getCanonicalPath(),
- sharedPrefDomainIncludes.iterator().next());
+ sharedPrefDomainIncludes.get(0));
+ assertEquals("Invalid path parsed for <include/>",
+ new File(mContext.getSharedPrefsFile("foo").getParentFile(), "include3.xml")
+ .getCanonicalPath(),
+ sharedPrefDomainIncludes.get(1));
+ assertEquals("Invalid path parsed for <include/>",
+ new File(mContext.getSharedPrefsFile("foo").getParentFile(), "include4.xml")
+ .getCanonicalPath(),
+ sharedPrefDomainIncludes.get(2));
- assertEquals("Unexpected number of <exclude/>s", 4, excludesSet.size());
+ assertEquals("Unexpected number of <exclude/>s", 6, excludesSet.size());
// Sets are annoying to iterate over b/c order isn't enforced - convert to an array and
// sort lexicographically.
List<String> arrayedSet = new ArrayList<String>(excludesSet);
@@ -173,9 +186,17 @@
new File(mContext.getFilesDir(), "exclude1.txt").getCanonicalPath(),
arrayedSet.get(2));
assertEquals("Invalid path parsed for <exclude/>",
- new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude3.txt")
+ new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude3")
.getCanonicalPath(),
arrayedSet.get(3));
+ assertEquals("Invalid path parsed for <exclude/>",
+ new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude3.xml")
+ .getCanonicalPath(),
+ arrayedSet.get(4));
+ assertEquals("Invalid path parsed for <exclude/>",
+ new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude4.xml")
+ .getCanonicalPath(),
+ arrayedSet.get(5));
}
public void testParseBackupSchemeFromXml_invalidXmlFails() throws Exception {
diff --git a/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
new file mode 100644
index 0000000..80181cf
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.pm;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.test.AndroidTestCase;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+public class AppsQueryHelperTests extends AndroidTestCase {
+
+ private AppsQueryHelper mAppsQueryHelper;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mAppsQueryHelper = new AppsQueryHelperTestable(getContext());
+ }
+
+ public void testQueryAppsSystemAppsOnly() {
+ List<String> apps = mAppsQueryHelper.queryApps(0, true, UserHandle.SYSTEM);
+ assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app2", "sys_app3"), apps);
+
+ apps = mAppsQueryHelper.queryApps(0, false, UserHandle.SYSTEM);
+ assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app2", "sys_app3", "app4"), apps);
+ }
+
+ public void testQueryAppsNonLaunchable() {
+ List<String> apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS,
+ true, UserHandle.SYSTEM);
+ assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app3"), apps);
+
+ apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS,
+ false, UserHandle.SYSTEM);
+ assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app3"), apps);
+ }
+
+ public void testQueryAppsInteractAcrossUser() {
+ List<String> apps = mAppsQueryHelper.queryApps(
+ AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM, true, UserHandle.SYSTEM);
+ assertEqualsIgnoreOrder(Arrays.asList("sys_app1"), apps);
+
+ apps = mAppsQueryHelper.queryApps(
+ AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM, false, UserHandle.SYSTEM);
+ assertEqualsIgnoreOrder(Arrays.asList("sys_app1"), apps);
+ }
+
+ public void testQueryApps() {
+ List<String> apps = mAppsQueryHelper.queryApps(
+ AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
+ |AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM,
+ true, UserHandle.SYSTEM);
+ assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app3"), apps);
+
+ apps = mAppsQueryHelper.queryApps(
+ AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
+ |AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM,
+ false, UserHandle.SYSTEM);
+ assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "sys_app3"), apps);
+ }
+
+ private class AppsQueryHelperTestable extends AppsQueryHelper {
+
+ public AppsQueryHelperTestable(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected List<ApplicationInfo> getAllApps(int userId) {
+ final ApplicationInfo ai1 = new ApplicationInfo();
+ ai1.flags |= ApplicationInfo.FLAG_SYSTEM;
+ ai1.packageName = "sys_app1";
+ final ApplicationInfo ai2 = new ApplicationInfo();
+ ai2.flags |= ApplicationInfo.FLAG_SYSTEM;
+ ai2.packageName = "sys_app2";
+ ai2.flags |= ApplicationInfo.FLAG_SYSTEM;
+ final ApplicationInfo ai3 = new ApplicationInfo();
+ ai3.packageName = "sys_app3";
+ ai3.flags |= ApplicationInfo.FLAG_SYSTEM;
+ final ApplicationInfo ai4 = new ApplicationInfo();
+ ai4.packageName = "app4";
+ return Arrays.asList(ai1, ai2, ai3, ai4);
+ }
+
+ @Override
+ protected List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int userId) {
+ assertEquals(Intent.CATEGORY_LAUNCHER, intent.getCategories().iterator().next());
+ final ResolveInfo r2 = new ResolveInfo();
+ r2.activityInfo = new ActivityInfo();
+ r2.activityInfo.packageName = "sys_app2";
+ r2.activityInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ final ResolveInfo r4 = new ResolveInfo();
+ r4.activityInfo = new ActivityInfo();
+ r4.activityInfo.packageName = "app4";
+ return Arrays.asList(r2, r4);
+ }
+
+ @Override
+ protected List<PackageInfo> getPackagesHoldingPermission(String perm, int userId) {
+ final PackageInfo p1 = new PackageInfo();
+ p1.packageName = "sys_app1";
+ p1.applicationInfo = new ApplicationInfo();
+ p1.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ return Arrays.asList(p1);
+ }
+ }
+
+ private static void assertEqualsIgnoreOrder(List<String> expected, List<String> actual) {
+ assertTrue("Lists not equal. Expected " + expected + " but was " + actual,
+ (expected.size() == actual.size())
+ && (new HashSet<>(expected).equals(new HashSet<>(actual))));
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
new file mode 100644
index 0000000..e5d5542
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextPaint;
+import android.text.style.SuggestionSpan;
+import android.text.style.TextAppearanceSpan;
+
+import com.android.frameworks.coretests.R;
+
+/**
+ * SuggestionsPopupWindowTest tests.
+ */
+public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2<TextViewActivity> {
+
+ public SuggestionsPopupWindowTest() {
+ super(TextViewActivity.class);
+ }
+
+ @SmallTest
+ public void testTextAndAppearanceInSuggestionsPopup() {
+ final Activity activity = getActivity();
+
+ final String sampleText = "abc def ghi";
+ final String[] candidate = {"DEF", "Def"};
+ final SuggestionSpan suggestionSpan = new SuggestionSpan(activity, candidate,
+ SuggestionSpan.FLAG_AUTO_CORRECTION);
+ final int spanStart = 4;
+ final int spanEnd = 7;
+ TextAppearanceSpan expectedSpan = new TextAppearanceSpan(activity,
+ android.R.style.TextAppearance_SuggestionHighlight);
+ TextPaint tmpTp = new TextPaint();
+ expectedSpan.updateDrawState(tmpTp);
+ final int expectedHighlightTextColor = tmpTp.getColor();
+ final float expectedHighlightTextSize = tmpTp.getTextSize();
+
+ // Create and wait until SuggestionsPopupWindow is shown.
+ final EditText editText = (EditText) activity.findViewById(R.id.textview);
+ final Editor editor = editText.getEditorForTesting();
+ assertNotNull(editor);
+ activity.runOnUiThread(new Runnable() {
+ public void run() {
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+ ssb.append(sampleText);
+ ssb.setSpan(suggestionSpan, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ editText.setText(ssb);
+
+ Selection.setSelection(editText.getText(), spanStart, spanEnd);
+ editText.onTextContextMenuItem(TextView.ID_REPLACE);
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+
+ // In this test, the SuggestionsPopupWindow looks like
+ // abc def ghi
+ // ----------
+ // | *DEF* |
+ // | *Def* |
+ // | DELETE |
+ // ----------
+ // *XX* means that XX is highlighted.
+ activity.runOnUiThread(new Runnable() {
+ public void run() {
+ Editor.SuggestionsPopupWindow popupWindow =
+ editor.getSuggestionsPopupWindowForTesting();
+ assertNotNull(popupWindow);
+
+ ListView listView = (ListView) popupWindow.getContentViewForTesting();
+ assertNotNull(listView);
+
+ int childNum = listView.getChildCount();
+ // +1 for "DELETE" command.
+ assertEquals(candidate.length + 1, childNum);
+
+ for (int i = 0; i < candidate.length; ++i) {
+ TextView textView = (TextView) listView.getChildAt(i);
+ assertNotNull(textView);
+
+ Spanned spanned = (Spanned) textView.getText();
+ assertNotNull(spanned);
+
+ // Check that the suggestion item order is kept.
+ assertEquals(candidate[i], spanned.toString());
+
+ // Check that the text is highlighted with correct color and text size.
+ TextAppearanceSpan[] taSpan = spanned.getSpans(0, candidate[i].length(),
+ TextAppearanceSpan.class);
+ assertEquals(1, taSpan.length);
+ TextPaint tp = new TextPaint();
+ taSpan[0].updateDrawState(tp);
+ assertEquals(expectedHighlightTextColor, tp.getColor());
+ assertEquals(expectedHighlightTextSize, tp.getTextSize());
+ }
+ }
+ });
+ }
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 350310c..27c6620 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -143,4 +143,7 @@
access while in power save mode, even if they aren't in the foreground. -->
<allow-in-power-save-except-idle package="com.android.providers.downloads" />
+ <!-- These are the packages that shouldn't run as system user -->
+ <system-user-blacklisted-app package="com.android.wallpaper.livepicker" />
+ <system-user-blacklisted-app package="com.android.settings" />
</permissions>
diff --git a/docs/html/images/training/auto-desktop-head-unit-context-menu-enabled.png b/docs/html/images/training/auto-desktop-head-unit-context-menu-enabled.png
new file mode 100644
index 0000000..99e60e9
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-context-menu-enabled.png
Binary files differ
diff --git a/docs/html/images/training/auto-desktop-head-unit-launch.png b/docs/html/images/training/auto-desktop-head-unit-launch.png
new file mode 100644
index 0000000..012857a4
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-launch.png
Binary files differ
diff --git a/docs/html/images/training/auto-desktop-head-unit-server-running.png b/docs/html/images/training/auto-desktop-head-unit-server-running.png
new file mode 100644
index 0000000..5aa3b83
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-server-running.png
Binary files differ
diff --git a/docs/html/images/training/auto-desktop-head-unit-wkst-launch.png b/docs/html/images/training/auto-desktop-head-unit-wkst-launch.png
new file mode 100644
index 0000000..4d48ac4
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-wkst-launch.png
Binary files differ
diff --git a/docs/html/tools/help/desktop-head-unit.jd b/docs/html/tools/help/desktop-head-unit.jd
new file mode 100644
index 0000000..981979c
--- /dev/null
+++ b/docs/html/tools/help/desktop-head-unit.jd
@@ -0,0 +1,439 @@
+page.title=Desktop Head Unit
+page.tags="auto", "car", "testing","dhu"
+@jd:body
+
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#launching-dhu">Launching the DHU</a></li>
+ <li><a href="#dhu-commands">Issuing DHU Commands</a></li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}training/auto/start/index.html#test-it-dhu">Run and Test Auto Apps</a></li>
+ </ol>
+
+</div>
+</div>
+
+
+<p>The Desktop Head Unit (DHU) enables your development machine to emulate an Android Auto
+head unit, so you can easily run and test Android Auto apps. The DHU runs on
+Windows, Mac, and Linux hosts and replaces previous Android Auto simulators,
+such as the Android Media Browser and Messaging
+simulators.</p>
+
+<p class="note"><strong>Note:</strong> For other information about testing Auto apps, see the
+training lesson
+<a href="{@docRoot}training/auto/start/index.html#test-it-dhu">Run and Test Auto Apps</a>. </p>
+
+
+<h2 id="launching-dhu">Launching the DHU</h2>
+
+<p>
+ To launch the DHU, run the <code>desktop-head-unit.exe</code> (on Windows)
+ or <code>desktop-head-unit</code> (on Mac or Linux) command, as described in
+ <a href="{@docRoot}training/auto/start/index.html#connecting-dhu">Connecting
+ the DHU to your mobile device</a>.
+</p>
+
+<p>
+ By default, the DHU emulates the most common form of Android Auto-compatible
+ head unit, which uses a touch screen user interface. You can simulate user
+ touches by clicking the DHU with a mouse. To emulate head units which use
+ a rotary controller for input, you can use the <code>-i controller</code> flag,
+ as in this example:
+</p>
+
+<pre class="no-pretty-print">$ ./desktop-head-unit -i controller</pre>
+
+<p>
+ When the DHU is in rotary-controller mode you can simulate controller
+ operations by using keyboard shortcuts, as described in <a href=
+ "#cmd-bindings">DHU commands and key bindings</a>. If the DHU is in rotary
+ controller mode, it ignores mouse clicks; you must operate Android Auto with
+ the simulated rotary controller operations.
+</p>
+
+<h2 id="dhu-commands">Issuing DHU Commands</h2>
+
+<p>
+ DHU commands allow you to test your app with Android Auto features, such as
+ playing voice input or switching between night and day display mode. You can issue commands to
+ the DHU by running commands from the terminal window where you launched DHU.
+ You can also issue commands by selecting the DHU window and
+ using keyboard shortcuts. The DHU commands
+ and key bindings for all controls are listed in <a href="#cmd-bindings">DHU
+ commands and key bindings</a>.
+</p>
+
+
+<h3 id="day-night">Switching between day and night mode</h3>
+
+<p>
+ Android Auto supports different color schemes for day and night. You should test your app in both
+ day and night mode. You can switch between night and day mode in either of the
+ following ways:
+</p>
+
+<ul>
+ <li>Run the command <code>daynight</code> in the terminal where you launched the DHU.
+ </li>
+
+ <li>Select the DHU window and press the <strong>N</strong> key.
+ </li>
+</ul>
+
+<h3>Microphone testing</h3>
+
+<p>The DHU supports using a microphone for voice input. You can also instruct the DHU to treat
+a pre-recorded voice track as input, as if the DHU had heard the track through the microphone.</p>
+
+<p>To use a pre-recorded sound file as input, enter this command: </p>
+<pre class="no-pretty-print">
+$ mic play <sound_file_path>/<sound_file>.wav
+</pre>
+
+<p>For your convenience, we have provided the following sound files for common
+voice commands. These sound files are installed in the
+<code><sdk>/extras/google/auto/voice/</code> directory.</p>
+
+<dl>
+ <dt>
+ <code>exitnav.wav</code>
+ </dt>
+
+ <dd>
+ "Exit navigation."
+ </dd>
+
+ <dt>
+ <code>navgoogle.wav</code>
+ </dt>
+
+ <dd>
+ "Navigate to 1600 Amphitheatre Parkway, Mountain View."
+ </dd>
+
+ <dt>
+ <code>navsoh.wav</code>
+ </dt>
+
+ <dd>
+ "Navigate to Sydney Opera House."
+ </dd>
+
+ <dt>
+ <code>nextturn.wav</code>
+ </dt>
+
+ <dd>
+ "When is my next turn?"
+ </dd>
+
+ <dt>
+ <code>showalternateroute.wav</code>
+ </dt>
+
+ <dd>
+ "Show alternate routes.""
+ </dd>
+
+ <dt>
+ <code>howlong.wav</code>
+ </dt>
+
+ <dd>
+ "How long until I get there?"
+ </dd>
+
+ <dt>
+ <code>navhome.wav</code>
+ </dt>
+
+ <dd>
+ "Navigate to home."
+ </dd>
+
+ <dt>
+ <code>navwork.wav</code>
+ </dt>
+
+ <dd>
+ "Navigate to work.""
+ </dd>
+
+ <dt>
+ <code>pause.wav</code>
+ </dt>
+
+ <dd>
+ "Pause music."
+ </dd>
+
+ <dt>
+ <code>showtraffic.wav</code>
+ </dt>
+
+ <dd>
+ "Show traffic."
+ </dd>
+</dl>
+<h3 id="cmd-bindings">DHU commands and key bindings</h3>
+
+<p>The DHU supports the following commands.</p>
+
+<p class="table-caption" id="table-commands"><strong>Table 1.</strong> Commands and key bindings</p>
+<table>
+<tr>
+ <th>Category</th>
+ <th>Command</th>
+ <th>Subcommand</th>
+ <th>Argument(s)</th>
+ <th>Keyboard Shortcut(s)</th>
+ <th>Description</th>
+</tr>
+
+<!--system-->
+
+<tr>
+<td rowspan="4">System</td>
+<td>help</td>
+<td></td>
+<td>[command]</td>
+<td></td>
+<td>Shows the full command set. Specifying a command name (for example, <code>help day</code>)
+ causes the system to show help for that command.</td>
+</tr>
+
+<tr>
+
+<td>quit</td>
+<td></td>
+<td></td>
+<td>Alt+q</td>
+<td>Quits the head unit.</td>
+</tr>
+
+<tr>
+
+<td>sleep</td>
+<td></td>
+<td>[seconds]</td>
+<td></td>
+<td>Sleeps for one second. Specifying an argument (for example, <code>sleep 30</code>) causes the
+system to sleep the specified number of seconds. This command
+is useful if you are writing scripts for the DHU. (You can run a script by using I/O redirection
+from the command line: <code>./desktop-head-unit < script.txt</code> loads commands from the
+file <code>script.txt</code>.)</td>
+</tr>
+
+<tr>
+
+<td>screenshot</td>
+<td></td>
+<td>filename.png</td>
+<td></td>
+<td>Saves a screenshot to <code>filename.png</code>.</td>
+</tr>
+
+
+<!--microphone-->
+
+<tr>
+<td rowspan="3">Microphone</td>
+<td rowspan="3">mic</td>
+<td>begin</td>
+<td></td>
+<td>m </td>
+<td>Activates the microphone (equivalent to clicking the steering wheel's microphone button) and
+waits for input from the computer microphone.</td>
+</tr>
+
+<tr>
+
+
+<td>play</td>
+<td>filename.wav</td>
+<td></td>
+<td>Causes the DHU to treat <code>filename.wav</code> as voice input, as if it had heard that sound
+ through the microphone. You do not hear the sound file being played, but you do hear
+ the response from Android Auto.</td>
+</tr>
+
+<tr>
+
+
+<td>repeat</td>
+<td></td>
+<td></td>
+<td>Repeats the last <code>mic play</code> command, as if you had called <code>mic play</code>
+ again with the same sound file parameter.</td>
+</tr>
+
+<!--Input-->
+
+<tr>
+<td rowspan="7">Input</td>
+<td rowspan="6">dpad</td>
+<td>up <br> down <br> left <br> right</td>
+<td></td>
+<td>Arrow keys</td>
+<td>Simulates moving the rotary controller.</td>
+</tr>
+
+<tr>
+
+
+<td>soft left <br> soft right</td>
+<td></td>
+<td>Shift+Arrow keys</td>
+<td>Simulates pressing the side buttons available on some rotary controllers.</td>
+</tr>
+
+<tr>
+
+
+<td>click</td>
+<td></td>
+<td>Return</td>
+<td>Simulates pressing the rotary controller.</td>
+</tr>
+
+<tr>
+
+
+<td>back</td>
+<td></td>
+<td>Backspace</td>
+<td>Simulates pressing the <strong>back</strong> button available below some rotary
+ controllers.</td>
+</tr>
+
+<tr>
+
+
+<td>rotate left <br> rotate right</td>
+<td></td>
+<td>1 <br> 2</td>
+<td>Simulates rotating the rotary controller left (counter-clockwise) or right (clockwise).</td>
+</tr>
+
+<tr>
+
+
+<td>flick left <br> flick right</td>
+<td></td>
+<td>Shift+1 <br> Shift+2</td>
+<td>Simulates a fast spin of the rotary controller to the left (counter-clockwise) or right
+ (clockwise).</td>
+</tr>
+
+<tr>
+
+<td>tap</td>
+<td></td>
+<td>x y</td>
+<td></td>
+<td>Simulates a touch event at the specified coordinates. For example, <code>tap 50 100</code></td>
+</tr>
+
+
+<!--Day/Night-->
+
+<tr>
+<td rowspan="3">Day/Night</td>
+<td>day</td>
+<td></td>
+<td></td>
+<td>Shift+n</td>
+<td>Activates day mode (high brightness, full color).</td>
+</tr>
+
+<tr>
+
+<td>night</td>
+<td></td>
+<td> </td>
+<td>Ctrl+n </td>
+<td>Activates night mode (low brightness, high contrast).</td>
+</tr>
+
+<tr>
+
+<td>daynight</td>
+<td></td>
+<td></td>
+<td>n </td>
+<td>Toggles current day/night mode.</td>
+</tr>
+
+</table>
+
+
+
+
+<h2 id="auto-simulators">Media Browser and Messaging Simulators</h2>
+
+<p class="caution"><strong>Important:</strong> Use of the Android Media Browser and Messaging
+Simulators for testing Android Auto apps is deprecated. Instead, we recommend using the
+Desktop Head Unit, which enables your development machine to act as if it were an Android Auto head
+unit.</p>
+
+<p>To get the simulators, open the
+<a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> and download
+them from <strong>Extras > Android Auto API Simulators</strong>.</p>
+
+<p>Before you begin testing, compile your app in your development environment.
+Install your app and the Android simulator for the features you want to test
+(that is, audio or messaging) on a physical or virtual device running Android
+5.0 (API level 21) or higher. To check the version of Android on the device, go
+to <strong>Settings > About phone</strong> (or <strong>About tablet</strong>)
+<strong>> Android Version</strong>.</p>
+
+<h3 id="testing-audio-apps">Testing audio apps</h3>
+<p>To run and test audio apps:</p>
+
+<ol>
+<li>Install the Android Media Browser simulator
+({@code <sdk>/extras/google/simulators/media-browser-simulator.apk}) on
+the test device. You can do this using
+the <a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
+<li>Enable <a href="{@docRoot}tools/device.html#developer-device-options">
+developer options</a> on the test device.</li>
+<li>Install your app on the test device.</li>
+<li>Launch the Android Media Browser simulator to see how your audio app
+appears in Auto. If your app does not appear, stop the simulator from
+<strong>Settings > Apps</strong> and restart it.</li>
+</ol>
+
+
+<h3 id="testing-messaging-apps">Testing messaging apps</h3>
+<p>To run and test messaging apps:</p>
+
+<ol>
+<li>Install the Android Messaging simulator
+ ({@code <sdk>/extras/google/simulators/messaging-simulator.apk})
+on the test device. You can do this using the
+<a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
+<li>Enable the simulator to read notifications posted on the system:
+<ol type="a">
+ <li>Enable <a href="{@docRoot}tools/device.html#developer-device-options">
+developer options</a> on the test device.</li>
+ <li>Click <strong>Settings > Sounds & Notifications > Notification
+ Access</strong> and check the box labeled
+ <strong>Messaging Simulator</strong>.</li>
+</ol>
+<li>Install your app on the test device.</li>
+<li>Launch the Android Messaging Simulator to see how your messaging app appears
+in Auto. If your app does not appear, stop the simulator from
+<strong>Settings > Apps</strong> and restart it.</li>
+</ol>
+
+
+
+
diff --git a/docs/html/tools/help/monkey.jd b/docs/html/tools/help/monkey.jd
index 941f5d9..7c64830 100644
--- a/docs/html/tools/help/monkey.jd
+++ b/docs/html/tools/help/monkey.jd
@@ -130,7 +130,7 @@
<td><code>--pct-majornav <percent></code></td>
<td>Adjust percentage of "major" navigation events.
(These are navigation events that will typically cause actions within your UI, such as
-the center button in a 5-way pad, the back key, or the menu key.)</td>
+the center button in a 5-way pad or the menu key.)</td>
</tr>
<tr>
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index db19d4f..72f9f21 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -160,6 +160,7 @@
<li><a href="<?cs var:toroot ?>tools/help/avd-manager.html">AVD Manager</a></li>
<li><a href="<?cs var:toroot ?>tools/help/bmgr.html">bmgr</a>
<li><a href="<?cs var:toroot ?>tools/help/monitor.html">Device Monitor</a></li>
+ <li><a href="<?cs var:toroot ?>tools/help/desktop-head-unit.html">Desktop Head Unit</a></li>
<li><a href="<?cs var:toroot ?>tools/help/dmtracedump.html">dmtracedump</a></li>
<li><a href="<?cs var:toroot ?>tools/help/draw9patch.html">Draw 9-Patch</a></li>
<li><a href="<?cs var:toroot ?>tools/help/emulator.html">Emulator</a></li>
diff --git a/docs/html/training/auto/start/index.jd b/docs/html/training/auto/start/index.jd
index 22e7521..f6cdbd1 100644
--- a/docs/html/training/auto/start/index.jd
+++ b/docs/html/training/auto/start/index.jd
@@ -16,7 +16,7 @@
<ol>
<li><a href="#dev-project">Set Up an Auto Project</a></li>
<li><a href="#build-it">Build Auto Apps</a></li>
- <li><a href="#test-it">Run and Test Auto Apps</a></li>
+ <li><a href="#test-it-dhu">Run and Test Auto Apps </a></li>
</ol>
<h2>You should also read</h2>
@@ -149,57 +149,124 @@
more information, see
<a href="{@docRoot}distribute/essentials/quality/auto.html">Auto App Quality</a>.</p>
-<h2 id="test-it">Run and Test Auto Apps</h2>
-<p>As you prepare to publish your app, make sure that your app looks correct
-when projected on the Auto user interface. Use the Android Media Browser
-simulator and Android Messaging simulators to view and test your audio or
-messaging apps in a screen that looks similar to what is projected on Auto.</p>
-<p>To get the simulators, open the
-<a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> and download
-them from <strong>Extras > Android Auto API Simulators</strong>.</p>
+<h2 id="test-it-dhu">Run and Test Auto Apps </h2>
-<p>Before you begin testing, compile your app in your development environment.
-Install your app and the Android simulator for the features you want to test
-(that is, audio or messaging) on a physical or virtual device running Android
-5.0 (API level 21) or higher. To check the version of Android on the device, go
-to <strong>Settings > About > Android Version</strong>.</p>
+<p>
+ As you develop, you can run and test your app on your development machine
+ using the <em>Desktop Head Unit</em> (DHU). The DHU replaces the existing
+ simulators and enables your development machine to simulate a vehicle
+ dashboard system running Android Auto.
+</p>
-<h3 id="testing-audio-apps">Testing audio apps</h3>
-<p>To run and test audio apps:</p>
+<h3 id="installing-dhu">Installing the DHU</h3>
<ol>
-<li>Install the Android Media Browser simulator
-({@code <sdk>/extras/google/simulators/media-browser-simulator.apk}) on
-the test device. You can do this using
-the <a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
-<li>Enable <a href="{@docRoot}tools/device.html#device-developer-options">
-developer options</a> on the test device.</li>
-<li>Install your app on the test device.</li>
-<li>Launch the Android Media Browser simulator to see how your audio app
-appears in Auto. If your app does not appear, stop the simulator from
-<strong>Settings > Apps</strong> then restart it.</li>
+ <li>Enable developer mode on your mobile device, as described in
+ <a href="{@docRoot}tools/device.html#developer-device-options">Enabling On-device
+ Developer Options</a>. </li>
+ <li>Compile your app in your development environment and install your app on
+ a physical mobile device running Android 5.0 (API level 21) or higher. To check the
+ version of Android on a Nexus device, go to
+ <strong>Settings > About phone</strong> (or <strong>About tablet</strong>) <strong>>
+ Android version</strong>.</li>
+
+ <li>Install the
+ <a class="external-link"
+ href="https://play.google.com/store/apps/details?id=com.google.android.projection.gearhead&hl=en"
+ >Android Auto app</a> on the mobile device.</li>
+ <li>Open the <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> and
+ download the DHU package <strong>Android Auto Desktop Head Unit</strong> from the
+ <em>SDK Tools</em> tab. The DHU installs in the <code><sdk>/extras/google/auto/</code>
+ directory.</li>
+ <li>If you are running the DHU on Linux, you must also install
+ the portaudio, libpng, sdl2, and sdl2_ttf libraries.
+ The procedure to do this varies depending on your Linux distribution. For example, on
+ Debian-derived Linux distributions, you can install the libraries with this command:
+
+<pre class="no-pretty-print">
+$ sudo apt-get install libsdl2-2.0-0 libsdl2-ttf-2.0-0 libportaudio2 libpng12-0
+</pre>
+
+ </li>
</ol>
-<h3 id="testing-messaging-apps">Testing messaging apps</h3>
-<p>To run and test messaging apps:</p>
+<div class="figure" style="width:330px">
+ <img src="{@docRoot}images/training/auto-desktop-head-unit-server-running.png"
+ alt="" >
+ <p class="img-caption">
+ <strong>Figure 2.</strong> Notification that the head unit server is running.
+ </p>
+</div>
+<img src="{@docRoot}images/training/auto-desktop-head-unit-context-menu-enabled.png"
+ alt="" >
+<p class="img-caption">
+ <strong>Figure 1.</strong> Context menu with developer options.
+</p>
+
+<h3 id="connecting-dhu">Connecting the DHU to your mobile device</h3>
+
+<p>Run the DHU by connecting your mobile device to a development machine and setting up a connection to
+ the head unit server over <a href="{@docRoot}tools/help/adb.html">Android Debug Bridge
+ (ADB)</a>. Follow these steps to set up tunneling and start the DHU:</p>
<ol>
-<li>Install the Android Messaging simulator
- ({@code <sdk>/extras/google/simulators/messaging-simulator.apk})
-on the test device. You can do this using the
-<a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
-<li>Enable the simulator to read notifications posted on the system:
-<ol type="a">
- <li>Enable <a href="{@docRoot}tools/device.html#device-developer-options">
-developer options</a> on the test device.</li>
- <li>Click <strong>Settings > Sounds & Notifications > Notification
- Access</strong> and check the box labeled
- <strong>Messaging Simulator</strong>.</li>
+ <li>On the mobile device, enable Android Auto developer mode by starting the Android Auto
+ companion app, and then tapping the header image 10 times.
+ This step is only required the first time you run the companion app.
+ </li>
+ <li>If the server is not already running, select <strong>Start head unit server</strong>
+ from the Android Auto menu.
+ <p>On the device, a foreground service appears in the notification area. </p>
+ </li>
+
+ <li>Connect the mobile device to the development machine via USB. Your device must be unlocked to
+ launch the DHU.
+ </li>
+ <li>On the development machine, run the following {@code adb} command to
+ forward socket connections from the
+ development machine's port 5277 to the same port number on the Android device.
+ This configuration allows the DHU to connect to the head unit server running on your phone over
+ a TCP socket.
+ <pre class="no-pretty-print">$ adb forward tcp:5277 tcp:5277</pre>
+ </li>
+
+ <li>Start the DHU by running the command <code>desktop-head-unit.exe</code> (on Windows)
+ or <code>./desktop-head-unit</code> (on Mac or Linux) from the
+ <code><sdk>/extras/google/auto/</code> directory.
+
+<pre class="no-pretty-print">$ cd <sdk>/extras/google/auto
+$ ./desktop-head-unit</pre>
+
+ <p>
+ By default, the head unit server connects over port 5277. To override the host or port
+ (for example, to forward over SSH), use the <code>--adb</code> flag.
+ </p>
+
+ </li>
</ol>
-<li>Install your app on the test device.</li>
-<li>Launch the Android Messaging Simulator to see how your messaging app appears
-in Auto. If your app does not appear, stop the simulator from
-<strong>Settings > Apps</strong> then restart it.</li>
-</ol>
+
+<div class="figure" style="width:432px">
+
+ <img src="{@docRoot}images/training/auto-desktop-head-unit-wkst-launch.png"
+ alt="" >
+ <p class="img-caption">
+ <strong>Figure 4.</strong> DHU launches on the development machine.
+ </p>
+</div>
+
+ <img src="{@docRoot}images/training/auto-desktop-head-unit-launch.png"
+ alt="" >
+ <p class="img-caption">
+ <strong>Figure 3.</strong> Android Auto launches on the mobile device.
+ </p>
+
+<p>
+ After you set up and start the DHU, you can run DHU commands from the command line to run and
+ test your app from the terminal. You can also run these commands by using keyboard shortcuts. For
+ more information about DHU configuration and commands, see <a href=
+ "{@docRoot}tools/help/desktop-head-unit.html">Desktop Head Unit</a>.
+</p>
+
+
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 32af59a..b95c183 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1146,77 +1146,11 @@
* document, tries to create a Drawable from that tag. Returns {@code null}
* if the tag is not a valid drawable.
*/
- @SuppressWarnings("deprecation")
public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
- final Drawable drawable;
-
- final String name = parser.getName();
- switch (name) {
- case "selector":
- drawable = new StateListDrawable();
- break;
- case "animated-selector":
- drawable = new AnimatedStateListDrawable();
- break;
- case "level-list":
- drawable = new LevelListDrawable();
- break;
- case "layer-list":
- drawable = new LayerDrawable();
- break;
- case "transition":
- drawable = new TransitionDrawable();
- break;
- case "ripple":
- drawable = new RippleDrawable();
- break;
- case "color":
- drawable = new ColorDrawable();
- break;
- case "shape":
- drawable = new GradientDrawable();
- break;
- case "vector":
- drawable = new VectorDrawable();
- break;
- case "animated-vector":
- drawable = new AnimatedVectorDrawable();
- break;
- case "scale":
- drawable = new ScaleDrawable();
- break;
- case "clip":
- drawable = new ClipDrawable();
- break;
- case "rotate":
- drawable = new RotateDrawable();
- break;
- case "animated-rotate":
- drawable = new AnimatedRotateDrawable();
- break;
- case "animation-list":
- drawable = new AnimationDrawable();
- break;
- case "inset":
- drawable = new InsetDrawable();
- break;
- case "bitmap":
- drawable = new BitmapDrawable();
- break;
- case "nine-patch":
- drawable = new NinePatchDrawable();
- break;
- default:
- throw new XmlPullParserException(parser.getPositionDescription() +
- ": invalid drawable tag " + name);
-
- }
- drawable.inflate(r, parser, attrs, theme);
- return drawable;
+ return r.getDrawableInflater().inflateFromXml(parser.getName(), parser, attrs, theme);
}
-
/**
* Create a drawable from file path name.
*/
diff --git a/graphics/java/android/graphics/drawable/DrawableInflater.java b/graphics/java/android/graphics/drawable/DrawableInflater.java
new file mode 100644
index 0000000..20d54ca
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/DrawableInflater.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.util.AttributeSet;
+import android.view.InflateException;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
+/**
+ * Instantiates a drawable XML file into its corresponding
+ * {@link android.graphics.drawable.Drawable} objects.
+ * <p>
+ * For performance reasons, inflation relies heavily on pre-processing of
+ * XML files that is done at build time. Therefore, it is not currently possible
+ * to use this inflater with an XmlPullParser over a plain XML file at runtime;
+ * it only works with an XmlPullParser returned from a compiled resource (R.
+ * <em>something</em> file.)
+ *
+ * @hide Pending API finalization.
+ */
+public final class DrawableInflater {
+ private static final HashMap<String, Constructor<? extends Drawable>> CONSTRUCTOR_MAP =
+ new HashMap<>();
+
+ private final Resources mRes;
+ private final ClassLoader mClassLoader;
+
+ /**
+ * Loads the drawable resource with the specified identifier.
+ *
+ * @param context the context in which the drawable should be loaded
+ * @param id the identifier of the drawable resource
+ * @return a drawable, or {@code null} if the drawable failed to load
+ */
+ @Nullable
+ public static Drawable loadDrawable(@NonNull Context context, @DrawableRes int id) {
+ return loadDrawable(context.getResources(), context.getTheme(), id);
+ }
+
+ /**
+ * Loads the drawable resource with the specified identifier.
+ *
+ * @param resources the resources from which the drawable should be loaded
+ * @param theme the theme against which the drawable should be inflated
+ * @param id the identifier of the drawable resource
+ * @return a drawable, or {@code null} if the drawable failed to load
+ */
+ @Nullable
+ public static Drawable loadDrawable(
+ @NonNull Resources resources, @Nullable Theme theme, @DrawableRes int id) {
+ return resources.getDrawable(id, theme);
+ }
+
+ /**
+ * Constructs a new drawable inflater using the specified resources and
+ * class loader.
+ *
+ * @param res the resources used to resolve resource identifiers
+ * @param classLoader the class loader used to load custom drawables
+ * @hide
+ */
+ public DrawableInflater(@NonNull Resources res, @NonNull ClassLoader classLoader) {
+ mRes = res;
+ mClassLoader = classLoader;
+ }
+
+ /**
+ * Inflates a drawable from inside an XML document using an optional
+ * {@link Theme}.
+ * <p>
+ * This method should be called on a parser positioned at a tag in an XML
+ * document defining a drawable resource. It will attempt to create a
+ * Drawable from the tag at the current position.
+ *
+ * @param name the name of the tag at the current position
+ * @param parser an XML parser positioned at the drawable tag
+ * @param attrs an attribute set that wraps the parser
+ * @param theme the theme against which the drawable should be inflated, or
+ * {@code null} to not inflate against a theme
+ * @return a drawable
+ *
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ @NonNull
+ public Drawable inflateFromXml(@NonNull String name, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws XmlPullParserException, IOException {
+ Drawable drawable = inflateFromTag(name);
+ if (drawable == null) {
+ drawable = inflateFromClass(name);
+ }
+ drawable.inflate(mRes, parser, attrs, theme);
+ return drawable;
+ }
+
+ @NonNull
+ @SuppressWarnings("deprecation")
+ private Drawable inflateFromTag(@NonNull String name) {
+ switch (name) {
+ case "selector":
+ return new StateListDrawable();
+ case "animated-selector":
+ return new AnimatedStateListDrawable();
+ case "level-list":
+ return new LevelListDrawable();
+ case "layer-list":
+ return new LayerDrawable();
+ case "transition":
+ return new TransitionDrawable();
+ case "ripple":
+ return new RippleDrawable();
+ case "color":
+ return new ColorDrawable();
+ case "shape":
+ return new GradientDrawable();
+ case "vector":
+ return new VectorDrawable();
+ case "animated-vector":
+ return new AnimatedVectorDrawable();
+ case "scale":
+ return new ScaleDrawable();
+ case "clip":
+ return new ClipDrawable();
+ case "rotate":
+ return new RotateDrawable();
+ case "animated-rotate":
+ return new AnimatedRotateDrawable();
+ case "animation-list":
+ return new AnimationDrawable();
+ case "inset":
+ return new InsetDrawable();
+ case "bitmap":
+ return new BitmapDrawable();
+ case "nine-patch":
+ return new NinePatchDrawable();
+ default:
+ return null;
+ }
+ }
+
+ @NonNull
+ private Drawable inflateFromClass(@NonNull String className) {
+ try {
+ Constructor<? extends Drawable> constructor;
+ synchronized (CONSTRUCTOR_MAP) {
+ constructor = CONSTRUCTOR_MAP.get(className);
+ if (constructor == null) {
+ final Class<? extends Drawable> clazz =
+ mClassLoader.loadClass(className).asSubclass(Drawable.class);
+ constructor = clazz.getConstructor();
+ CONSTRUCTOR_MAP.put(className, constructor);
+ }
+ }
+ return constructor.newInstance();
+ } catch (NoSuchMethodException e) {
+ final InflateException ie = new InflateException(
+ "Error inflating class " + className);
+ ie.initCause(e);
+ throw ie;
+ } catch (ClassCastException e) {
+ // If loaded class is not a Drawable subclass.
+ final InflateException ie = new InflateException(
+ "Class is not a Drawable " + className);
+ ie.initCause(e);
+ throw ie;
+ } catch (ClassNotFoundException e) {
+ // If loadClass fails, we should propagate the exception.
+ throw new InflateException("Class not found " + className);
+ } catch (Exception e) {
+ final InflateException ie = new InflateException(
+ "Error inflating class " + className);
+ ie.initCause(e);
+ throw ie;
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index d960c8f..f9474ef 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -48,8 +48,8 @@
// Software rendering properties.
private float mOpacity = 0;
- public RippleBackground(RippleDrawable owner, Rect bounds) {
- super(owner, bounds);
+ public RippleBackground(RippleDrawable owner, Rect bounds, boolean forceSoftware) {
+ super(owner, bounds, forceSoftware);
}
public boolean isVisible() {
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index 23a3ee3..2d378c6 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -52,9 +52,16 @@
/** Screen density used to adjust pixel-based constants. */
protected float mDensity;
- public RippleComponent(RippleDrawable owner, Rect bounds) {
+ /**
+ * If set, force all ripple animations to not run on RenderThread, even if it would be
+ * available.
+ */
+ private final boolean mForceSoftware;
+
+ public RippleComponent(RippleDrawable owner, Rect bounds, boolean forceSoftware) {
mOwner = owner;
mBounds = bounds;
+ mForceSoftware = forceSoftware;
}
public void onBoundsChange() {
@@ -143,7 +150,7 @@
* @return {@code true} if something was drawn, {@code false} otherwise
*/
public boolean draw(Canvas c, Paint p) {
- final boolean hasDisplayListCanvas = c.isHardwareAccelerated()
+ final boolean hasDisplayListCanvas = !mForceSoftware && c.isHardwareAccelerated()
&& c instanceof DisplayListCanvas;
if (mHasDisplayListCanvas != hasDisplayListCanvas) {
mHasDisplayListCanvas = hasDisplayListCanvas;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 464f3de..2690223 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -166,6 +166,12 @@
private boolean mOverrideBounds;
/**
+ * If set, force all ripple animations to not run on RenderThread, even if it would be
+ * available.
+ */
+ private boolean mForceSoftware;
+
+ /**
* Constructor used for drawable inflation.
*/
RippleDrawable() {
@@ -546,7 +552,7 @@
*/
private void tryBackgroundEnter(boolean focused) {
if (mBackground == null) {
- mBackground = new RippleBackground(this, mHotspotBounds);
+ mBackground = new RippleBackground(this, mHotspotBounds, mForceSoftware);
}
mBackground.setup(mState.mMaxRadius, mDensity);
@@ -584,7 +590,7 @@
}
final boolean isBounded = isBounded();
- mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded);
+ mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded, mForceSoftware);
}
mRipple.setup(mState.mMaxRadius, mDensity);
@@ -949,6 +955,16 @@
}
}
+ /**
+ * Sets whether to disable RenderThread animations for this ripple.
+ *
+ * @param forceSoftware true if RenderThread animations should be disabled, false otherwise
+ * @hide
+ */
+ public void setForceSoftware(boolean forceSoftware) {
+ mForceSoftware = forceSoftware;
+ }
+
@Override
public ConstantState getConstantState() {
return mState;
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 4853b04..c660846 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -87,8 +87,8 @@
private boolean mHasFinishedExit;
public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
- boolean isBounded) {
- super(owner, bounds);
+ boolean isBounded, boolean forceSoftware) {
+ super(owner, bounds, forceSoftware);
mIsBounded = isBounded;
mStartingX = startingX;
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
index f4943d5..bc2dae1 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
@@ -42,6 +42,8 @@
public static final String ACTIVITY_STILL = "android.activity_recognition.still";
public static final String ACTIVITY_TILTING = "android.activity_recognition.tilting";
+ // NOTE: when adding an additional EVENT_TYPE_, EVENT_TYPE_COUNT needs to be updated in
+ // android.hardware.location.ActivityRecognitionHardware
public static final int EVENT_TYPE_FLUSH_COMPLETE = 0;
public static final int EVENT_TYPE_ENTER = 1;
public static final int EVENT_TYPE_EXIT = 2;
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 0f1be6b..e92f294 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -671,15 +671,15 @@
case USAGE_VOICE_COMMUNICATION:
return new String("USAGE_VOICE_COMMUNICATION");
case USAGE_VOICE_COMMUNICATION_SIGNALLING:
- return new String("USAGE_VOICE_COMMUNICATION");
+ return new String("USAGE_VOICE_COMMUNICATION_SIGNALLING");
case USAGE_ALARM:
return new String("USAGE_ALARM");
case USAGE_NOTIFICATION:
return new String("USAGE_NOTIFICATION");
case USAGE_NOTIFICATION_RINGTONE:
- return new String("USAGE_NOTIFICATION");
+ return new String("USAGE_NOTIFICATION_RINGTONE");
case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
- return new String("USAGE_NOTIFICATION");
+ return new String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST");
case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
return new String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT");
case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index 7197dc0..e72bdb4 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -288,7 +288,6 @@
// fetch MidiDeviceInfo from the server
MidiDeviceInfo deviceInfo = server.getDeviceInfo();
device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken);
- sendOpenDeviceResponse(device, listenerF, handlerF);
} catch (RemoteException e) {
Log.e(TAG, "remote exception in getDeviceInfo()");
}
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index fbe047d..3cd157e 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -257,11 +257,12 @@
* on completion, and must be done by the caller.
*
* @param objectHandle handle of the target file
+ * @param size size of the file in bytes
* @param descriptor file descriptor to read the data from.
* @return true if the file transfer succeeds
*/
- public boolean sendObject(int objectHandle, ParcelFileDescriptor descriptor) {
- return native_send_object(objectHandle, descriptor.getFd());
+ public boolean sendObject(int objectHandle, int size, ParcelFileDescriptor descriptor) {
+ return native_send_object(objectHandle, size, descriptor.getFd());
}
/**
@@ -294,6 +295,6 @@
private native long native_get_storage_id(int objectHandle);
private native boolean native_import_file(int objectHandle, String destPath);
private native boolean native_import_file(int objectHandle, int fd);
- private native boolean native_send_object(int objectHandle, int fd);
+ private native boolean native_send_object(int objectHandle, int size, int fd);
private native MtpObjectInfo native_send_object_info(MtpObjectInfo info);
}
diff --git a/media/java/android/mtp/MtpObjectInfo.java b/media/java/android/mtp/MtpObjectInfo.java
index f79d52e..a080c73 100644
--- a/media/java/android/mtp/MtpObjectInfo.java
+++ b/media/java/android/mtp/MtpObjectInfo.java
@@ -273,25 +273,25 @@
public Builder(MtpObjectInfo objectInfo) {
mObjectInfo = new MtpObjectInfo();
mObjectInfo.mHandle = -1;
- mObjectInfo.mAssociationDesc = mObjectInfo.mAssociationDesc;
- mObjectInfo.mAssociationType = mObjectInfo.mAssociationType;
- mObjectInfo.mCompressedSize = mObjectInfo.mCompressedSize;
- mObjectInfo.mDateCreated = mObjectInfo.mDateCreated;
- mObjectInfo.mDateModified = mObjectInfo.mDateModified;
- mObjectInfo.mFormat = mObjectInfo.mFormat;
- mObjectInfo.mImagePixDepth = mObjectInfo.mImagePixDepth;
- mObjectInfo.mImagePixHeight = mObjectInfo.mImagePixHeight;
- mObjectInfo.mImagePixWidth = mObjectInfo.mImagePixWidth;
- mObjectInfo.mKeywords = mObjectInfo.mKeywords;
- mObjectInfo.mName = mObjectInfo.mName;
- mObjectInfo.mParent = mObjectInfo.mParent;
- mObjectInfo.mProtectionStatus = mObjectInfo.mProtectionStatus;
- mObjectInfo.mSequenceNumber = mObjectInfo.mSequenceNumber;
- mObjectInfo.mStorageId = mObjectInfo.mStorageId;
- mObjectInfo.mThumbCompressedSize = mObjectInfo.mThumbCompressedSize;
- mObjectInfo.mThumbFormat = mObjectInfo.mThumbFormat;
- mObjectInfo.mThumbPixHeight = mObjectInfo.mThumbPixHeight;
- mObjectInfo.mThumbPixWidth = mObjectInfo.mThumbPixWidth;
+ mObjectInfo.mAssociationDesc = objectInfo.mAssociationDesc;
+ mObjectInfo.mAssociationType = objectInfo.mAssociationType;
+ mObjectInfo.mCompressedSize = objectInfo.mCompressedSize;
+ mObjectInfo.mDateCreated = objectInfo.mDateCreated;
+ mObjectInfo.mDateModified = objectInfo.mDateModified;
+ mObjectInfo.mFormat = objectInfo.mFormat;
+ mObjectInfo.mImagePixDepth = objectInfo.mImagePixDepth;
+ mObjectInfo.mImagePixHeight = objectInfo.mImagePixHeight;
+ mObjectInfo.mImagePixWidth = objectInfo.mImagePixWidth;
+ mObjectInfo.mKeywords = objectInfo.mKeywords;
+ mObjectInfo.mName = objectInfo.mName;
+ mObjectInfo.mParent = objectInfo.mParent;
+ mObjectInfo.mProtectionStatus = objectInfo.mProtectionStatus;
+ mObjectInfo.mSequenceNumber = objectInfo.mSequenceNumber;
+ mObjectInfo.mStorageId = objectInfo.mStorageId;
+ mObjectInfo.mThumbCompressedSize = objectInfo.mThumbCompressedSize;
+ mObjectInfo.mThumbFormat = objectInfo.mThumbFormat;
+ mObjectInfo.mThumbPixHeight = objectInfo.mThumbPixHeight;
+ mObjectInfo.mThumbPixWidth = objectInfo.mThumbPixWidth;
}
public Builder setAssociationDesc(int value) {
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 9dd3861..2a46ee7 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -412,18 +412,13 @@
}
static jboolean
-android_mtp_MtpDevice_send_object(JNIEnv *env, jobject thiz, jint object_id, jint fd)
+android_mtp_MtpDevice_send_object(JNIEnv *env, jobject thiz, jint object_id, jint size, jint fd)
{
MtpDevice* device = get_device_from_object(env, thiz);
if (!device)
return JNI_FALSE;
- MtpObjectInfo* object_info = device->getObjectInfo(object_id);
- if (!object_info)
- return JNI_FALSE;
- bool result = device->sendObject(object_info, fd);
- delete object_info;
- return result;
+ return device->sendObject(object_id, size, fd);
}
static jobject
@@ -516,7 +511,7 @@
{"native_import_file", "(ILjava/lang/String;)Z",
(void *)android_mtp_MtpDevice_import_file},
{"native_import_file", "(II)Z",(void *)android_mtp_MtpDevice_import_file_to_fd},
- {"native_send_object", "(II)Z",(void *)android_mtp_MtpDevice_send_object},
+ {"native_send_object", "(III)Z",(void *)android_mtp_MtpDevice_send_object},
{"native_send_object_info", "(Landroid/mtp/MtpObjectInfo;)Landroid/mtp/MtpObjectInfo;",
(void *)android_mtp_MtpDevice_send_object_info}
};
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java
index fbde2b4..1f81a05 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java
@@ -46,6 +46,7 @@
device = mDeviceServerMap.get(bluetoothDevice);
if (device == null) {
device = new BluetoothMidiDevice(this, bluetoothDevice, this);
+ mDeviceServerMap.put(bluetoothDevice, device);
}
}
return device.getBinder();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BandSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/BandSelectManager.java
deleted file mode 100644
index 74170f5..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/BandSelectManager.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.documentsui;
-
-import static com.android.documentsui.Events.isMouseEvent;
-import static com.android.internal.util.Preconditions.checkState;
-
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.util.Log;
-import android.util.SparseBooleanArray;
-import android.view.MotionEvent;
-import android.view.View;
-
-/**
- * Provides mouse driven band-select support when used in conjuction with {@link RecyclerView} and
- * {@link MultiSelectManager}. This class is responsible for rendering the band select overlay and
- * selecting overlaid items via MultiSelectManager.
- */
-public class BandSelectManager extends RecyclerView.SimpleOnItemTouchListener {
-
- private static final int NOT_SELECTED = -1;
- private static final int NOT_SET = -1;
-
- // For debugging purposes.
- private static final String TAG = "BandSelectManager";
- private static final boolean DEBUG = false;
-
- private final RecyclerView mRecyclerView;
- private final MultiSelectManager mSelectManager;
- private final Drawable mRegionSelectorDrawable;
- private final SparseBooleanArray mSelectedByBand = new SparseBooleanArray();
-
- private boolean mIsBandSelectActive = false;
- private Point mOrigin;
- private Point mPointer;
- private Rect mBounds;
-
- // Maintain the last selection made by band, so if bounds shrink back, we can deselect
- // the respective items.
- private int mCursorDeltaY = 0;
- private int mFirstSelected = NOT_SELECTED;
-
- // The time at which the current band selection-induced scroll began. If no scroll is in
- // progress, the value is NOT_SET.
- private long mScrollStartTime = NOT_SET;
- private final Runnable mScrollRunnable = new Runnable() {
- /**
- * The number of milliseconds of scrolling at which scroll speed continues to increase. At
- * first, the scroll starts slowly; then, the rate of scrolling increases until it reaches
- * its maximum value at after this many milliseconds.
- */
- private static final long SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000;
-
- @Override
- public void run() {
- // Compute the number of pixels the pointer's y-coordinate is past the view. Negative
- // values mean the pointer is at or before the top of the view, and positive values mean
- // that the pointer is at or after the bottom of the view. Note that one additional
- // pixel is added here so that the view still scrolls when the pointer is exactly at the
- // top or bottom.
- int pixelsPastView = 0;
- if (mPointer.y <= 0) {
- pixelsPastView = mPointer.y - 1;
- } else if (mPointer.y >= mRecyclerView.getHeight() - 1) {
- pixelsPastView = mPointer.y - mRecyclerView.getHeight() + 1;
- }
-
- if (!mIsBandSelectActive || pixelsPastView == 0) {
- // If band selection is inactive, or if it is active but not at the edge of the
- // view, no scrolling is necessary.
- mScrollStartTime = NOT_SET;
- return;
- }
-
- if (mScrollStartTime == NOT_SET) {
- // If the pointer was previously not at the edge of the view but now is, set the
- // start time for the scroll.
- mScrollStartTime = System.currentTimeMillis();
- }
-
- // Compute the number of pixels to scroll, and scroll that many pixels.
- final int numPixels = computeNumPixelsToScroll(
- pixelsPastView, System.currentTimeMillis() - mScrollStartTime);
- mRecyclerView.scrollBy(0, numPixels);
-
- // Adjust the y-coordinate of the origin the opposite number of pixels so that the
- // origin remains in the same place relative to the view's items.
- mOrigin.y -= numPixels;
- resizeBandSelectRectangle();
-
- mRecyclerView.removeCallbacks(mScrollRunnable);
- mRecyclerView.postOnAnimation(this);
- }
-
- /**
- * Computes the number of pixels to scroll based on how far the pointer is past the end of
- * the view and how long it has been there. Roughly based on ItemTouchHelper's algorithm for
- * computing the number of pixels to scroll when an item is dragged to the end of a
- * {@link RecyclerView}.
- * @param pixelsPastView
- * @param scrollDuration
- * @return
- */
- private int computeNumPixelsToScroll(int pixelsPastView, long scrollDuration) {
- final int maxScrollStep = computeMaxScrollStep(mRecyclerView);
- final int direction = (int) Math.signum(pixelsPastView);
- final int absPastView = Math.abs(pixelsPastView);
-
- // Calculate the ratio of how far out of the view the pointer currently resides to the
- // entire height of the view.
- final float outOfBoundsRatio = Math.min(
- 1.0f, (float) absPastView / mRecyclerView.getHeight());
- // Interpolate this ratio and use it to compute the maximum scroll that should be
- // possible for this step.
- final float cappedScrollStep =
- direction * maxScrollStep * smoothOutOfBoundsRatio(outOfBoundsRatio);
-
- // Likewise, calculate the ratio of the time spent in the scroll to the limit.
- final float timeRatio = Math.min(
- 1.0f, (float) scrollDuration / SCROLL_ACCELERATION_LIMIT_TIME_MS);
- // Interpolate this ratio and use it to compute the final number of pixels to scroll.
- final int numPixels = (int) (cappedScrollStep * smoothTimeRatio(timeRatio));
-
- // If the final number of pixels to scroll ends up being 0, the view should still scroll
- // at least one pixel.
- return numPixels != 0 ? numPixels : direction;
- }
-
- /**
- * Computes the maximum scroll allowed for a given animation frame. Currently, this
- * defaults to the height of the view, but this could be tweaked if this results in scrolls
- * that are too fast or too slow.
- * @param rv
- * @return
- */
- private int computeMaxScrollStep(RecyclerView rv) {
- return rv.getHeight();
- }
-
- /**
- * Interpolates the given out of bounds ratio on a curve which starts at (0,0) and ends at
- * (1,1) and quickly approaches 1 near the start of that interval. This ensures that drags
- * that are at the edge or barely past the edge of the view still cause sufficient
- * scrolling. The equation y=(x-1)^5+1 is used, but this could also be tweaked if needed.
- * @param ratio A ratio which is in the range [0, 1].
- * @return A "smoothed" value, also in the range [0, 1].
- */
- private float smoothOutOfBoundsRatio(float ratio) {
- return (float) Math.pow(ratio - 1.0f, 5) + 1.0f;
- }
-
- /**
- * Interpolates the given time ratio on a curve which starts at (0,0) and ends at (1,1) and
- * stays close to 0 for most input values except those very close to 1. This ensures that
- * scrolls start out very slowly but speed up drastically after the scroll has been in
- * progress close to SCROLL_ACCELERATION_LIMIT_TIME_MS. The equation y=x^5 is used, but this
- * could also be tweaked if needed.
- * @param ratio A ratio which is in the range [0, 1].
- * @return A "smoothed" value, also in the range [0, 1].
- */
- private float smoothTimeRatio(float ratio) {
- return (float) Math.pow(ratio, 5);
- }
- };
-
- /**
- * @param recyclerView
- * @param multiSelectManager
- */
- public BandSelectManager(RecyclerView recyclerView, MultiSelectManager multiSelectManager) {
- mRecyclerView = recyclerView;
- mSelectManager = multiSelectManager;
- mRegionSelectorDrawable =
- mRecyclerView.getContext().getTheme().getDrawable(R.drawable.band_select_overlay);
-
- mRecyclerView.addOnItemTouchListener(this);
- }
-
- @Override
- public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
- // Only intercept the event if it was triggered by a mouse. If band select is inactive,
- // do not intercept ACTION_UP events as they will not be processed.
- return isMouseEvent(e) &&
- (mIsBandSelectActive || e.getActionMasked() != MotionEvent.ACTION_UP);
- }
-
- @Override
- public void onTouchEvent(RecyclerView rv, MotionEvent e) {
- checkState(isMouseEvent(e));
- processMotionEvent(e);
- }
-
- /**
- * Processes a MotionEvent by starting, ending, or resizing the band select overlay.
- * @param e
- */
- private void processMotionEvent(MotionEvent e) {
- if (mIsBandSelectActive && e.getActionMasked() == MotionEvent.ACTION_UP) {
- endBandSelect();
- return;
- }
-
- mPointer = new Point((int) e.getX(), (int) e.getY());
- if (!mIsBandSelectActive) {
- startBandSelect();
- }
-
- scrollViewIfNecessary();
- resizeBandSelectRectangle();
- selectChildrenCoveredBySelection();
- }
-
- /**
- * Starts band select by adding the drawable to the RecyclerView's overlay.
- */
- private void startBandSelect() {
- if (DEBUG) Log.d(TAG, "Starting band select from (" + mPointer.x + "," + mPointer.y + ").");
- mIsBandSelectActive = true;
- mOrigin = mPointer;
- mRecyclerView.getOverlay().add(mRegionSelectorDrawable);
- }
-
- /**
- * Scrolls the view if necessary.
- */
- private void scrollViewIfNecessary() {
- mRecyclerView.removeCallbacks(mScrollRunnable);
- mScrollRunnable.run();
- mRecyclerView.invalidate();
- }
-
- /**
- * Resizes the band select rectangle by using the origin and the current pointer positoin as
- * two opposite corners of the selection.
- */
- private void resizeBandSelectRectangle() {
- if (mBounds != null) {
- mCursorDeltaY = mPointer.y - mBounds.bottom;
- }
-
- mBounds = new Rect(Math.min(mOrigin.x, mPointer.x),
- Math.min(mOrigin.y, mPointer.y),
- Math.max(mOrigin.x, mPointer.x),
- Math.max(mOrigin.y, mPointer.y));
-
- mRegionSelectorDrawable.setBounds(mBounds);
- }
-
- /**
- * Selects the children covered by the band select overlay by delegating to MultiSelectManager.
- * TODO: Provide a finished implementation. This is down and dirty, proof of concept code.
- * Final optimized implementation, with support for managing offscreen selection to come.
- */
- private void selectChildrenCoveredBySelection() {
-
- // track top and bottom selections. Details on why this is useful below.
- int first = NOT_SELECTED;
- int last = NOT_SELECTED;
-
- for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
-
- View child = mRecyclerView.getChildAt(i);
- ViewHolder holder = mRecyclerView.getChildViewHolder(child);
- Rect childRect = new Rect();
- child.getHitRect(childRect);
-
- boolean shouldSelect = Rect.intersects(childRect, mBounds);
- int position = holder.getAdapterPosition();
-
- // This also allows us to clear the selection of elements
- // that only temporarily entered the bounds of the band.
- if (mSelectedByBand.get(position) && !shouldSelect) {
- mSelectManager.setItemSelected(position, false);
- mSelectedByBand.delete(position);
- }
-
- // We need to keep track of the first and last items selected.
- // We'll use this information along with cursor direction
- // to determine the starting point of the selection.
- // We provide this information to selection manager
- // to enable more natural user interaction when working
- // with Shift+Click and multiple contiguous selection ranges.
- if (shouldSelect) {
- if (first == NOT_SELECTED) {
- first = position;
- } else {
- last = position;
- }
- mSelectManager.setItemSelected(position, true);
- mSelectedByBand.put(position, true);
- }
- }
-
- // Remember which is the last selected item, so we can
- // share that with selection manager when band select ends.
- // It'll use that as it's begin selection point when
- // user SHIFT+Clicks.
- if (mCursorDeltaY < 0 && last != NOT_SELECTED) {
- mFirstSelected = last;
- } else if (mCursorDeltaY > 0 && first != NOT_SELECTED) {
- mFirstSelected = first;
- }
- }
-
- /**
- * Ends band select by removing the overlay.
- */
- private void endBandSelect() {
- if (DEBUG) Log.d(TAG, "Ending band select.");
- mIsBandSelectActive = false;
- mSelectedByBand.clear();
- mRecyclerView.getOverlay().remove(mRegionSelectorDrawable);
- if (mFirstSelected != NOT_SELECTED) {
- mSelectManager.setSelectionFocusBegin(mFirstSelected);
- }
- }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BandSelectMatrix.java b/packages/DocumentsUI/src/com/android/documentsui/BandSelectMatrix.java
deleted file mode 100644
index f259059..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/BandSelectMatrix.java
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * Copyright (C) 2013 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;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import com.google.common.base.Preconditions;
-
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.GridLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.LayoutManager;
-import android.support.v7.widget.RecyclerView.OnScrollListener;
-import android.util.SparseBooleanArray;
-import android.view.View;
-
-/**
- * Provides a band selection item model for views within a RecyclerView. This class queries the
- * RecyclerView to determine where its items are placed; then, once band selection is underway, it
- * alerts listeners of which items are covered by the selections.
- */
-public final class BandSelectMatrix extends RecyclerView.OnScrollListener {
-
- private final RecyclerViewHelper mHelper;
- private final List<OnSelectionChangedListener> mOnSelectionChangedListeners = new ArrayList<>();
-
- // Map from the x-value of the left side of an item to an ordered list of metadata of all items
- // whose x-values are the same. The list is ordered by the y-values of the items in the column.
- // For example, if the first column of the view starts at an x-value of 5, mColumns.get(5) would
- // return a list of all items in that column, with the top-most item first in the list and the
- // bottom-most item last in the list.
- private final Map<Integer, List<ItemData>> mColumns = new HashMap<>();
-
- // List of limits along the x-axis. For example, if the view has two columns, this list will
- // have two elements, each of which lists the lower- and upper-limits of the x-values of the
- // view items. This list is sorted from furthest left to furthest right.
- private final List<Limits> mXLimitsList = new ArrayList<>();
-
- // Like mXLimitsList, but for y-coordinates. Note that this list only contains items which have
- // been in the viewport. Thus, limits which exist in an area of the view to which the view has
- // not scrolled are not present in the list.
- private final List<Limits> mYLimitsList = new ArrayList<>();
-
- // The adapter positions which have been recorded so far.
- private final SparseBooleanArray mRecordedPositions = new SparseBooleanArray();
-
- // Array passed to registered OnSelectionChangedListeners. One array is created and reused
- // throughout the lifetime of the object.
- private final SparseBooleanArray mSelectionForListeners = new SparseBooleanArray();
-
- // The current pointer (in absolute positioning from the top of the view).
- private Point mPointer = null;
-
- // The bounds of the band selection.
- private RelativePoint mRelativeOrigin;
- private RelativePoint mRelativePointer;
-
- BandSelectMatrix(RecyclerViewHelper helper) {
- mHelper = helper;
- mHelper.addOnScrollListener(this);
- }
-
- BandSelectMatrix(RecyclerView rv) {
- this(new RuntimeRecyclerViewHelper(rv));
- }
-
- /**
- * Stops listening to the view's scrolls. Call this function before discarding a
- * BandSelecMatrix object to prevent memory leaks.
- */
- void stopListening() {
- mHelper.removeOnScrollListener(this);
- }
-
- /**
- * Start a band select operation at the given point.
- * @param relativeOrigin The origin of the band select operation, relative to the viewport.
- * For example, if the view is scrolled to the bottom, the top-left of the viewport would
- * have a relative origin of (0, 0), even though its absolute point has a higher y-value.
- */
- void startSelection(Point relativeOrigin) {
- Point absoluteOrigin = mHelper.createAbsolutePoint(relativeOrigin);
- mPointer = new Point(absoluteOrigin.x, absoluteOrigin.y);
-
- processVisibleChildren();
- mRelativeOrigin = new RelativePoint(absoluteOrigin);
- mRelativePointer = new RelativePoint(mPointer);
- computeCurrentSelection();
- notifyListeners();
- }
-
- /**
- * Resizes the selection by adjusting the pointer (i.e., the corner of the selection opposite
- * the origin.
- * @param relativePointer The pointer (opposite of the origin) of the band select operation,
- * relative to the viewport. For example, if the view is scrolled to the bottom, the
- * top-left of the viewport would have a relative origin of (0, 0), even though its absolute
- * point has a higher y-value.
- */
- void resizeSelection(Point relativePointer) {
- mPointer = mHelper.createAbsolutePoint(relativePointer);
- handlePointerMoved();
- }
-
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- if (mPointer == null) {
- return;
- }
-
- mPointer.x += dx;
- mPointer.y += dy;
- processVisibleChildren();
- handlePointerMoved();
- }
-
- /**
- * Queries the view for all children and records their location metadata.
- */
- private void processVisibleChildren() {
- for (int i = 0; i < mHelper.getVisibleChildCount(); i++) {
- int adapterPosition = mHelper.getAdapterPositionAt(i);
- if (!mRecordedPositions.get(adapterPosition)) {
- mRecordedPositions.put(adapterPosition, true);
- captureItemLayoutData(mHelper.getAbsoluteRectForChildViewAt(i), adapterPosition);
- }
- }
- }
-
- /**
- * Updates the limits lists and column map with the given item metadata.
- * @param absoluteChildRect The absolute rectangle for the child view being processed.
- * @param adapterPosition The position of the child view being processed.
- */
- private void captureItemLayoutData(Rect absoluteChildRect, int adapterPosition) {
- if (mXLimitsList.size() != mHelper.getNumColumns()) {
- // If not all x-limits have been recorded, record this one.
- recordLimits(
- mXLimitsList, new Limits(absoluteChildRect.left, absoluteChildRect.right));
- }
-
- if (mYLimitsList.size() != mHelper.getNumRows()) {
- // If not all y-limits have been recorded, record this one.
- recordLimits(
- mYLimitsList, new Limits(absoluteChildRect.top, absoluteChildRect.bottom));
- }
-
- List<ItemData> columnList = mColumns.get(absoluteChildRect.left);
- if (columnList == null) {
- columnList = new ArrayList<ItemData>();
- mColumns.put(absoluteChildRect.left, columnList);
- }
- recordItemData(
- columnList, new ItemData(adapterPosition, absoluteChildRect.top));
- }
-
- /**
- * Ensures limits exists within the sorted list limitsList, and adds it to the list if it does
- * not exist.
- */
- private static void recordLimits(List<Limits> limitsList, Limits limits) {
- int index = Collections.binarySearch(limitsList, limits);
- if (index < 0) {
- limitsList.add(~index, limits);
- }
- }
-
- /**
- * Ensures itemData exists within the sorted list itemDataList, and adds it to the list if it
- * does not exist.
- */
- private static void recordItemData(List<ItemData> itemDataList, ItemData itemData) {
- int index = Collections.binarySearch(itemDataList, itemData);
- if (index < 0) {
- itemDataList.add(~index, itemData);
- }
- }
-
- /**
- * Handles a moved pointer; this function determines whether the pointer movement resulted in a
- * selection change and, if it has, notifies listeners of this change.
- */
- private void handlePointerMoved() {
- RelativePoint old = mRelativePointer;
- mRelativePointer = new RelativePoint(mPointer);
- if (old != null && mRelativePointer.equals(old)) {
- return;
- }
-
- computeCurrentSelection();
- notifyListeners();
- }
-
- /**
- * Computes the currently-selected items.
- */
- private void computeCurrentSelection() {
- Rect selectionRect = mRelativePointer.computeBounds(mRelativeOrigin);
- computePositionsCoveredByRect(selectionRect);
- }
-
- /**
- * Notifies all listeners of a selection change. Note that this function simply passes
- * mSelectionForListeners, so computeCurrentSelection() should be called before this function.
- */
- private void notifyListeners() {
- for (OnSelectionChangedListener listener : mOnSelectionChangedListeners) {
- listener.onSelectionChanged(mSelectionForListeners);
- }
- }
-
- /**
- * @param rect Rectangle including all covered items.
- */
- private void computePositionsCoveredByRect(@Nullable Rect rect) {
- mSelectionForListeners.clear();
- if (rect == null) {
- // If there is no bounding rectangle, there are no items selected, so just return early.
- return;
- }
-
- int columnIndex = Collections.binarySearch(mXLimitsList, new Limits(rect.left, rect.left));
- Preconditions.checkState(columnIndex >= 0);
-
- for (; columnIndex < mXLimitsList.size() &&
- mXLimitsList.get(columnIndex).lowerLimit <= rect.right; columnIndex++) {
- List<ItemData> positions =
- mColumns.get(mXLimitsList.get(columnIndex).lowerLimit);
- int rowIndex = Collections.binarySearch(positions, new ItemData(0, rect.top));
- if (rowIndex < 0) {
- // If band select occurs after the last item in a row with fewer items than columns,
- // go to the next column. This situation occurs in the last row of the grid when the
- // total number of items is not a multiple of the number of columns (e.g., when 10
- // items exist in a grid with 4 columns).
- continue;
- }
-
- for (; rowIndex < positions.size() &&
- positions.get(rowIndex).offset <= rect.bottom; rowIndex++) {
- mSelectionForListeners.append(positions.get(rowIndex).position, true);
- }
- }
- }
-
- /**
- * Provides functionality for interfacing with the view. In practice, RecyclerViewMatrixHelper
- * should be used; this interface exists solely for the purpose of decoupling the view from
- * this class so that the view can be mocked out for tests.
- */
- interface RecyclerViewHelper {
- public void addOnScrollListener(RecyclerView.OnScrollListener listener);
- public void removeOnScrollListener(RecyclerView.OnScrollListener listener);
- public Point createAbsolutePoint(Point relativePoint);
- public int getVisibleChildCount();
- public int getTotalChildCount();
- public int getNumColumns();
- public int getNumRows();
- public int getAdapterPositionAt(int index);
- public Rect getAbsoluteRectForChildViewAt(int index);
- }
-
- /**
- * Concrete MatrixHelper implementation for use within the Files app.
- */
- static class RuntimeRecyclerViewHelper implements RecyclerViewHelper {
- private final RecyclerView mRecyclerView;
-
- RuntimeRecyclerViewHelper(RecyclerView rv) {
- mRecyclerView = rv;
- }
-
- @Override
- public int getAdapterPositionAt(int index) {
- View child = mRecyclerView.getChildAt(index);
- return mRecyclerView.getChildViewHolder(child).getAdapterPosition();
- }
-
- @Override
- public void addOnScrollListener(OnScrollListener listener) {
- mRecyclerView.addOnScrollListener(listener);
- }
-
- @Override
- public void removeOnScrollListener(OnScrollListener listener) {
- mRecyclerView.removeOnScrollListener(listener);
- }
-
- @Override
- public Point createAbsolutePoint(Point relativePoint) {
- return new Point(relativePoint.x + mRecyclerView.computeHorizontalScrollOffset(),
- relativePoint.y + mRecyclerView.computeVerticalScrollOffset());
- }
-
- @Override
- public Rect getAbsoluteRectForChildViewAt(int index) {
- final View child = mRecyclerView.getChildAt(index);
- final Rect childRect = new Rect();
- child.getHitRect(childRect);
- childRect.left += mRecyclerView.computeHorizontalScrollOffset();
- childRect.right += mRecyclerView.computeHorizontalScrollOffset();
- childRect.top += mRecyclerView.computeVerticalScrollOffset();
- childRect.bottom += mRecyclerView.computeVerticalScrollOffset();
- return childRect;
- }
-
- @Override
- public int getVisibleChildCount() {
- return mRecyclerView.getChildCount();
- }
-
- @Override
- public int getTotalChildCount() {
- return mRecyclerView.getAdapter().getItemCount();
- }
-
- @Override
- public int getNumColumns() {
- LayoutManager layoutManager = mRecyclerView.getLayoutManager();
- if (layoutManager instanceof GridLayoutManager) {
- return ((GridLayoutManager) layoutManager).getSpanCount();
- }
-
- // Otherwise, it is a list with 1 column.
- return 1;
- }
-
- @Override
- public int getNumRows() {
- int numFullColumns = getTotalChildCount() / getNumColumns();
- boolean hasPartiallyFullColumn = getTotalChildCount() % getNumColumns() != 0;
- return numFullColumns + (hasPartiallyFullColumn ? 1 : 0);
- }
- }
-
- /**
- * Listener for changes in which items have been band selected.
- */
- interface OnSelectionChangedListener {
- public void onSelectionChanged(SparseBooleanArray updatedSelection);
- }
-
- void addOnSelectionChangedListener(OnSelectionChangedListener listener) {
- mOnSelectionChangedListeners.add(listener);
- }
-
- void removeOnSelectionChangedListener(OnSelectionChangedListener listener) {
- mOnSelectionChangedListeners.remove(listener);
- }
-
- /**
- * Metadata for an item in the view, consisting of the adapter position and the offset from the
- * top of the view (in pixels). Stored in the mColumns map to model the item grid.
- */
- private static class ItemData implements Comparable<ItemData> {
- int position;
- int offset;
-
- ItemData(int position, int offset) {
- this.position = position;
- this.offset = offset;
- }
-
- @Override
- public int compareTo(ItemData other) {
- // The list of columns is sorted via the offset from the top, so PositionMetadata
- // objects with lower y-values are befor those with higher y-values.
- return offset - other.offset;
- }
- }
-
- /**
- * Limits of a view item. For example, if an item's left side is at x-value 5 and its right side
- * is at x-value 10, the limits would be from 5 to 10. Used to record the left- and right sides
- * of item columns and the top- and bottom sides of item rows so that it can be determined
- * whether the pointer is located within the bounds of an item.
- */
- private static class Limits implements Comparable<Limits> {
- int lowerLimit;
- int upperLimit;
-
- Limits(int lowerLimit, int upperLimit) {
- this.lowerLimit = lowerLimit;
- this.upperLimit = upperLimit;
- }
-
- @Override
- public int compareTo(Limits other) {
- return lowerLimit - other.lowerLimit;
- }
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof Limits)) {
- return false;
- }
-
- return ((Limits) other).lowerLimit == lowerLimit &&
- ((Limits) other).upperLimit == upperLimit;
- }
- }
-
- /**
- * The location of a coordinate relative to items. This class represents a general area of the
- * view as it relates to band selection rather than an explicit point. For example, two
- * different points within an item are considered to have the same "location" because band
- * selection originating within the item would select the same items no matter which point
- * was used. Same goes for points between items as well as those at the very beginning or end
- * of the view.
- *
- * Tracking a coordinate (e.g., an x-value) as a CoordinateLocation instead of as an int has the
- * advantage of tying the value to the Limits of items along that axis. This allows easy
- * selection of items within those Limits as opposed to a search through every item to see if a
- * given coordinate value falls within those Limits.
- */
- private static class RelativeCoordinate
- implements Comparable<RelativeCoordinate> {
- /**
- * Location describing points after the last known item.
- */
- static final int AFTER_LAST_ITEM = 0;
-
- /**
- * Location describing points before the first known item.
- */
- static final int BEFORE_FIRST_ITEM = 1;
-
- /**
- * Location describing points between two items.
- */
- static final int BETWEEN_TWO_ITEMS = 2;
-
- /**
- * Location describing points within the limits of one item.
- */
- static final int WITHIN_LIMITS = 3;
-
- /**
- * The type of this coordinate, which is one of AFTER_LAST_ITEM, BEFORE_FIRST_ITEM,
- * BETWEEN_TWO_ITEMS, or WITHIN_LIMITS.
- */
- final int type;
-
- /**
- * The limits before the coordinate; only populated when type == WITHIN_LIMITS or type ==
- * BETWEEN_TWO_ITEMS.
- */
- Limits limitsBeforeCoordinate;
-
- /**
- * The limits after the coordinate; only populated when type == BETWEEN_TWO_ITEMS.
- */
- Limits limitsAfterCoordinate;
-
- // Limits of the first known item; only populated when type == BEFORE_FIRST_ITEM.
- Limits mFirstKnownItem;
- // Limits of the last known item; only populated when type == AFTER_LAST_ITEM.
- Limits mLastKnownItem;
-
- /**
- * @param limitsList The sorted limits list for the coordinate type. If this
- * CoordinateLocation is an x-value, mXLimitsList should be passed; otherwise,
- * mYLimitsList should be pased.
- * @param value The coordinate value.
- */
- RelativeCoordinate(List<Limits> limitsList, int value) {
- Limits dummyLimits = new Limits(value, value);
- int index = Collections.binarySearch(limitsList, dummyLimits);
-
- if (index >= 0) {
- this.type = WITHIN_LIMITS;
- this.limitsBeforeCoordinate = limitsList.get(index);
- } else if (~index == 0) {
- this.type = BEFORE_FIRST_ITEM;
- this.mFirstKnownItem = limitsList.get(0);
- } else if (~index == limitsList.size()) {
- Limits lastLimits = limitsList.get(limitsList.size() - 1);
- if (lastLimits.lowerLimit <= value && value <= lastLimits.upperLimit) {
- this.type = WITHIN_LIMITS;
- this.limitsBeforeCoordinate = lastLimits;
- } else {
- this.type = AFTER_LAST_ITEM;
- this.mLastKnownItem = lastLimits;
- }
- } else {
- Limits limitsBeforeIndex = limitsList.get(~index - 1);
- if (limitsBeforeIndex.lowerLimit <= value && value <= limitsBeforeIndex.upperLimit) {
- this.type = WITHIN_LIMITS;
- this.limitsBeforeCoordinate = limitsList.get(~index - 1);
- } else {
- this.type = BETWEEN_TWO_ITEMS;
- this.limitsBeforeCoordinate = limitsList.get(~index - 1);
- this.limitsAfterCoordinate = limitsList.get(~index);
- }
- }
- }
-
- int toComparisonValue() {
- if (type == BEFORE_FIRST_ITEM) {
- return mFirstKnownItem.lowerLimit - 1;
- } else if (type == AFTER_LAST_ITEM) {
- return mLastKnownItem.upperLimit + 1;
- } else if (type == BETWEEN_TWO_ITEMS) {
- return limitsBeforeCoordinate.upperLimit + 1;
- } else {
- return limitsBeforeCoordinate.lowerLimit;
- }
- }
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof RelativeCoordinate)) {
- return false;
- }
-
- RelativeCoordinate otherCoordinate = (RelativeCoordinate) other;
- return toComparisonValue() == otherCoordinate.toComparisonValue();
- }
-
- @Override
- public int compareTo(RelativeCoordinate other) {
- return toComparisonValue() - other.toComparisonValue();
- }
- }
-
- /**
- * The location of a point relative to the Limits of nearby items; consists of both an x- and
- * y-RelativeCoordinateLocation.
- */
- private class RelativePoint {
- final RelativeCoordinate xLocation;
- final RelativeCoordinate yLocation;
-
- RelativePoint(Point point) {
- this.xLocation = new RelativeCoordinate(mXLimitsList, point.x);
- this.yLocation = new RelativeCoordinate(mYLimitsList, point.y);
- }
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof RelativePoint)) {
- return false;
- }
-
- RelativePoint otherPoint = (RelativePoint) other;
- return xLocation.equals(otherPoint.xLocation) && yLocation.equals(otherPoint.yLocation);
- }
-
- /**
- * Generates a rectangle which contains the items selected by the two points.
- * @param other The other PointLocation. A rectangle will be formed between "this" and
- * "other".
- * @return The rectangle, or null if no items were selected.
- */
- Rect computeBounds(RelativePoint other) {
- if (!areItemsCoveredBySelection(mRelativePointer, mRelativeOrigin)) {
- return null;
- }
-
- RelativeCoordinate minXLocation =
- xLocation.compareTo(other.xLocation) < 0 ? xLocation : other.xLocation;
- RelativeCoordinate maxXLocation =
- minXLocation == xLocation ? other.xLocation : xLocation;
- RelativeCoordinate minYLocation =
- yLocation.compareTo(other.yLocation) < 0 ? yLocation : other.yLocation;
- RelativeCoordinate maxYLocation =
- minYLocation == yLocation ? other.yLocation : yLocation;
-
- Rect rect = new Rect();
- rect.left = getCoordinateValue(minXLocation, mXLimitsList, true);
- rect.right = getCoordinateValue(maxXLocation, mXLimitsList, false);
- rect.top = getCoordinateValue(minYLocation, mYLimitsList, true);
- rect.bottom = getCoordinateValue(maxYLocation, mYLimitsList, false);
- return rect;
- }
-
- int getCoordinateValue(RelativeCoordinate coordinate,
- List<Limits> limitsList, boolean isStartOfRange) {
- switch (coordinate.type) {
- case RelativeCoordinate.BEFORE_FIRST_ITEM:
- return limitsList.get(0).lowerLimit;
- case RelativeCoordinate.AFTER_LAST_ITEM:
- return limitsList.get(limitsList.size() - 1).upperLimit;
- case RelativeCoordinate.BETWEEN_TWO_ITEMS:
- if (isStartOfRange) {
- return coordinate.limitsAfterCoordinate.lowerLimit;
- } else {
- return coordinate.limitsBeforeCoordinate.upperLimit;
- }
- case RelativeCoordinate.WITHIN_LIMITS:
- return coordinate.limitsBeforeCoordinate.lowerLimit;
- }
-
- throw new RuntimeException("Invalid coordinate value.");
- }
- }
-
- private static boolean areItemsCoveredBySelection(
- RelativePoint first, RelativePoint second) {
- return doesCoordinateLocationCoverItems(first.xLocation, second.xLocation) &&
- doesCoordinateLocationCoverItems(first.yLocation, second.yLocation);
- }
-
- private static boolean doesCoordinateLocationCoverItems(
- RelativeCoordinate pointerCoordinate,
- RelativeCoordinate originCoordinate) {
- if (pointerCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM &&
- originCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM) {
- return false;
- }
-
- if (pointerCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM &&
- originCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM) {
- return false;
- }
-
- if (pointerCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS &&
- originCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS &&
- pointerCoordinate.limitsBeforeCoordinate.equals(originCoordinate) &&
- pointerCoordinate.limitsAfterCoordinate.equals(originCoordinate)) {
- return false;
- }
-
- return true;
- }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 240cdda..c28806b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -1773,8 +1773,6 @@
}
@Override
- public void afterActivityCreated(DirectoryFragment fragment) {
- new BandSelectManager(fragment.mRecView, fragment.mSelectionManager);
- }
+ public void afterActivityCreated(DirectoryFragment fragment) {}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
index f87fe4c..e972566 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
@@ -16,24 +16,33 @@
package com.android.documentsui;
+import static com.android.documentsui.Events.isMouseEvent;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkState;
+import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.Adapter;
import android.support.v7.widget.RecyclerView.AdapterDataObserver;
+import android.support.v7.widget.RecyclerView.LayoutManager;
+import android.support.v7.widget.RecyclerView.OnScrollListener;
import android.util.Log;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.view.GestureDetector;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
-
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.support.annotation.VisibleForTesting;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -61,12 +70,13 @@
private final List<MultiSelectManager.Callback> mCallbacks = new ArrayList<>(1);
private Adapter<?> mAdapter;
- private RecyclerViewHelper mHelper;
+ private MultiSelectHelper mHelper;
private boolean mSingleSelect;
+ private BandSelectManager mBandSelectManager;
/**
* @param recyclerView
- * @param gestureDelegate Option delage gesture listener.
+ * @param gestureDelegate Option delegate gesture listener.
* @param mode Selection mode
* @template A gestureDelegate that implements both {@link OnGestureListener}
* and {@link OnDoubleTapListener}
@@ -76,17 +86,11 @@
this(
recyclerView.getAdapter(),
- new RecyclerViewHelper() {
- @Override
- public int findEventPosition(MotionEvent e) {
- View view = recyclerView.findChildViewUnder(e.getX(), e.getY());
- return view != null
- ? recyclerView.getChildAdapterPosition(view)
- : RecyclerView.NO_POSITION;
- }
- },
+ new RuntimeRecyclerViewHelper(recyclerView),
mode);
+ mBandSelectManager = new BandSelectManager((RuntimeRecyclerViewHelper) mHelper);
+
GestureDetector.SimpleOnGestureListener listener =
new GestureDetector.SimpleOnGestureListener() {
@Override
@@ -101,11 +105,8 @@
CompositeOnGestureListener<? extends Object> compositeListener =
new CompositeOnGestureListener<>(listener, gestureDelegate);
- final GestureDetector detector = new GestureDetector(
- recyclerView.getContext(),
- gestureDelegate == null
- ? listener
- : compositeListener);
+ final GestureDetector detector =
+ new GestureDetector(recyclerView.getContext(), compositeListener);
detector.setOnDoubleTapListener(compositeListener);
@@ -113,9 +114,15 @@
new RecyclerView.OnItemTouchListener() {
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
detector.onTouchEvent(e);
- return false;
+
+ // Only intercept the event if it was a mouse-based band selection.
+ return isMouseEvent(e) && (mBandSelectManager.mIsActive ||
+ e.getActionMasked() != MotionEvent.ACTION_UP);
}
- public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
+ public void onTouchEvent(RecyclerView rv, MotionEvent e) {
+ checkState(isMouseEvent(e));
+ mBandSelectManager.processMotionEvent(e);
+ }
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
});
}
@@ -125,7 +132,7 @@
* @hide
*/
@VisibleForTesting
- MultiSelectManager(Adapter<?> adapter, RecyclerViewHelper helper, int mode) {
+ MultiSelectManager(Adapter<?> adapter, MultiSelectHelper helper, int mode) {
checkNotNull(adapter, "'adapter' cannot be null.");
checkNotNull(helper, "'helper' cannot be null.");
@@ -207,6 +214,7 @@
* @param selected
* @return True if the selection state of the item changed.
*/
+ @VisibleForTesting
public boolean setItemSelected(int position, boolean selected) {
if (mSingleSelect && !mSelection.isEmpty()) {
clearSelectionQuietly();
@@ -271,7 +279,7 @@
if (DEBUG) Log.i(TAG, "View is null. Cannot handle tap event.");
}
- onLongPress(position);
+ onLongPress(position, e.getMetaState());
}
/**
@@ -279,17 +287,16 @@
* can be mocked.
*
* @param position
+ * @param metaState as returned from {@link MotionEvent#getMetaState()}.
* @hide
*/
@VisibleForTesting
- void onLongPress(int position) {
+ void onLongPress(int position, int metaState) {
if (position == RecyclerView.NO_POSITION) {
if (DEBUG) Log.i(TAG, "View is null. Cannot handle tap event.");
}
- if (toggleSelection(position)) {
- notifySelectionChanged();
- }
+ handlePositionChanged(position, metaState);
}
/**
@@ -327,17 +334,26 @@
return false;
}
+ handlePositionChanged(position, metaState);
+ return false;
+ }
+
+ /**
+ * Handles a change caused by a click on the item with the given position. If the Shift key is
+ * held down, this performs a range select; otherwise, it simply toggles the item's selection
+ * state.
+ */
+ private void handlePositionChanged(int position, int metaState) {
if (Events.hasShiftBit(metaState) && mRanger != null) {
mRanger.snapSelection(position);
- } else {
- toggleSelection(position);
- }
- // We're being lazy here notifying even when something might not have changed.
- // To make this more correct, we'd need to update the Ranger class to return
- // information about what has changed.
- notifySelectionChanged();
- return false;
+ // We're being lazy here notifying even when something might not have changed.
+ // To make this more correct, we'd need to update the Ranger class to return
+ // information about what has changed.
+ notifySelectionChanged();
+ } else if (toggleSelection(position)) {
+ notifySelectionChanged();
+ }
}
/**
@@ -588,10 +604,23 @@
*/
public static final class Selection {
- private SparseBooleanArray mSelection;
+ // This class tracks selected positions by managing two arrays: the saved selection, and
+ // the total selection. Saved selections are those which have been completed by tapping an
+ // item or by completing a band select operation. Provisional selections are selections
+ // which have been temporarily created by an in-progress band select operation (once the
+ // user releases the mouse button during a band select operation, the selected items
+ // become saved). The total selection is the combination of both the saved selection and
+ // the provisional selection. Tracking both separately is necessary to ensure that saved
+ // selections do not become deselected when they are removed from the provisional selection;
+ // for example, if item A is tapped (and selected), then an in-progress band select covers A
+ // then uncovers A, A should still be selected as it has been saved. To ensure this
+ // behavior, the saved selection must be tracked separately.
+ private SparseBooleanArray mSavedSelection;
+ private SparseBooleanArray mTotalSelection;
public Selection() {
- mSelection = new SparseBooleanArray();
+ mSavedSelection = new SparseBooleanArray();
+ mTotalSelection = new SparseBooleanArray();
}
/**
@@ -599,7 +628,7 @@
* @return true if the position is currently selected.
*/
public boolean contains(int position) {
- return mSelection.get(position);
+ return mTotalSelection.get(position);
}
/**
@@ -613,21 +642,85 @@
* @return the position value stored at specified index.
*/
public int get(int index) {
- return mSelection.keyAt(index);
+ return mTotalSelection.keyAt(index);
}
/**
* @return size of the selection.
*/
public int size() {
- return mSelection.size();
+ return mTotalSelection.size();
}
/**
* @return true if the selection is empty.
*/
public boolean isEmpty() {
- return mSelection.size() == 0;
+ return mTotalSelection.size() == 0;
+ }
+
+ /**
+ * Sets the provisional selection, which is a temporary selection that can be saved,
+ * canceled, or adjusted at a later time. When a new provision selection is applied, the old
+ * one (if it exists) is abandoned.
+ * @return Array with entry for each position added or removed. Entries which were added
+ * contain a value of true, and entries which were removed contain a value of false.
+ */
+ @VisibleForTesting
+ protected SparseBooleanArray setProvisionalSelection(
+ SparseBooleanArray provisionalSelection) {
+ SparseBooleanArray delta = new SparseBooleanArray();
+
+ for (int i = 0; i < mTotalSelection.size(); i++) {
+ int position = mTotalSelection.keyAt(i);
+ if (!provisionalSelection.get(position) && !mSavedSelection.get(position)) {
+ // Remove each item that used to be in the selection but is unsaved and not in
+ // the new provisional selection.
+ delta.put(position, false);
+ }
+ }
+
+ for (int i = 0; i < provisionalSelection.size(); i++) {
+ int position = provisionalSelection.keyAt(i);
+ if (!mTotalSelection.get(position)) {
+ // Add each item that was not previously in the selection but is in the
+ // new provisional selection.
+ delta.put(position, true);
+ }
+ }
+
+ // Now, iterate through the changes and actually add/remove them to/from
+ // mCurrentSelection. This could not be done in the previous loops because changing the
+ // size of the selection mid-iteration changes iteration order erroneously.
+ for (int i = 0; i < delta.size(); i++) {
+ int position = delta.keyAt(i);
+ if (delta.get(position)) {
+ mTotalSelection.put(position, true);
+ } else {
+ mTotalSelection.delete(position);
+ }
+ }
+
+ return delta;
+ }
+
+ /**
+ * Saves the existing provisional selection. Once the provisional selection is saved,
+ * subsequent provisional selections which are different from this existing one cannot
+ * cause items in this existing provisional selection to become deselected.
+ */
+ @VisibleForTesting
+ protected void applyProvisionalSelection() {
+ mSavedSelection = mTotalSelection.clone();
+ }
+
+ /**
+ * Abandons the existing provisional selection so that all items provisionally selected are
+ * now deselected.
+ */
+ @VisibleForTesting
+ protected void cancelProvisionalSelection() {
+ mTotalSelection = mSavedSelection.clone();
}
private boolean flip(int position) {
@@ -643,8 +736,9 @@
/** @hide */
@VisibleForTesting
boolean add(int position) {
- if (!mSelection.get(position)) {
- mSelection.put(position, true);
+ if (!mTotalSelection.get(position)) {
+ mTotalSelection.put(position, true);
+ mSavedSelection.put(position, true);
return true;
}
return false;
@@ -653,8 +747,9 @@
/** @hide */
@VisibleForTesting
boolean remove(int position) {
- if (mSelection.get(position)) {
- mSelection.delete(position);
+ if (mTotalSelection.get(position)) {
+ mTotalSelection.delete(position);
+ mSavedSelection.delete(position);
return true;
}
return false;
@@ -663,7 +758,8 @@
/**
* Adjusts the selection range to reflect the existence of newly inserted values at
* the specified positions. This has the effect of adjusting all existing selected
- * positions within the specified range accordingly.
+ * positions within the specified range accordingly. Note that this function discards any
+ * provisional selections which may have been applied.
*
* @param startPosition
* @param count
@@ -673,11 +769,13 @@
void expand(int startPosition, int count) {
checkState(startPosition >= 0);
checkState(count > 0);
+ cancelProvisionalSelection();
- for (int i = 0; i < mSelection.size(); i++) {
- int itemPosition = mSelection.keyAt(i);
+ for (int i = 0; i < mTotalSelection.size(); i++) {
+ int itemPosition = mTotalSelection.keyAt(i);
if (itemPosition >= startPosition) {
- mSelection.setKeyAt(i, itemPosition + count);
+ mTotalSelection.setKeyAt(i, itemPosition + count);
+ mSavedSelection.setKeyAt(i, itemPosition + count);
}
}
}
@@ -685,7 +783,8 @@
/**
* Adjusts the selection range to reflect the removal specified positions. This has
* the effect of adjusting all existing selected positions within the specified range
- * accordingly.
+ * accordingly. Note that this function discards any provisional selections which may have
+ * been applied.
*
* @param startPosition
* @param count The length of the range to collapse. Must be greater than 0.
@@ -699,27 +798,30 @@
int endPosition = startPosition + count - 1;
SparseBooleanArray newSelection = new SparseBooleanArray();
- for (int i = 0; i < mSelection.size(); i++) {
- int itemPosition = mSelection.keyAt(i);
+ for (int i = 0; i < mSavedSelection.size(); i++) {
+ int itemPosition = mSavedSelection.keyAt(i);
if (itemPosition < startPosition) {
newSelection.append(itemPosition, true);
} else if (itemPosition > endPosition) {
newSelection.append(itemPosition - count, true);
}
}
- mSelection = newSelection;
+ mSavedSelection = newSelection;
+ cancelProvisionalSelection();
}
/** @hide */
@VisibleForTesting
void clear() {
- mSelection.clear();
+ mSavedSelection.clear();
+ mTotalSelection.clear();
}
/** @hide */
@VisibleForTesting
void copyFrom(Selection source) {
- mSelection = source.mSelection.clone();
+ mSavedSelection = source.mSavedSelection.clone();
+ mTotalSelection = source.mTotalSelection.clone();
}
@Override
@@ -728,16 +830,16 @@
return "size=0, items=[]";
}
- StringBuilder buffer = new StringBuilder(mSelection.size() * 28);
+ StringBuilder buffer = new StringBuilder(mTotalSelection.size() * 28);
buffer.append("{size=")
- .append(mSelection.size())
+ .append(mTotalSelection.size())
.append(", ")
.append("items=[");
- for (int i=0; i < mSelection.size(); i++) {
+ for (int i=0; i < mTotalSelection.size(); i++) {
if (i > 0) {
buffer.append(", ");
}
- buffer.append(mSelection.keyAt(i));
+ buffer.append(mTotalSelection.keyAt(i));
}
buffer.append("]}");
return buffer.toString();
@@ -745,7 +847,7 @@
@Override
public int hashCode() {
- return mSelection.hashCode();
+ return mSavedSelection.hashCode() ^ mTotalSelection.hashCode();
}
@Override
@@ -758,14 +860,178 @@
return false;
}
- return mSelection.equals(((Selection) that).mSelection);
+ return mSavedSelection.equals(((Selection) that).mSavedSelection) &&
+ mTotalSelection.equals(((Selection) that).mTotalSelection);
}
}
- interface RecyclerViewHelper {
+ /**
+ * Provides functionality for MultiSelectManager. In practice, use RuntimeRecyclerViewHelper;
+ * this interface exists only for mocking in tests.
+ */
+ interface MultiSelectHelper {
int findEventPosition(MotionEvent e);
}
+ /**
+ * Provides functionality for BandSelectManager. In practice, use RuntimeRecyclerViewHelper;
+ * this interface exists only for mocking in tests.
+ */
+ interface BandManagerHelper {
+ void drawBand(Rect rect);
+ void addOnScrollListener(RecyclerView.OnScrollListener listener);
+ int findEventPosition(MotionEvent e);
+ int getHeight();
+ void hideBand();
+ void invalidateView();
+ void postRunnable(Runnable r);
+ void removeCallback(Runnable r);
+ void scrollBy(int dy);
+ }
+
+ /**
+ * Provides functionality for BandSelectModel. In practice, use RuntimeRecyclerViewHelper;
+ * this interface exists only for mocking in tests.
+ */
+ interface BandModelHelper {
+ void addOnScrollListener(RecyclerView.OnScrollListener listener);
+ Point createAbsolutePoint(Point relativePoint);
+ Rect getAbsoluteRectForChildViewAt(int index);
+ int getAdapterPositionAt(int index);
+ int getNumColumns();
+ int getNumRows();
+ int getTotalChildCount();
+ int getVisibleChildCount();
+ void removeOnScrollListener(RecyclerView.OnScrollListener listener);
+ }
+
+ /**
+ * Concrete RecyclerViewHelper implementation for use within the Files app.
+ */
+ private static final class RuntimeRecyclerViewHelper implements MultiSelectHelper,
+ BandManagerHelper, BandModelHelper {
+
+ private final RecyclerView mRecyclerView;
+ private final Drawable mBandSelectRect;
+
+ private boolean mIsOverlayShown = false;
+
+ RuntimeRecyclerViewHelper(RecyclerView rv) {
+ mRecyclerView = rv;
+ mBandSelectRect = mRecyclerView.getContext().getTheme().getDrawable(
+ R.drawable.band_select_overlay);
+ }
+
+ @Override
+ public int getAdapterPositionAt(int index) {
+ View child = mRecyclerView.getChildAt(index);
+ return mRecyclerView.getChildViewHolder(child).getAdapterPosition();
+ }
+
+ @Override
+ public void addOnScrollListener(OnScrollListener listener) {
+ mRecyclerView.addOnScrollListener(listener);
+ }
+
+ @Override
+ public void removeOnScrollListener(OnScrollListener listener) {
+ mRecyclerView.removeOnScrollListener(listener);
+ }
+
+ @Override
+ public Point createAbsolutePoint(Point relativePoint) {
+ return new Point(relativePoint.x + mRecyclerView.computeHorizontalScrollOffset(),
+ relativePoint.y + mRecyclerView.computeVerticalScrollOffset());
+ }
+
+ @Override
+ public Rect getAbsoluteRectForChildViewAt(int index) {
+ final View child = mRecyclerView.getChildAt(index);
+ final Rect childRect = new Rect();
+ child.getHitRect(childRect);
+ childRect.left += mRecyclerView.computeHorizontalScrollOffset();
+ childRect.right += mRecyclerView.computeHorizontalScrollOffset();
+ childRect.top += mRecyclerView.computeVerticalScrollOffset();
+ childRect.bottom += mRecyclerView.computeVerticalScrollOffset();
+ return childRect;
+ }
+
+ @Override
+ public int getVisibleChildCount() {
+ return mRecyclerView.getChildCount();
+ }
+
+ @Override
+ public int getTotalChildCount() {
+ return mRecyclerView.getAdapter().getItemCount();
+ }
+
+ @Override
+ public int getNumColumns() {
+ LayoutManager layoutManager = mRecyclerView.getLayoutManager();
+ if (layoutManager instanceof GridLayoutManager) {
+ return ((GridLayoutManager) layoutManager).getSpanCount();
+ }
+
+ // Otherwise, it is a list with 1 column.
+ return 1;
+ }
+
+ @Override
+ public int getNumRows() {
+ int numFullColumns = getTotalChildCount() / getNumColumns();
+ boolean hasPartiallyFullColumn = getTotalChildCount() % getNumColumns() != 0;
+ return numFullColumns + (hasPartiallyFullColumn ? 1 : 0);
+ }
+
+ @Override
+ public int findEventPosition(MotionEvent e) {
+ View view = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
+ return view != null
+ ? mRecyclerView.getChildAdapterPosition(view)
+ : RecyclerView.NO_POSITION;
+ }
+
+ @Override
+ public int getHeight() {
+ return mRecyclerView.getHeight();
+ }
+
+ @Override
+ public void invalidateView() {
+ mRecyclerView.invalidate();
+ }
+
+ @Override
+ public void postRunnable(Runnable r) {
+ mRecyclerView.postOnAnimation(r);
+ }
+
+ @Override
+ public void removeCallback(Runnable r) {
+ mRecyclerView.removeCallbacks(r);
+ }
+
+ @Override
+ public void scrollBy(int dy) {
+ mRecyclerView.scrollBy(0, dy);
+ }
+
+ @Override
+ public void drawBand(Rect rect) {
+ mBandSelectRect.setBounds(rect);
+
+ if (!mIsOverlayShown) {
+ mRecyclerView.getOverlay().add(mBandSelectRect);
+ }
+ }
+
+ @Override
+ public void hideBand() {
+ mRecyclerView.getOverlay().remove(mBandSelectRect);
+ }
+ }
+
public interface Callback {
/**
* Called when an item is selected or unselected while in selection mode.
@@ -893,4 +1159,846 @@
return false;
}
}
+
+ /**
+ * Provides mouse driven band-select support when used in conjunction with {@link RecyclerView}
+ * and {@link MultiSelectManager}. This class is responsible for rendering the band select
+ * overlay and selecting overlaid items via MultiSelectManager.
+ */
+ public class BandSelectManager extends RecyclerView.OnScrollListener
+ implements BandSelectModel.OnSelectionChangedListener {
+
+ private static final int NOT_SET = -1;
+
+ private final BandManagerHelper mHelper;
+
+ private boolean mIsActive;
+ private Point mOrigin;
+ private Point mPointer;
+ private Rect mBounds;
+ private BandSelectModel mModel;
+
+ // The time at which the current band selection-induced scroll began. If no scroll is in
+ // progress, the value is NOT_SET.
+ private long mScrollStartTime = NOT_SET;
+ private final Runnable mViewScroller = new ViewScroller();
+
+ public <T extends BandManagerHelper & BandModelHelper>
+ BandSelectManager(T helper) {
+ mHelper = helper;
+ mHelper.addOnScrollListener(this);
+ mModel = new BandSelectModel(helper);
+ mModel.addOnSelectionChangedListener(this);
+ }
+
+ /**
+ * Processes a MotionEvent by starting, ending, or resizing the band select overlay.
+ * @param e
+ */
+ private void processMotionEvent(MotionEvent e) {
+ if (!isMouseEvent(e)) {
+ return;
+ }
+
+ if (mIsActive && e.getActionMasked() == MotionEvent.ACTION_UP) {
+ endBandSelect();
+ return;
+ }
+
+ mPointer = new Point((int) e.getX(), (int) e.getY());
+ if (!mIsActive) {
+ // Only start a band select if the pointer is in margins between items, not
+ // actually within an item's bounds.
+ if (mHelper.findEventPosition(e) != RecyclerView.NO_POSITION) {
+ return;
+ }
+ startBandSelect();
+ } else {
+ mModel.resizeSelection(mPointer);
+ }
+
+ scrollViewIfNecessary();
+ resizeBandSelectRectangle();
+ }
+
+ /**
+ * Starts band select by adding the drawable to the RecyclerView's overlay.
+ */
+ private void startBandSelect() {
+ if (DEBUG) {
+ Log.d(TAG, "Starting band select from (" + mPointer.x + "," + mPointer.y + ").");
+ }
+ mIsActive = true;
+ mOrigin = new Point(mPointer.x, mPointer.y);
+ mModel.startSelection(mOrigin);
+ }
+
+ /**
+ * Scrolls the view if necessary.
+ */
+ private void scrollViewIfNecessary() {
+ mHelper.removeCallback(mViewScroller);
+ mViewScroller.run();
+ mHelper.invalidateView();
+ }
+
+ /**
+ * Resizes the band select rectangle by using the origin and the current pointer position as
+ * two opposite corners of the selection.
+ */
+ private void resizeBandSelectRectangle() {
+ mBounds = new Rect(Math.min(mOrigin.x, mPointer.x),
+ Math.min(mOrigin.y, mPointer.y),
+ Math.max(mOrigin.x, mPointer.x),
+ Math.max(mOrigin.y, mPointer.y));
+ mHelper.drawBand(mBounds);
+ }
+
+ /**
+ * Ends band select by removing the overlay.
+ */
+ private void endBandSelect() {
+ if (DEBUG) Log.d(TAG, "Ending band select.");
+ mIsActive = false;
+ mHelper.hideBand();
+ mSelection.applyProvisionalSelection();
+ mModel.endSelection();
+ int firstSelected = mModel.getPositionNearestOrigin();
+ if (firstSelected != BandSelectModel.NOT_SET) {
+ setSelectionFocusBegin(firstSelected);
+ }
+ }
+
+ @Override
+ public void onSelectionChanged(SparseBooleanArray updatedSelection) {
+ SparseBooleanArray delta = mSelection.setProvisionalSelection(updatedSelection);
+ for (int i = 0; i < delta.size(); i++) {
+ int position = delta.keyAt(i);
+ notifyItemStateChanged(position, delta.get(position));
+ }
+ notifySelectionChanged();
+ }
+
+ private class ViewScroller implements Runnable {
+ /**
+ * The number of milliseconds of scrolling at which scroll speed continues to increase.
+ * At first, the scroll starts slowly; then, the rate of scrolling increases until it
+ * reaches its maximum value at after this many milliseconds.
+ */
+ private static final long SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000;
+
+ @Override
+ public void run() {
+ // Compute the number of pixels the pointer's y-coordinate is past the view.
+ // Negative values mean the pointer is at or before the top of the view, and
+ // positive values mean that the pointer is at or after the bottom of the view. Note
+ // that one additional pixel is added here so that the view still scrolls when the
+ // pointer is exactly at the top or bottom.
+ int pixelsPastView = 0;
+ if (mPointer.y <= 0) {
+ pixelsPastView = mPointer.y - 1;
+ } else if (mPointer.y >= mHelper.getHeight() - 1) {
+ pixelsPastView = mPointer.y - mHelper.getHeight() + 1;
+ }
+
+ if (!mIsActive || pixelsPastView == 0) {
+ // If band selection is inactive, or if it is active but not at the edge of the
+ // view, no scrolling is necessary.
+ mScrollStartTime = NOT_SET;
+ return;
+ }
+
+ if (mScrollStartTime == NOT_SET) {
+ // If the pointer was previously not at the edge of the view but now is, set the
+ // start time for the scroll.
+ mScrollStartTime = System.currentTimeMillis();
+ }
+
+ // Compute the number of pixels to scroll, and scroll that many pixels.
+ final int numPixels = computeScrollDistance(
+ pixelsPastView, System.currentTimeMillis() - mScrollStartTime);
+ mHelper.scrollBy(numPixels);
+
+ mHelper.removeCallback(mViewScroller);
+ mHelper.postRunnable(this);
+ }
+
+ /**
+ * Computes the number of pixels to scroll based on how far the pointer is past the end
+ * of the view and how long it has been there. Roughly based on ItemTouchHelper's
+ * algorithm for computing the number of pixels to scroll when an item is dragged to the
+ * end of a {@link RecyclerView}.
+ * @param pixelsPastView
+ * @param scrollDuration
+ * @return
+ */
+ private int computeScrollDistance(int pixelsPastView, long scrollDuration) {
+ final int maxScrollStep = mHelper.getHeight();
+ final int direction = (int) Math.signum(pixelsPastView);
+ final int absPastView = Math.abs(pixelsPastView);
+
+ // Calculate the ratio of how far out of the view the pointer currently resides to
+ // the entire height of the view.
+ final float outOfBoundsRatio = Math.min(
+ 1.0f, (float) absPastView / mHelper.getHeight());
+ // Interpolate this ratio and use it to compute the maximum scroll that should be
+ // possible for this step.
+ final float cappedScrollStep =
+ direction * maxScrollStep * smoothOutOfBoundsRatio(outOfBoundsRatio);
+
+ // Likewise, calculate the ratio of the time spent in the scroll to the limit.
+ final float timeRatio = Math.min(
+ 1.0f, (float) scrollDuration / SCROLL_ACCELERATION_LIMIT_TIME_MS);
+ // Interpolate this ratio and use it to compute the final number of pixels to
+ // scroll.
+ final int numPixels = (int) (cappedScrollStep * smoothTimeRatio(timeRatio));
+
+ // If the final number of pixels to scroll ends up being 0, the view should still
+ // scroll at least one pixel.
+ return numPixels != 0 ? numPixels : direction;
+ }
+
+ /**
+ * Interpolates the given out of bounds ratio on a curve which starts at (0,0) and ends
+ * at (1,1) and quickly approaches 1 near the start of that interval. This ensures that
+ * drags that are at the edge or barely past the edge of the view still cause sufficient
+ * scrolling. The equation y=(x-1)^5+1 is used, but this could also be tweaked if
+ * needed.
+ * @param ratio A ratio which is in the range [0, 1].
+ * @return A "smoothed" value, also in the range [0, 1].
+ */
+ private float smoothOutOfBoundsRatio(float ratio) {
+ return (float) Math.pow(ratio - 1.0f, 5) + 1.0f;
+ }
+
+ /**
+ * Interpolates the given time ratio on a curve which starts at (0,0) and ends at (1,1)
+ * and stays close to 0 for most input values except those very close to 1. This ensures
+ * that scrolls start out very slowly but speed up drastically after the scroll has been
+ * in progress close to SCROLL_ACCELERATION_LIMIT_TIME_MS. The equation y=x^5 is used,
+ * but this could also be tweaked if needed.
+ * @param ratio A ratio which is in the range [0, 1].
+ * @return A "smoothed" value, also in the range [0, 1].
+ */
+ private float smoothTimeRatio(float ratio) {
+ return (float) Math.pow(ratio, 5);
+ }
+ };
+
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ if (!mIsActive) {
+ return;
+ }
+
+ // Adjust the y-coordinate of the origin the opposite number of pixels so that the
+ // origin remains in the same place relative to the view's items.
+ mOrigin.y -= dy;
+ resizeBandSelectRectangle();
+ }
+ }
+
+ /**
+ * Provides a band selection item model for views within a RecyclerView. This class queries the
+ * RecyclerView to determine where its items are placed; then, once band selection is underway,
+ * it alerts listeners of which items are covered by the selections.
+ */
+ public static final class BandSelectModel extends RecyclerView.OnScrollListener {
+
+ public static final int NOT_SET = -1;
+
+ // Enum values used to determine the corner at which the origin is located within the
+ private static final int UPPER = 0x00;
+ private static final int LOWER = 0x01;
+ private static final int LEFT = 0x00;
+ private static final int RIGHT = 0x02;
+ private static final int UPPER_LEFT = UPPER | LEFT;
+ private static final int UPPER_RIGHT = UPPER | RIGHT;
+ private static final int LOWER_LEFT = LOWER | LEFT;
+ private static final int LOWER_RIGHT = LOWER | RIGHT;
+
+ private final BandModelHelper mHelper;
+ private final List<OnSelectionChangedListener> mOnSelectionChangedListeners = new ArrayList<>();
+
+ // Map from the x-value of the left side of a SparseBooleanArray of adapter positions, keyed
+ // by their y-offset. For example, if the first column of the view starts at an x-value of 5,
+ // mColumns.get(5) would return an array of positions in that column. Within that array, the
+ // value for key y is the adapter position for the item whose y-offset is y.
+ private final SparseArray<SparseIntArray> mColumns = new SparseArray<>();
+
+ // List of limits along the x-axis. For example, if the view has two columns, this list will
+ // have two elements, each of which lists the lower- and upper-limits of the x-values of the
+ // view items. This list is sorted from furthest left to furthest right.
+ private final List<Limits> mXLimitsList = new ArrayList<>();
+
+ // Like mXLimitsList, but for y-coordinates. Note that this list only contains items which
+ // have been in the viewport. Thus, limits which exist in an area of the view to which the
+ // view has not scrolled are not present in the list.
+ private final List<Limits> mYLimitsList = new ArrayList<>();
+
+ // The adapter positions which have been recorded so far.
+ private final SparseBooleanArray mKnownPositions = new SparseBooleanArray();
+
+ // Array passed to registered OnSelectionChangedListeners. One array is created and reused
+ // throughout the lifetime of the object.
+ private final SparseBooleanArray mSelection = new SparseBooleanArray();
+
+ // The current pointer (in absolute positioning from the top of the view).
+ private Point mPointer = null;
+
+ // The bounds of the band selection.
+ private RelativePoint mRelativeOrigin;
+ private RelativePoint mRelativePointer;
+
+ private boolean mIsActive;
+
+ // Tracks where the band select originated from. This is used to determine where selections
+ // should expand from when Shift+click is used.
+ private int mPositionNearestOrigin = NOT_SET;
+
+ BandSelectModel(BandModelHelper helper) {
+ mHelper = helper;
+ mHelper.addOnScrollListener(this);
+ }
+
+ /**
+ * Stops listening to the view's scrolls. Call this function before discarding a
+ * BandSelecModel object to prevent memory leaks.
+ */
+ void stopListening() {
+ mHelper.removeOnScrollListener(this);
+ }
+
+ /**
+ * Start a band select operation at the given point.
+ * @param relativeOrigin The origin of the band select operation, relative to the viewport.
+ * For example, if the view is scrolled to the bottom, the top-left of the viewport
+ * would have a relative origin of (0, 0), even though its absolute point has a higher
+ * y-value.
+ */
+ void startSelection(Point relativeOrigin) {
+ mIsActive = true;
+ mPointer = mHelper.createAbsolutePoint(relativeOrigin);
+
+ recordVisibleChildren();
+ mRelativeOrigin = new RelativePoint(mPointer);
+ mRelativePointer = new RelativePoint(mPointer);
+ computeCurrentSelection();
+ notifyListeners();
+ }
+
+ /**
+ * Resizes the selection by adjusting the pointer (i.e., the corner of the selection
+ * opposite the origin.
+ * @param relativePointer The pointer (opposite of the origin) of the band select operation,
+ * relative to the viewport. For example, if the view is scrolled to the bottom, the
+ * top-left of the viewport would have a relative origin of (0, 0), even though its
+ * absolute point has a higher y-value.
+ */
+ void resizeSelection(Point relativePointer) {
+ mPointer = mHelper.createAbsolutePoint(relativePointer);
+ updateModel();
+ }
+
+ /**
+ * Ends the band selection.
+ */
+ void endSelection() {
+ mIsActive = false;
+ }
+
+ /**
+ * @return The adapter position for the item nearest the origin corresponding to the latest
+ * band select operation, or NOT_SET if the selection did not cover any items.
+ */
+ int getPositionNearestOrigin() {
+ return mPositionNearestOrigin;
+ }
+
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ if (!mIsActive) {
+ return;
+ }
+
+ mPointer.x += dx;
+ mPointer.y += dy;
+ recordVisibleChildren();
+ updateModel();
+ }
+
+ /**
+ * Queries the view for all children and records their location metadata.
+ */
+ private void recordVisibleChildren() {
+ for (int i = 0; i < mHelper.getVisibleChildCount(); i++) {
+ int adapterPosition = mHelper.getAdapterPositionAt(i);
+ if (!mKnownPositions.get(adapterPosition)) {
+ mKnownPositions.put(adapterPosition, true);
+ recordItemData(
+ mHelper.getAbsoluteRectForChildViewAt(i), adapterPosition);
+ }
+ }
+ }
+
+ /**
+ * Updates the limits lists and column map with the given item metadata.
+ * @param absoluteChildRect The absolute rectangle for the child view being processed.
+ * @param adapterPosition The position of the child view being processed.
+ */
+ private void recordItemData(Rect absoluteChildRect, int adapterPosition) {
+ if (mXLimitsList.size() != mHelper.getNumColumns()) {
+ // If not all x-limits have been recorded, record this one.
+ recordLimits(
+ mXLimitsList, new Limits(absoluteChildRect.left, absoluteChildRect.right));
+ }
+
+ if (mYLimitsList.size() != mHelper.getNumRows()) {
+ // If not all y-limits have been recorded, record this one.
+ recordLimits(
+ mYLimitsList, new Limits(absoluteChildRect.top, absoluteChildRect.bottom));
+ }
+
+ SparseIntArray columnList = mColumns.get(absoluteChildRect.left);
+ if (columnList == null) {
+ columnList = new SparseIntArray();
+ mColumns.put(absoluteChildRect.left, columnList);
+ }
+ columnList.put(absoluteChildRect.top, adapterPosition);
+ }
+
+ /**
+ * Ensures limits exists within the sorted list limitsList, and adds it to the list if it
+ * does not exist.
+ */
+ private void recordLimits(List<Limits> limitsList, Limits limits) {
+ int index = Collections.binarySearch(limitsList, limits);
+ if (index < 0) {
+ limitsList.add(~index, limits);
+ }
+ }
+
+ /**
+ * Handles a moved pointer; this function determines whether the pointer movement resulted
+ * in a selection change and, if it has, notifies listeners of this change.
+ */
+ private void updateModel() {
+ RelativePoint old = mRelativePointer;
+ mRelativePointer = new RelativePoint(mPointer);
+ if (old != null && mRelativePointer.equals(old)) {
+ return;
+ }
+
+ computeCurrentSelection();
+ notifyListeners();
+ }
+
+ /**
+ * Computes the currently-selected items.
+ */
+ private void computeCurrentSelection() {
+ if (areItemsCoveredBySelection(mRelativePointer, mRelativeOrigin)) {
+ updateSelection(computeBounds());
+ } else {
+ mSelection.clear();
+ mPositionNearestOrigin = NOT_SET;
+ }
+ }
+
+ /**
+ * Notifies all listeners of a selection change. Note that this function simply passes
+ * mSelection, so computeCurrentSelection() should be called before this
+ * function.
+ */
+ private void notifyListeners() {
+ for (OnSelectionChangedListener listener : mOnSelectionChangedListeners) {
+ listener.onSelectionChanged(mSelection);
+ }
+ }
+
+ /**
+ * @param rect Rectangle including all covered items.
+ */
+ private void updateSelection(Rect rect) {
+ int columnStartIndex =
+ Collections.binarySearch(mXLimitsList, new Limits(rect.left, rect.left));
+ checkState(columnStartIndex >= 0);
+ int columnEndIndex = columnStartIndex;
+
+ for (int i = columnStartIndex;
+ i < mXLimitsList.size() && mXLimitsList.get(i).lowerLimit <= rect.right; i++) {
+ columnEndIndex = i;
+ }
+
+ SparseIntArray firstColumn =
+ mColumns.get(mXLimitsList.get(columnStartIndex).lowerLimit);
+ int rowStartIndex = firstColumn.indexOfKey(rect.top);
+ if (rowStartIndex < 0) {
+ mPositionNearestOrigin = NOT_SET;
+ return;
+ }
+
+ int rowEndIndex = rowStartIndex;
+ for (int i = rowStartIndex;
+ i < firstColumn.size() && firstColumn.keyAt(i) <= rect.bottom; i++) {
+ rowEndIndex = i;
+ }
+
+ updateSelection(columnStartIndex, columnEndIndex, rowStartIndex, rowEndIndex);
+ }
+
+ /**
+ * Computes the selection given the previously-computed start- and end-indices for each
+ * row and column.
+ */
+ private void updateSelection(
+ int columnStartIndex, int columnEndIndex, int rowStartIndex, int rowEndIndex) {
+ mSelection.clear();
+ for (int column = columnStartIndex; column <= columnEndIndex; column++) {
+ SparseIntArray items = mColumns.get(mXLimitsList.get(column).lowerLimit);
+ for (int row = rowStartIndex; row <= rowEndIndex; row++) {
+ int position = items.get(items.keyAt(row));
+ mSelection.append(position, true);
+ if (isPossiblePositionNearestOrigin(column, columnStartIndex, columnEndIndex,
+ row, rowStartIndex, rowEndIndex)) {
+ // If this is the position nearest the origin, record it now so that it
+ // can be returned by endSelection() later.
+ mPositionNearestOrigin = position;
+ }
+ }
+ }
+ }
+
+ /**
+ * @return Returns true if the position is the nearest to the origin, or, in the case of the
+ * lower-right corner, whether it is possible that the position is the nearest to the
+ * origin. See comment below for reasoning for this special case.
+ */
+ private boolean isPossiblePositionNearestOrigin(int columnIndex, int columnStartIndex,
+ int columnEndIndex, int rowIndex, int rowStartIndex, int rowEndIndex) {
+ int corner = computeCornerNearestOrigin();
+ switch (corner) {
+ case UPPER_LEFT:
+ return columnIndex == columnStartIndex && rowIndex == rowStartIndex;
+ case UPPER_RIGHT:
+ return columnIndex == columnEndIndex && rowIndex == rowStartIndex;
+ case LOWER_LEFT:
+ return columnIndex == columnStartIndex && rowIndex == rowEndIndex;
+ case LOWER_RIGHT:
+ // Note that in some cases, the last row will not have as many items as there
+ // are columns (e.g., if there are 4 items and 3 columns, the second row will
+ // only have one item in the first column). This function is invoked for each
+ // position from left to right, so return true for any position in the bottom
+ // row and only the right-most position in the bottom row will be recorded.
+ return rowIndex == rowEndIndex;
+ default:
+ throw new RuntimeException("Invalid corner type.");
+ }
+ }
+
+ /**
+ * Listener for changes in which items have been band selected.
+ */
+ static interface OnSelectionChangedListener {
+ public void onSelectionChanged(SparseBooleanArray updatedSelection);
+ }
+
+ void addOnSelectionChangedListener(OnSelectionChangedListener listener) {
+ mOnSelectionChangedListeners.add(listener);
+ }
+
+ void removeOnSelectionChangedListener(OnSelectionChangedListener listener) {
+ mOnSelectionChangedListeners.remove(listener);
+ }
+
+ /**
+ * Limits of a view item. For example, if an item's left side is at x-value 5 and its right side
+ * is at x-value 10, the limits would be from 5 to 10. Used to record the left- and right sides
+ * of item columns and the top- and bottom sides of item rows so that it can be determined
+ * whether the pointer is located within the bounds of an item.
+ */
+ private class Limits implements Comparable<Limits> {
+ int lowerLimit;
+ int upperLimit;
+
+ Limits(int lowerLimit, int upperLimit) {
+ this.lowerLimit = lowerLimit;
+ this.upperLimit = upperLimit;
+ }
+
+ @Override
+ public int compareTo(Limits other) {
+ return lowerLimit - other.lowerLimit;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Limits)) {
+ return false;
+ }
+
+ return ((Limits) other).lowerLimit == lowerLimit &&
+ ((Limits) other).upperLimit == upperLimit;
+ }
+ }
+
+ /**
+ * The location of a coordinate relative to items. This class represents a general area of the
+ * view as it relates to band selection rather than an explicit point. For example, two
+ * different points within an item are considered to have the same "location" because band
+ * selection originating within the item would select the same items no matter which point
+ * was used. Same goes for points between items as well as those at the very beginning or end
+ * of the view.
+ *
+ * Tracking a coordinate (e.g., an x-value) as a CoordinateLocation instead of as an int has the
+ * advantage of tying the value to the Limits of items along that axis. This allows easy
+ * selection of items within those Limits as opposed to a search through every item to see if a
+ * given coordinate value falls within those Limits.
+ */
+ private class RelativeCoordinate
+ implements Comparable<RelativeCoordinate> {
+ /**
+ * Location describing points after the last known item.
+ */
+ static final int AFTER_LAST_ITEM = 0;
+
+ /**
+ * Location describing points before the first known item.
+ */
+ static final int BEFORE_FIRST_ITEM = 1;
+
+ /**
+ * Location describing points between two items.
+ */
+ static final int BETWEEN_TWO_ITEMS = 2;
+
+ /**
+ * Location describing points within the limits of one item.
+ */
+ static final int WITHIN_LIMITS = 3;
+
+ /**
+ * The type of this coordinate, which is one of AFTER_LAST_ITEM, BEFORE_FIRST_ITEM,
+ * BETWEEN_TWO_ITEMS, or WITHIN_LIMITS.
+ */
+ final int type;
+
+ /**
+ * The limits before the coordinate; only populated when type == WITHIN_LIMITS or type ==
+ * BETWEEN_TWO_ITEMS.
+ */
+ Limits limitsBeforeCoordinate;
+
+ /**
+ * The limits after the coordinate; only populated when type == BETWEEN_TWO_ITEMS.
+ */
+ Limits limitsAfterCoordinate;
+
+ // Limits of the first known item; only populated when type == BEFORE_FIRST_ITEM.
+ Limits mFirstKnownItem;
+ // Limits of the last known item; only populated when type == AFTER_LAST_ITEM.
+ Limits mLastKnownItem;
+
+ /**
+ * @param limitsList The sorted limits list for the coordinate type. If this
+ * CoordinateLocation is an x-value, mXLimitsList should be passed; otherwise,
+ * mYLimitsList should be pased.
+ * @param value The coordinate value.
+ */
+ RelativeCoordinate(List<Limits> limitsList, int value) {
+ Limits dummyLimits = new Limits(value, value);
+ int index = Collections.binarySearch(limitsList, dummyLimits);
+
+ if (index >= 0) {
+ this.type = WITHIN_LIMITS;
+ this.limitsBeforeCoordinate = limitsList.get(index);
+ } else if (~index == 0) {
+ this.type = BEFORE_FIRST_ITEM;
+ this.mFirstKnownItem = limitsList.get(0);
+ } else if (~index == limitsList.size()) {
+ Limits lastLimits = limitsList.get(limitsList.size() - 1);
+ if (lastLimits.lowerLimit <= value && value <= lastLimits.upperLimit) {
+ this.type = WITHIN_LIMITS;
+ this.limitsBeforeCoordinate = lastLimits;
+ } else {
+ this.type = AFTER_LAST_ITEM;
+ this.mLastKnownItem = lastLimits;
+ }
+ } else {
+ Limits limitsBeforeIndex = limitsList.get(~index - 1);
+ if (limitsBeforeIndex.lowerLimit <= value && value <= limitsBeforeIndex.upperLimit) {
+ this.type = WITHIN_LIMITS;
+ this.limitsBeforeCoordinate = limitsList.get(~index - 1);
+ } else {
+ this.type = BETWEEN_TWO_ITEMS;
+ this.limitsBeforeCoordinate = limitsList.get(~index - 1);
+ this.limitsAfterCoordinate = limitsList.get(~index);
+ }
+ }
+ }
+
+ int toComparisonValue() {
+ if (type == BEFORE_FIRST_ITEM) {
+ return mFirstKnownItem.lowerLimit - 1;
+ } else if (type == AFTER_LAST_ITEM) {
+ return mLastKnownItem.upperLimit + 1;
+ } else if (type == BETWEEN_TWO_ITEMS) {
+ return limitsBeforeCoordinate.upperLimit + 1;
+ } else {
+ return limitsBeforeCoordinate.lowerLimit;
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RelativeCoordinate)) {
+ return false;
+ }
+
+ RelativeCoordinate otherCoordinate = (RelativeCoordinate) other;
+ return toComparisonValue() == otherCoordinate.toComparisonValue();
+ }
+
+ @Override
+ public int compareTo(RelativeCoordinate other) {
+ return toComparisonValue() - other.toComparisonValue();
+ }
+ }
+
+ /**
+ * The location of a point relative to the Limits of nearby items; consists of both an x- and
+ * y-RelativeCoordinateLocation.
+ */
+ private class RelativePoint {
+ final RelativeCoordinate xLocation;
+ final RelativeCoordinate yLocation;
+
+ RelativePoint(Point point) {
+ this.xLocation = new RelativeCoordinate(mXLimitsList, point.x);
+ this.yLocation = new RelativeCoordinate(mYLimitsList, point.y);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RelativePoint)) {
+ return false;
+ }
+
+ RelativePoint otherPoint = (RelativePoint) other;
+ return xLocation.equals(otherPoint.xLocation) && yLocation.equals(otherPoint.yLocation);
+ }
+ }
+
+ /**
+ * Generates a rectangle which contains the items selected by the pointer and origin.
+ * @return The rectangle, or null if no items were selected.
+ */
+ private Rect computeBounds() {
+ Rect rect = new Rect();
+ rect.left = getCoordinateValue(
+ min(mRelativeOrigin.xLocation, mRelativePointer.xLocation),
+ mXLimitsList,
+ true);
+ rect.right = getCoordinateValue(
+ max(mRelativeOrigin.xLocation, mRelativePointer.xLocation),
+ mXLimitsList,
+ false);
+ rect.top = getCoordinateValue(
+ min(mRelativeOrigin.yLocation, mRelativePointer.yLocation),
+ mYLimitsList,
+ true);
+ rect.bottom = getCoordinateValue(
+ max(mRelativeOrigin.yLocation, mRelativePointer.yLocation),
+ mYLimitsList,
+ false);
+ return rect;
+ }
+
+ /**
+ * Computes the corner of the selection nearest the origin.
+ * @return
+ */
+ private int computeCornerNearestOrigin() {
+ int cornerValue = 0;
+
+ if (mRelativeOrigin.yLocation ==
+ min(mRelativeOrigin.yLocation, mRelativePointer.yLocation)) {
+ cornerValue |= UPPER;
+ } else {
+ cornerValue |= LOWER;
+ }
+
+ if (mRelativeOrigin.xLocation ==
+ min(mRelativeOrigin.xLocation, mRelativePointer.xLocation)) {
+ cornerValue |= LEFT;
+ } else {
+ cornerValue |= RIGHT;
+ }
+
+ return cornerValue;
+ }
+
+ private RelativeCoordinate min(RelativeCoordinate first, RelativeCoordinate second) {
+ return first.compareTo(second) < 0 ? first : second;
+ }
+
+ private RelativeCoordinate max(RelativeCoordinate first, RelativeCoordinate second) {
+ return first.compareTo(second) > 0 ? first : second;
+ }
+
+ /**
+ * @return The absolute coordinate (i.e., the x- or y-value) of the given relative
+ * coordinate.
+ */
+ private int getCoordinateValue(RelativeCoordinate coordinate,
+ List<Limits> limitsList, boolean isStartOfRange) {
+ switch (coordinate.type) {
+ case RelativeCoordinate.BEFORE_FIRST_ITEM:
+ return limitsList.get(0).lowerLimit;
+ case RelativeCoordinate.AFTER_LAST_ITEM:
+ return limitsList.get(limitsList.size() - 1).upperLimit;
+ case RelativeCoordinate.BETWEEN_TWO_ITEMS:
+ if (isStartOfRange) {
+ return coordinate.limitsAfterCoordinate.lowerLimit;
+ } else {
+ return coordinate.limitsBeforeCoordinate.upperLimit;
+ }
+ case RelativeCoordinate.WITHIN_LIMITS:
+ return coordinate.limitsBeforeCoordinate.lowerLimit;
+ }
+
+ throw new RuntimeException("Invalid coordinate value.");
+ }
+
+ private boolean areItemsCoveredBySelection(
+ RelativePoint first, RelativePoint second) {
+ return doesCoordinateLocationCoverItems(first.xLocation, second.xLocation) &&
+ doesCoordinateLocationCoverItems(first.yLocation, second.yLocation);
+ }
+
+ private boolean doesCoordinateLocationCoverItems(
+ RelativeCoordinate pointerCoordinate,
+ RelativeCoordinate originCoordinate) {
+ if (pointerCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM &&
+ originCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM) {
+ return false;
+ }
+
+ if (pointerCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM &&
+ originCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM) {
+ return false;
+ }
+
+ if (pointerCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS &&
+ originCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS &&
+ pointerCoordinate.limitsBeforeCoordinate.equals(
+ originCoordinate.limitsBeforeCoordinate) &&
+ pointerCoordinate.limitsAfterCoordinate.equals(
+ originCoordinate.limitsAfterCoordinate)) {
+ return false;
+ }
+
+ return true;
+ }
+ }
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectMatrixTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectModelTest.java
similarity index 71%
rename from packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectMatrixTest.java
rename to packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectModelTest.java
index f15a643..20c4548 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectMatrixTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectModelTest.java
@@ -18,7 +18,7 @@
import static org.junit.Assert.*;
-import com.android.documentsui.BandSelectMatrix;
+import com.android.documentsui.MultiSelectManager.BandSelectModel;
import android.graphics.Point;
import android.graphics.Rect;
@@ -28,13 +28,13 @@
import org.junit.After;
import org.junit.Test;
-public class BandSelectMatrixTest {
+public class BandSelectModelTest {
private static final int VIEW_PADDING_PX = 5;
private static final int CHILD_VIEW_EDGE_PX = 100;
private static final int VIEWPORT_HEIGHT = 500;
- private static BandSelectMatrix matrix;
+ private static BandSelectModel model;
private static TestHelper helper;
private static SparseBooleanArray lastSelection;
private static int viewWidth;
@@ -42,8 +42,8 @@
private static void setUp(int numChildren, int numColumns) {
helper = new TestHelper(numChildren, numColumns);
viewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX);
- matrix = new BandSelectMatrix(helper);
- matrix.addOnSelectionChangedListener(new BandSelectMatrix.OnSelectionChangedListener() {
+ model = new BandSelectModel(helper);
+ model.addOnSelectionChangedListener(new BandSelectModel.OnSelectionChangedListener() {
@Override
public void onSelectionChanged(SparseBooleanArray updatedSelection) {
@@ -54,7 +54,7 @@
@After
public void tearDown() {
- matrix = null;
+ model = null;
helper = null;
lastSelection = null;
}
@@ -62,111 +62,120 @@
@Test
public void testSelectionLeftOfItems() {
setUp(20, 5);
- matrix.startSelection(new Point(0, 10));
- matrix.resizeSelection(new Point(1, 11));
+ model.startSelection(new Point(0, 10));
+ model.resizeSelection(new Point(1, 11));
assertSelected(new int[0]);
+ assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
public void testSelectionRightOfItems() {
setUp(20, 4);
- matrix.startSelection(new Point(viewWidth - 1, 10));
- matrix.resizeSelection(new Point(viewWidth - 2, 11));
+ model.startSelection(new Point(viewWidth - 1, 10));
+ model.resizeSelection(new Point(viewWidth - 2, 11));
assertSelected(new int[0]);
+ assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
public void testSelectionAboveItems() {
setUp(20, 4);
- matrix.startSelection(new Point(10, 0));
- matrix.resizeSelection(new Point(11, 1));
+ model.startSelection(new Point(10, 0));
+ model.resizeSelection(new Point(11, 1));
assertSelected(new int[0]);
+ assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
public void testSelectionBelowItems() {
setUp(5, 4);
- matrix.startSelection(new Point(10, VIEWPORT_HEIGHT - 1));
- matrix.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2));
+ model.startSelection(new Point(10, VIEWPORT_HEIGHT - 1));
+ model.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2));
assertSelected(new int[0]);
+ assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
public void testVerticalSelectionBetweenItems() {
setUp(20, 4);
- matrix.startSelection(new Point(106, 0));
- matrix.resizeSelection(new Point(107, 200));
+ model.startSelection(new Point(106, 0));
+ model.resizeSelection(new Point(107, 200));
assertSelected(new int[0]);
+ assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
public void testHorizontalSelectionBetweenItems() {
setUp(20, 4);
- matrix.startSelection(new Point(0, 105));
- matrix.resizeSelection(new Point(200, 106));
+ model.startSelection(new Point(0, 105));
+ model.resizeSelection(new Point(200, 106));
assertSelected(new int[0]);
+ assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
public void testGrowingAndShrinkingSelection() {
setUp(20, 4);
- matrix.startSelection(new Point(0, 0));
- matrix.resizeSelection(new Point(5, 5));
+ model.startSelection(new Point(0, 0));
+ model.resizeSelection(new Point(5, 5));
assertSelected(new int[] {0});
- matrix.resizeSelection(new Point(109, 109));
+ model.resizeSelection(new Point(109, 109));
assertSelected(new int[] {0});
- matrix.resizeSelection(new Point(110, 109));
+ model.resizeSelection(new Point(110, 109));
assertSelected(new int[] {0, 1});
- matrix.resizeSelection(new Point(110, 110));
+ model.resizeSelection(new Point(110, 110));
assertSelected(new int[] {0, 1, 4, 5});
- matrix.resizeSelection(new Point(214, 214));
+ model.resizeSelection(new Point(214, 214));
assertSelected(new int[] {0, 1, 4, 5});
- matrix.resizeSelection(new Point(215, 214));
+ model.resizeSelection(new Point(215, 214));
assertSelected(new int[] {0, 1, 2, 4, 5, 6});
- matrix.resizeSelection(new Point(214, 214));
+ model.resizeSelection(new Point(214, 214));
assertSelected(new int[] {0, 1, 4, 5});
- matrix.resizeSelection(new Point(110, 110));
+ model.resizeSelection(new Point(110, 110));
assertSelected(new int[] {0, 1, 4, 5});
- matrix.resizeSelection(new Point(110, 109));
+ model.resizeSelection(new Point(110, 109));
assertSelected(new int[] {0, 1});
- matrix.resizeSelection(new Point(109, 109));
+ model.resizeSelection(new Point(109, 109));
assertSelected(new int[] {0});
- matrix.resizeSelection(new Point(5, 5));
+ model.resizeSelection(new Point(5, 5));
assertSelected(new int[] {0});
- matrix.resizeSelection(new Point(0, 0));
+ model.resizeSelection(new Point(0, 0));
assertSelected(new int[0]);
+ assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
}
@Test
public void testSelectionMovingAroundOrigin() {
setUp(16, 4);
- matrix.startSelection(new Point(210, 210));
- matrix.resizeSelection(new Point(viewWidth - 1, 0));
+ model.startSelection(new Point(210, 210));
+ model.resizeSelection(new Point(viewWidth - 1, 0));
assertSelected(new int[] {2, 3, 6, 7});
- matrix.resizeSelection(new Point(0, 0));
+ model.resizeSelection(new Point(0, 0));
assertSelected(new int[] {0, 1, 4, 5});
- matrix.resizeSelection(new Point(0, 420));
+ model.resizeSelection(new Point(0, 420));
assertSelected(new int[] {8, 9, 12, 13});
- matrix.resizeSelection(new Point(viewWidth - 1, 420));
+ model.resizeSelection(new Point(viewWidth - 1, 420));
assertSelected(new int[] {10, 11, 14, 15});
+ assertEquals(10, model.getPositionNearestOrigin());
}
@Test
public void testScrollingBandSelect() {
setUp(40, 4);
- matrix.startSelection(new Point(0, 0));
- matrix.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
+ model.startSelection(new Point(0, 0));
+ model.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
assertSelected(new int[] {0, 4, 8, 12, 16});
scroll(CHILD_VIEW_EDGE_PX);
assertSelected(new int[] {0, 4, 8, 12, 16, 20});
- matrix.resizeSelection(new Point(200, VIEWPORT_HEIGHT - 1));
+ model.resizeSelection(new Point(200, VIEWPORT_HEIGHT - 1));
assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21});
scroll(CHILD_VIEW_EDGE_PX);
assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25});
scroll(-2 * CHILD_VIEW_EDGE_PX);
assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17});
- matrix.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
+ model.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
assertSelected(new int[] {0, 4, 8, 12, 16});
+ assertEquals(0, model.getPositionNearestOrigin());
}
private static void assertSelected(int[] selectedPositions) {
@@ -179,10 +188,10 @@
private static void scroll(int dy) {
assertTrue(helper.verticalOffset + VIEWPORT_HEIGHT + dy <= helper.getTotalHeight());
helper.verticalOffset += dy;
- matrix.onScrolled(null, 0, dy);
+ model.onScrolled(null, 0, dy);
}
- private static final class TestHelper implements BandSelectMatrix.RecyclerViewHelper {
+ private static final class TestHelper implements MultiSelectManager.BandModelHelper {
public int horizontalOffset = 0;
public int verticalOffset = 0;
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java
index 03ad3d4..b82251c 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java
@@ -19,12 +19,12 @@
import static org.junit.Assert.*;
import android.support.v7.widget.RecyclerView;
+import android.util.SparseBooleanArray;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import com.android.documentsui.MultiSelectManager.RecyclerViewHelper;
import com.android.documentsui.MultiSelectManager.Selection;
import org.junit.Before;
@@ -79,7 +79,7 @@
@Test
public void mouseClick_NoPosition_ClearsSelection() {
- mManager.onLongPress(7);
+ mManager.onLongPress(7, 0);
click(11);
click(RecyclerView.NO_POSITION);
assertSelection();
@@ -95,27 +95,27 @@
@Test
public void longPress_StartsSelectionMode() {
- mManager.onLongPress(7);
+ mManager.onLongPress(7, 0);
assertSelection(7);
}
@Test
public void longPress_SecondPressExtendsSelection() {
- mManager.onLongPress(7);
- mManager.onLongPress(99);
+ mManager.onLongPress(7, 0);
+ mManager.onLongPress(99, 0);
assertSelection(7, 99);
}
@Test
public void singleTapUp_UnselectsSelectedItem() {
- mManager.onLongPress(7);
+ mManager.onLongPress(7, 0);
tap(7);
assertSelection();
}
@Test
public void singleTapUp_NoPosition_ClearsSelection() {
- mManager.onLongPress(7);
+ mManager.onLongPress(7, 0);
tap(11);
tap(RecyclerView.NO_POSITION);
assertSelection();
@@ -123,7 +123,7 @@
@Test
public void singleTapUp_ExtendsSelection() {
- mManager.onLongPress(99);
+ mManager.onLongPress(99, 0);
tap(7);
tap(13);
tap(129899);
@@ -132,21 +132,21 @@
@Test
public void singleTapUp_ShiftCreatesRangeSelection() {
- mManager.onLongPress(7);
+ mManager.onLongPress(7, 0);
shiftTap(17);
assertRangeSelection(7, 17);
}
@Test
public void singleTapUp_ShiftCreatesRangeSeletion_Backwards() {
- mManager.onLongPress(17);
+ mManager.onLongPress(17, 0);
shiftTap(7);
assertRangeSelection(7, 17);
}
@Test
public void singleTapUp_SecondShiftClickExtendsSelection() {
- mManager.onLongPress(7);
+ mManager.onLongPress(7, 0);
shiftTap(11);
shiftTap(17);
assertRangeSelection(7, 17);
@@ -154,7 +154,7 @@
@Test
public void singleTapUp_MultipleContiguousRangesSelected() {
- mManager.onLongPress(7);
+ mManager.onLongPress(7, 0);
shiftTap(11);
tap(20);
shiftTap(25);
@@ -165,7 +165,7 @@
@Test
public void singleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick() {
- mManager.onLongPress(7);
+ mManager.onLongPress(7, 0);
shiftTap(17);
shiftTap(10);
assertRangeSelection(7, 10);
@@ -173,7 +173,7 @@
@Test
public void singleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick_Backwards() {
- mManager.onLongPress(17);
+ mManager.onLongPress(17, 0);
shiftTap(7);
shiftTap(14);
assertRangeSelection(14, 17);
@@ -182,7 +182,7 @@
@Test
public void singleTapUp_ShiftReversesSelectionDirection() {
- mManager.onLongPress(7);
+ mManager.onLongPress(7, 0);
shiftTap(17);
shiftTap(0);
assertRangeSelection(0, 7);
@@ -206,6 +206,36 @@
assertSelection(20);
}
+ @Test
+ public void provisionaSelection() {
+ Selection s = mManager.getSelection();
+ assertSelection();
+
+ SparseBooleanArray provisional = new SparseBooleanArray();
+ provisional.append(1, true);
+ provisional.append(2, true);
+ s.setProvisionalSelection(provisional);
+ assertSelection(1, 2);
+
+ provisional.delete(1);
+ provisional.append(3, true);
+ s.setProvisionalSelection(provisional);
+ assertSelection(2, 3);
+
+ s.applyProvisionalSelection();
+ assertSelection(2, 3);
+
+ provisional.clear();
+ provisional.append(3, true);
+ provisional.append(4, true);
+ s.setProvisionalSelection(provisional);
+ assertSelection(2, 3, 4);
+
+ provisional.delete(3);
+ s.setProvisionalSelection(provisional);
+ assertSelection(2, 3, 4);
+ }
+
private void tap(int position) {
mManager.onSingleTapUp(position, 0, MotionEvent.TOOL_TYPE_MOUSE);
}
@@ -252,7 +282,8 @@
assertEquals(selection.toString(), expected, selection.size());
}
- private static final class EventHelper implements RecyclerViewHelper {
+ private static final class EventHelper implements MultiSelectManager.MultiSelectHelper {
+
@Override
public int findEventPosition(MotionEvent e) {
throw new UnsupportedOperationException();
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java
index 0553270..d90130f 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java
@@ -22,7 +22,7 @@
@RunWith(Suite.class)
@SuiteClasses({
- BandSelectMatrixTest.class,
+ BandSelectModelTest.class,
MultiSelectManager_SelectionTest.class,
MultiSelectManagerTest.class
})
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index dfc31ab..b03871a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -40,6 +40,7 @@
protected SecurityMessageDisplay mSecurityMessageDisplay;
protected View mEcaView;
protected boolean mEnableHaptics;
+ private boolean mDismissing;
// To avoid accidental lockout due to events while the device in in the pocket, ignore
// any passwords with length less than or equal to this length.
@@ -67,6 +68,7 @@
@Override
public void reset() {
// start fresh
+ mDismissing = false;
resetPasswordText(false /* animate */);
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
@@ -113,6 +115,8 @@
}
protected void verifyPasswordAndUnlock() {
+ if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
+
final String entry = getPasswordText();
setPasswordEntryInputEnabled(false);
if (mPendingLockCheck != null) {
@@ -143,6 +147,7 @@
private void onPasswordChecked(boolean matched, int timeoutMs, boolean isValidPassword) {
if (matched) {
+ mDismissing = true;
mCallback.reportUnlockAttempt(true, 0);
mCallback.dismiss(true);
} else {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 4cd4845..ce2d11a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -62,6 +62,7 @@
return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
}
+ @Override
protected void resetState() {
mPasswordEntry.setEnabled(true);
}
@@ -69,11 +70,13 @@
@Override
protected void setPasswordEntryEnabled(boolean enabled) {
mPasswordEntry.setEnabled(enabled);
+ mOkButton.setEnabled(enabled);
}
@Override
protected void setPasswordEntryInputEnabled(boolean enabled) {
mPasswordEntry.setEnabled(enabled);
+ mOkButton.setEnabled(enabled);
}
@Override
@@ -186,6 +189,7 @@
mDeleteButton = findViewById(R.id.delete_button);
mDeleteButton.setVisibility(View.VISIBLE);
mDeleteButton.setOnClickListener(new OnClickListener() {
+ @Override
public void onClick(View v) {
// check for time-based lockouts
if (mPasswordEntry.isEnabled()) {
@@ -195,6 +199,7 @@
}
});
mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
public boolean onLongClick(View v) {
// check for time-based lockouts
if (mPasswordEntry.isEnabled()) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 77215a7..f45b9bd 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -255,7 +255,6 @@
if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
- SecurityMode mode = mSecurityModel.getSecurityMode();
final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
final int failedAttemptsBeforeWipe =
@@ -264,7 +263,6 @@
final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
(failedAttemptsBeforeWipe - failedAttempts)
: Integer.MAX_VALUE; // because DPM returns 0 if no restriction
- boolean showTimeout = false;
if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
// The user has installed a DevicePolicyManager that requests a user/profile to be wiped
// N attempts. Once we get below the grace period, we post this dialog every time as a
@@ -273,7 +271,8 @@
final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(currentUser);
int userType = USER_TYPE_PRIMARY;
if (expiringUser == currentUser) {
- if (expiringUser != UserHandle.USER_OWNER) {
+ // TODO: http://b/23522538
+ if (expiringUser != UserHandle.USER_SYSTEM) {
userType = USER_TYPE_SECONDARY_USER;
}
} else if (expiringUser != UserHandle.USER_NULL) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index ead0307..fc6117f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -523,7 +523,8 @@
final DevicePolicyManager dpm =
(DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
return dpm != null && (dpm.getKeyguardDisabledFeatures(null, userId)
- & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) != 0;
+ & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) != 0
+ || isSimPinSecure();
}
public boolean getUserCanSkipBouncer(int userId) {
@@ -1016,7 +1017,7 @@
private boolean shouldListenForFingerprint() {
return (mKeyguardIsVisible || !mDeviceInteractive) && !mSwitchingUser
- && !mFingerprintAlreadyAuthenticated;
+ && !mFingerprintAlreadyAuthenticated && !isFingerprintDisabled(getCurrentUser());
}
private void startListeningForFingerprint() {
@@ -1090,6 +1091,7 @@
* Handle {@link #MSG_DPM_STATE_CHANGED}
*/
protected void handleDevicePolicyManagerStateChanged() {
+ updateFingerprintListeningState();
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java
index 7126694..c1d9609 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java
@@ -76,18 +76,25 @@
void addToCursor(Identifier rootIdentifier, MatrixCursor.RowBuilder builder) {
final Identifier identifier = new Identifier(
rootIdentifier.mDeviceId, rootIdentifier.mStorageId, mObjectHandle);
+ final String mimeType = formatTypeToMimeType(mFormat);
int flag = 0;
if (mObjectHandle != DUMMY_HANDLE_FOR_ROOT) {
- flag |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE;
if (mThumbSize > 0) {
flag |= DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL;
}
+ if (!mReadOnly) {
+ flag |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
+ DocumentsContract.Document.FLAG_SUPPORTS_WRITE;
+ }
+ }
+ if (mimeType == DocumentsContract.Document.MIME_TYPE_DIR && !mReadOnly) {
+ flag |= DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE;
}
builder.add(Document.COLUMN_DOCUMENT_ID, identifier.toDocumentId());
builder.add(Document.COLUMN_DISPLAY_NAME, mName);
- builder.add(Document.COLUMN_MIME_TYPE, formatTypeToMimeType(mFormat));
+ builder.add(Document.COLUMN_MIME_TYPE, mimeType);
builder.add(
Document.COLUMN_LAST_MODIFIED,
mDateModified != null ? mDateModified.getTime() : null);
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 031cc07..a3cf3e2 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -167,13 +167,18 @@
public ParcelFileDescriptor openDocument(
String documentId, String mode, CancellationSignal signal)
throws FileNotFoundException {
- if (!"r".equals(mode) && !"w".equals(mode)) {
- // TODO: Support seekable file.
- throw new UnsupportedOperationException("The provider does not support seekable file.");
- }
final Identifier identifier = Identifier.createFromDocumentId(documentId);
try {
- return mPipeManager.readDocument(mMtpManager, identifier);
+ switch (mode) {
+ case "r":
+ return mPipeManager.readDocument(mMtpManager, identifier);
+ case "w":
+ return mPipeManager.writeDocument(getContext(), mMtpManager, identifier);
+ default:
+ // TODO: Add support for seekable files.
+ throw new UnsupportedOperationException(
+ "The provider does not support seekable file.");
+ }
} catch (IOException error) {
throw new FileNotFoundException(error.getMessage());
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 27ba794..6647009 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -108,6 +108,11 @@
return results;
}
+ synchronized MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ return device.getObjectInfo(objectHandle);
+ }
+
synchronized MtpDocument getDocument(int deviceId, int objectHandle) throws IOException {
final MtpDevice device = getDevice(deviceId);
return new MtpDocument(device.getObjectInfo(objectHandle));
@@ -137,15 +142,20 @@
}
}
+ // TODO: Remove this method.
synchronized int createDocument(int deviceId, int storageId, int parentObjectHandle,
String mimeType, String name) throws IOException {
- final MtpDevice device = getDevice(deviceId);
final MtpObjectInfo objectInfo = new MtpObjectInfo.Builder()
.setName(name)
.setStorageId(storageId)
.setParent(parentObjectHandle)
.setFormat(MtpDocument.mimeTypeToFormatType(mimeType))
.build();
+ return createDocument(deviceId, objectInfo);
+ }
+
+ synchronized int createDocument(int deviceId, MtpObjectInfo objectInfo) throws IOException {
+ final MtpDevice device = getDevice(deviceId);
final MtpObjectInfo result = device.sendObjectInfo(objectInfo);
if (result == null) {
throw new IOException("Failed to create a document");
@@ -168,6 +178,13 @@
device.importFile(objectHandle, target);
}
+ synchronized void sendObject(int deviceId, int objectHandle, int size,
+ ParcelFileDescriptor source) throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ if (!device.sendObject(objectHandle, size, source))
+ throw new IOException("Failed to send a document");
+ }
+
private MtpDevice getDevice(int deviceId) throws IOException {
final MtpDevice device = mDevices.get(deviceId);
if (device == null) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
index ba13b31..53f1b65 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
@@ -16,10 +16,16 @@
package com.android.mtp;
+import android.content.Context;
+import android.mtp.MtpObjectInfo;
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -40,6 +46,13 @@
return task.getReadingFileDescriptor();
}
+ ParcelFileDescriptor writeDocument(Context context, MtpManager model, Identifier identifier)
+ throws IOException {
+ final Task task = new WriteDocumentTask(context, model, identifier);
+ mExecutor.execute(task);
+ return task.getWritingFileDescriptor();
+ }
+
ParcelFileDescriptor readThumbnail(MtpManager model, Identifier identifier) throws IOException {
final Task task = new GetThumbnailTask(model, identifier);
mExecutor.execute(task);
@@ -60,6 +73,10 @@
ParcelFileDescriptor getReadingFileDescriptor() {
return mDescriptors[0];
}
+
+ ParcelFileDescriptor getWritingFileDescriptor() {
+ return mDescriptors[1];
+ }
}
private static class ImportFileTask extends Task {
@@ -83,6 +100,70 @@
}
}
+ private static class WriteDocumentTask extends Task {
+ private final Context mContext;
+
+ WriteDocumentTask(Context context, MtpManager model, Identifier identifier)
+ throws IOException {
+ super(model, identifier);
+ mContext = context;
+ }
+
+ @Override
+ public void run() {
+ File tempFile = null;
+ try {
+ // Obtain a temporary file and copy the data to it.
+ tempFile = mContext.getCacheDir().createTempFile("mtp", "tmp");
+ try (
+ final FileOutputStream tempOutputStream =
+ new ParcelFileDescriptor.AutoCloseOutputStream(
+ ParcelFileDescriptor.open(
+ tempFile, ParcelFileDescriptor.MODE_WRITE_ONLY));
+ final ParcelFileDescriptor.AutoCloseInputStream inputStream =
+ new ParcelFileDescriptor.AutoCloseInputStream(mDescriptors[0])
+ ) {
+ final byte[] buffer = new byte[32 * 1024];
+ int bytes;
+ while ((bytes = inputStream.read(buffer)) != -1) {
+ mDescriptors[0].checkError();
+ tempOutputStream.write(buffer, 0, bytes);
+ }
+ tempOutputStream.flush();
+ }
+
+ // Get the placeholder object info.
+ final MtpObjectInfo placeholderObjectInfo =
+ mManager.getObjectInfo(mIdentifier.mDeviceId, mIdentifier.mObjectHandle);
+
+ // Delete the target object info if it already exists (as a placeholder).
+ mManager.deleteDocument(mIdentifier.mDeviceId, mIdentifier.mObjectHandle);
+
+ // Create the target object info with a correct file size.
+ final int targetObjectHandle =
+ mManager.createDocument(
+ mIdentifier.mDeviceId,
+ new MtpObjectInfo.Builder(placeholderObjectInfo)
+ .setCompressedSize((int) tempFile.length())
+ .build());
+
+ // Upload the object.
+ final ParcelFileDescriptor tempInputDescriptor = ParcelFileDescriptor.open(
+ tempFile, ParcelFileDescriptor.MODE_READ_ONLY);
+ mManager.sendObject(mIdentifier.mDeviceId,
+ targetObjectHandle, (int) tempFile.length(), tempInputDescriptor);
+
+ } catch (IOException error) {
+ Log.w(MtpDocumentsProvider.TAG,
+ "Failed to send a file because of: " + error.getMessage());
+ } finally {
+ if (tempFile != null) {
+ tempFile.delete();
+ }
+ }
+ }
+ }
+
private static class GetThumbnailTask extends Task {
GetThumbnailTask(MtpManager model, Identifier identifier) throws IOException {
super(model, identifier);
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index f06e2ff..1826bd0 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -140,7 +140,7 @@
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("0_1", cursor.getString(0));
- assertEquals(Root.FLAG_SUPPORTS_IS_CHILD, cursor.getInt(1));
+ assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);
assertEquals("Storage A", cursor.getString(3));
@@ -156,7 +156,7 @@
cursor.moveToNext();
cursor.moveToNext();
assertEquals("1_1", cursor.getString(0));
- assertEquals(Root.FLAG_SUPPORTS_IS_CHILD, cursor.getInt(1));
+ assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);
assertEquals("Storage B", cursor.getString(3));
@@ -194,7 +194,7 @@
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals("1_1", cursor.getString(0));
- assertEquals(Root.FLAG_SUPPORTS_IS_CHILD, cursor.getInt(1));
+ assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
// TODO: Add storage icon for MTP devices.
assertTrue(cursor.isNull(2) /* icon */);
assertEquals("Storage B", cursor.getString(3));
@@ -211,7 +211,7 @@
new Date(1422716400000L) /* modified date */,
1024 * 1024 * 5 /* file size */,
1024 * 50 /* thumbnail size */,
- true /* read only */));
+ false /* read only */));
final Cursor cursor = mProvider.queryDocument("0_1_2", null);
assertEquals(1, cursor.getCount());
@@ -222,11 +222,37 @@
assertEquals(1422716400000L, cursor.getLong(3));
assertEquals(
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
+ DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL,
cursor.getInt(4));
assertEquals(1024 * 1024 * 5, cursor.getInt(5));
}
+ public void testQueryDocument_directory() throws IOException {
+ mMtpManager.setDocument(0, 2, new MtpDocument(
+ 2 /* object handle */,
+ 0x3001 /* directory */,
+ "directory" /* display name */,
+ new Date(1422716400000L) /* modified date */,
+ 0 /* file size */,
+ 0 /* thumbnail size */,
+ false /* read only */));
+ final Cursor cursor = mProvider.queryDocument("0_1_2", null);
+ assertEquals(1, cursor.getCount());
+
+ cursor.moveToNext();
+ assertEquals("0_1_2", cursor.getString(0));
+ assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
+ assertEquals("directory", cursor.getString(2));
+ assertEquals(1422716400000L, cursor.getLong(3));
+ assertEquals(
+ DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
+ DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
+ DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE,
+ cursor.getInt(4));
+ assertEquals(0, cursor.getInt(5));
+ }
+
public void testQueryDocument_forRoot() throws IOException {
mMtpManager.setRoots(0, new MtpRoot[] {
new MtpRoot(
@@ -269,10 +295,7 @@
assertEquals("image/jpeg", cursor.getString(1));
assertEquals("image.jpg", cursor.getString(2));
assertEquals(0, cursor.getLong(3));
- assertEquals(
- DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
- DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL,
- cursor.getInt(4));
+ assertEquals(DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL, cursor.getInt(4));
assertEquals(1024 * 1024 * 5, cursor.getInt(5));
assertFalse(cursor.moveToNext());
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
index 35918e1..e2cc3ed 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
@@ -21,6 +21,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import java.io.IOException;
+import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@@ -53,6 +54,41 @@
assertDescriptorError(descriptor);
}
+ public void testWriteDocument_basic() throws Exception {
+ // Create a placeholder file which should be replaced by a real file later.
+ mtpManager.setDocument(0, 1, new MtpDocument(1, 0, "", new Date(), 0, 0, false));
+
+ // Upload testing bytes.
+ final ParcelFileDescriptor descriptor = pipeManager.writeDocument(
+ getContext(), mtpManager, new Identifier(0, 0, 1));
+ final ParcelFileDescriptor.AutoCloseOutputStream outputStream =
+ new ParcelFileDescriptor.AutoCloseOutputStream(descriptor);
+ outputStream.write(HELLO_BYTES, 0, HELLO_BYTES.length);
+ outputStream.close();
+ executor.awaitTermination(1000, TimeUnit.MILLISECONDS);
+
+ // Check if the placeholder file is removed.
+ try {
+ final MtpDocument placeholderDocument = mtpManager.getDocument(0, 1);
+ fail(); // The placeholder file has not been deleted.
+ } catch (IOException e) {
+ // Expected error, as the file is gone.
+ }
+
+ // Confirm that the target file is created.
+ final MtpDocument targetDocument = mtpManager.getDocument(
+ 0, TestMtpManager.CREATED_DOCUMENT_HANDLE);
+ assertTrue(targetDocument != null);
+
+ // Verify uploaded bytes.
+ final byte[] uploadedBytes = mtpManager.getImportFileBytes(
+ 0, TestMtpManager.CREATED_DOCUMENT_HANDLE);
+ assertEquals(HELLO_BYTES.length, uploadedBytes.length);
+ for (int i = 0; i < HELLO_BYTES.length; i++) {
+ assertEquals(HELLO_BYTES[i], uploadedBytes[i]);
+ }
+ }
+
public void testReadThumbnail_basic() throws Exception {
mtpManager.setThumbnail(0, 1, HELLO_BYTES);
final ParcelFileDescriptor descriptor = pipeManager.readThumbnail(
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 40de7b4..94b5ba0 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -17,10 +17,13 @@
package com.android.mtp;
import android.content.Context;
+import android.mtp.MtpObjectInfo;
+import android.mtp.MtpObjectInfo.Builder;
import android.os.ParcelFileDescriptor;
import java.io.IOException;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -28,6 +31,8 @@
import java.util.TreeSet;
public class TestMtpManager extends MtpManager {
+ public static final int CREATED_DOCUMENT_HANDLE = 1000;
+
protected static String pack(int... args) {
return Arrays.toString(args);
}
@@ -65,6 +70,10 @@
mImportFileBytes.put(pack(deviceId, objectHandle), bytes);
}
+ byte[] getImportFileBytes(int deviceId, int objectHandle) {
+ return mImportFileBytes.get(pack(deviceId, objectHandle));
+ }
+
void setThumbnail(int deviceId, int objectHandle, byte[] bytes) {
mThumbnailBytes.put(pack(deviceId, objectHandle), bytes);
}
@@ -109,6 +118,15 @@
}
@Override
+ MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
+ final MtpDocument document = getDocument(deviceId, objectHandle);
+ // It's impossible to set an object id of MtpObjectInfo at this stage. Also,
+ // it's hard to get any information from MtpDocument, as it's designed to return them
+ // only via cursors. Rework these.
+ return new MtpObjectInfo.Builder().build();
+ }
+
+ @Override
int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle) throws IOException {
final String key = pack(deviceId, storageId, parentObjectHandle);
if (mObjectHandles.containsKey(key)) {
@@ -119,8 +137,9 @@
}
@Override
- void importFile(int deviceId, int storageId, ParcelFileDescriptor target) throws IOException {
- final String key = pack(deviceId, storageId);
+ void importFile(int deviceId, int objectHandle, ParcelFileDescriptor target)
+ throws IOException {
+ final String key = pack(deviceId, objectHandle);
if (mImportFileBytes.containsKey(key)) {
try (final ParcelFileDescriptor.AutoCloseOutputStream outputStream =
new ParcelFileDescriptor.AutoCloseOutputStream(target)) {
@@ -132,6 +151,44 @@
}
@Override
+ int createDocument(int deviceId, MtpObjectInfo objectInfo) throws IOException {
+ // For simplicity, it allows to create only one document, and it always has the hardcoded
+ // CREATED_DOCUMENT_HANDLE document handle.
+ final String key = pack(deviceId, CREATED_DOCUMENT_HANDLE);
+ if (!mDocuments.containsKey(key)) {
+ mDocuments.put(key, new MtpDocument(
+ CREATED_DOCUMENT_HANDLE,
+ objectInfo.getFormat(),
+ objectInfo.getName(),
+ new Date(objectInfo.getDateModified()),
+ objectInfo.getCompressedSize(),
+ objectInfo.getThumbCompressedSize(),
+ false /* Always writable for testing. */));
+ } else {
+ throw new IOException();
+ }
+ return CREATED_DOCUMENT_HANDLE;
+ }
+
+ @Override
+ void sendObject(int deviceId, int objectHandle, int size, ParcelFileDescriptor source)
+ throws IOException {
+ final String key = pack(deviceId, objectHandle);
+ if (!mDocuments.containsKey(key)) {
+ throw new IOException();
+ }
+
+ ParcelFileDescriptor.AutoCloseInputStream inputStream =
+ new ParcelFileDescriptor.AutoCloseInputStream(source);
+ byte[] buffer = new byte[size];
+ if (inputStream.read(buffer, 0, size) != size) {
+ throw new IOException();
+ }
+
+ mImportFileBytes.put(pack(deviceId, objectHandle), buffer);
+ }
+
+ @Override
byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
final String key = pack(deviceId, objectHandle);
if (mThumbnailBytes.containsKey(key)) {
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 1c4b05f..3ad8f21 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -21,4 +21,6 @@
<!-- The translation for disappearing security views after having solved them. -->
<dimen name="disappear_y_translation">-32dp</dimen>
-</resources>
\ No newline at end of file
+
+ <dimen name="circle_avatar_size">40dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b03f100..9b1f103 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -195,4 +195,34 @@
<!-- Content description of the WIFI signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_wifi_signal_full">Wifi signal full.</string>
+ <!-- Label for kernel threads in battery usage -->
+ <string name="process_kernel_label">Android OS</string>
+ <!-- Title of data usage item that represents all uninstalled applications. [CHAR LIMIT=48] -->
+ <string name="data_usage_uninstalled_apps">Removed apps</string>
+ <!-- Title of data usage item that represents all uninstalled applications or removed users. [CHAR LIMIT=48] -->
+ <string name="data_usage_uninstalled_apps_users">Removed apps and users</string>
+
+ <!-- Tethering controls, item title to go into the tethering settings -->
+ <!-- Tethering controls, item title to go into the tethering settings when only USB tethering is available [CHAR LIMIT=25]-->
+ <string name="tether_settings_title_usb">USB tethering</string>
+ <!-- Tethering controls, item title to go into the tethering settings when only Wifi tethering is available [CHAR LIMIT=25]-->
+ <string name="tether_settings_title_wifi">Portable hotspot</string>
+ <!-- Tethering controls, item title to go into the tethering settings when only Bluetooth tethering is available [CHAR LIMIT=25]-->
+ <string name="tether_settings_title_bluetooth">Bluetooth tethering</string>
+ <!-- Tethering controls, item title to go into the tethering settings when USB and Bluetooth tethering are available [CHAR LIMIT=25]-->
+ <string name="tether_settings_title_usb_bluetooth">Tethering</string>
+ <!-- Tethering controls, item title to go into the tethering settings when USB, Bluetooth and Wifi tethering are available [CHAR LIMIT=25]-->
+ <string name="tether_settings_title_all">Tethering & portable hotspot</string>
+
+ <!-- Title for a work profile. [CHAR LIMIT=25] -->
+ <string name="managed_user_title">Work profile</string>
+
+ <!-- Title for Guest user [CHAR LIMIT=35] -->
+ <string name="user_guest">Guest</string>
+
+ <!-- Manage apps, individual app screen, substituted for the application's label when the app's label CAN NOT be determined.-->
+ <string name="unknown">Unknown</string>
+
+ <!-- [CHAR LIMIT=NONE] Label of a running process that represents another user -->
+ <string name="running_process_item_user_label">User: <xliff:g id="user_name">%1$s</xliff:g></string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
new file mode 100644
index 0000000..621a09cd
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -0,0 +1,84 @@
+package com.android.settingslib;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.os.UserManager;
+
+import com.android.internal.util.UserIcons;
+import com.android.settingslib.drawable.CircleFramedDrawable;
+
+public final class Utils {
+
+ /**
+ * Return string resource that best describes combination of tethering
+ * options available on this device.
+ */
+ public static int getTetheringLabel(ConnectivityManager cm) {
+ String[] usbRegexs = cm.getTetherableUsbRegexs();
+ String[] wifiRegexs = cm.getTetherableWifiRegexs();
+ String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
+
+ boolean usbAvailable = usbRegexs.length != 0;
+ boolean wifiAvailable = wifiRegexs.length != 0;
+ boolean bluetoothAvailable = bluetoothRegexs.length != 0;
+
+ if (wifiAvailable && usbAvailable && bluetoothAvailable) {
+ return R.string.tether_settings_title_all;
+ } else if (wifiAvailable && usbAvailable) {
+ return R.string.tether_settings_title_all;
+ } else if (wifiAvailable && bluetoothAvailable) {
+ return R.string.tether_settings_title_all;
+ } else if (wifiAvailable) {
+ return R.string.tether_settings_title_wifi;
+ } else if (usbAvailable && bluetoothAvailable) {
+ return R.string.tether_settings_title_usb_bluetooth;
+ } else if (usbAvailable) {
+ return R.string.tether_settings_title_usb;
+ } else {
+ return R.string.tether_settings_title_bluetooth;
+ }
+ }
+
+ /**
+ * Returns a label for the user, in the form of "User: user name" or "Work profile".
+ */
+ public static String getUserLabel(Context context, UserInfo info) {
+ String name = info != null ? info.name : null;
+ if (info.isManagedProfile()) {
+ // We use predefined values for managed profiles
+ return context.getString(R.string.managed_user_title);
+ } else if (info.isGuest()) {
+ name = context.getString(R.string.user_guest);
+ }
+ if (name == null && info != null) {
+ name = Integer.toString(info.id);
+ } else if (info == null) {
+ name = context.getString(R.string.unknown);
+ }
+ return context.getResources().getString(R.string.running_process_item_user_label, name);
+ }
+
+ /**
+ * Returns a circular icon for a user.
+ */
+ public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
+ if (user.isManagedProfile()) {
+ // We use predefined values for managed profiles
+ Bitmap b = BitmapFactory.decodeResource(context.getResources(),
+ com.android.internal.R.drawable.ic_corp_icon);
+ return CircleFramedDrawable.getInstance(context, b);
+ }
+ if (user.iconPath != null) {
+ Bitmap icon = um.getUserIcon(user.id);
+ if (icon != null) {
+ return CircleFramedDrawable.getInstance(context, icon);
+ }
+ }
+ return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
+ UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
new file mode 100644
index 0000000..278b57d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 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.settingslib.drawable;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.R;
+
+/**
+ * Converts the user avatar icon to a circularly clipped one.
+ * TODO: Move this to an internal framework class and share with the one in Keyguard.
+ */
+public class CircleFramedDrawable extends Drawable {
+
+ private final Bitmap mBitmap;
+ private final int mSize;
+ private final Paint mPaint;
+
+ private float mScale;
+ private Rect mSrcRect;
+ private RectF mDstRect;
+
+ public static CircleFramedDrawable getInstance(Context context, Bitmap icon) {
+ Resources res = context.getResources();
+ float iconSize = res.getDimension(R.dimen.circle_avatar_size);
+
+ CircleFramedDrawable instance = new CircleFramedDrawable(icon, (int) iconSize);
+ return instance;
+ }
+
+ public CircleFramedDrawable(Bitmap icon, int size) {
+ super();
+ mSize = size;
+
+ mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(mBitmap);
+
+ final int width = icon.getWidth();
+ final int height = icon.getHeight();
+ final int square = Math.min(width, height);
+
+ final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, square, square);
+ final RectF circleRect = new RectF(0f, 0f, mSize, mSize);
+
+ final Path fillPath = new Path();
+ fillPath.addArc(circleRect, 0f, 360f);
+
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+ // opaque circle matte
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setColor(Color.BLACK);
+ mPaint.setStyle(Paint.Style.FILL);
+ canvas.drawPath(fillPath, mPaint);
+
+ // mask in the icon where the bitmap is opaque
+ mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ canvas.drawBitmap(icon, cropRect, circleRect, mPaint);
+
+ // prepare paint for frame drawing
+ mPaint.setXfermode(null);
+
+ mScale = 1f;
+
+ mSrcRect = new Rect(0, 0, mSize, mSize);
+ mDstRect = new RectF(0, 0, mSize, mSize);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ final float inside = mScale * mSize;
+ final float pad = (mSize - inside) / 2f;
+
+ mDstRect.set(pad, pad, mSize - pad, mSize - pad);
+ canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
+ }
+
+ public void setScale(float scale) {
+ mScale = scale;
+ }
+
+ public float getScale() {
+ return mScale;
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mSize;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mSize;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
new file mode 100644
index 0000000..572bae1
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 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.settingslib.net;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStats;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+public class SummaryForAllUidLoader extends AsyncTaskLoader<NetworkStats> {
+ private static final String KEY_TEMPLATE = "template";
+ private static final String KEY_START = "start";
+ private static final String KEY_END = "end";
+
+ private final INetworkStatsSession mSession;
+ private final Bundle mArgs;
+
+ public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
+ final Bundle args = new Bundle();
+ args.putParcelable(KEY_TEMPLATE, template);
+ args.putLong(KEY_START, start);
+ args.putLong(KEY_END, end);
+ return args;
+ }
+
+ public SummaryForAllUidLoader(Context context, INetworkStatsSession session, Bundle args) {
+ super(context);
+ mSession = session;
+ mArgs = args;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ super.onStartLoading();
+ forceLoad();
+ }
+
+ @Override
+ public NetworkStats loadInBackground() {
+ final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
+ final long start = mArgs.getLong(KEY_START);
+ final long end = mArgs.getLong(KEY_END);
+
+ try {
+ return mSession.getSummaryForAllUid(template, start, end, false);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ super.onStopLoading();
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ cancelLoad();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java
new file mode 100644
index 0000000..5e42281
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 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.settingslib.net;
+
+import android.graphics.drawable.Drawable;
+
+public class UidDetail {
+ public CharSequence label;
+ public CharSequence contentDescription;
+ public CharSequence[] detailLabels;
+ public CharSequence[] detailContentDescriptions;
+ public Drawable icon;
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java
new file mode 100644
index 0000000..224b967
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2011 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.settingslib.net;
+
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.TrafficStats;
+import android.os.UserManager;
+import android.os.UserHandle;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.settingslib.R;
+import com.android.settingslib.Utils;
+
+/**
+ * Return details about a specific UID, handling special cases like
+ * {@link TrafficStats#UID_TETHERING} and {@link UserInfo}.
+ */
+public class UidDetailProvider {
+ private static final String TAG = "DataUsage";
+ private final Context mContext;
+ private final SparseArray<UidDetail> mUidDetailCache;
+
+ public static final int OTHER_USER_RANGE_START = -2000;
+
+ public static int buildKeyForUser(int userHandle) {
+ return OTHER_USER_RANGE_START - userHandle;
+ }
+
+ public static boolean isKeyForUser(int key) {
+ return key <= OTHER_USER_RANGE_START;
+ }
+
+ public static int getUserIdForKey(int key) {
+ return OTHER_USER_RANGE_START - key;
+ }
+
+ public UidDetailProvider(Context context) {
+ mContext = context.getApplicationContext();
+ mUidDetailCache = new SparseArray<UidDetail>();
+ }
+
+ public void clearCache() {
+ synchronized (mUidDetailCache) {
+ mUidDetailCache.clear();
+ }
+ }
+
+ /**
+ * Resolve best descriptive label for the given UID.
+ */
+ public UidDetail getUidDetail(int uid, boolean blocking) {
+ UidDetail detail;
+
+ synchronized (mUidDetailCache) {
+ detail = mUidDetailCache.get(uid);
+ }
+
+ if (detail != null) {
+ return detail;
+ } else if (!blocking) {
+ return null;
+ }
+
+ detail = buildUidDetail(uid);
+
+ synchronized (mUidDetailCache) {
+ mUidDetailCache.put(uid, detail);
+ }
+
+ return detail;
+ }
+
+ /**
+ * Build {@link UidDetail} object, blocking until all {@link Drawable}
+ * lookup is finished.
+ */
+ private UidDetail buildUidDetail(int uid) {
+ final Resources res = mContext.getResources();
+ final PackageManager pm = mContext.getPackageManager();
+
+ final UidDetail detail = new UidDetail();
+ detail.label = pm.getNameForUid(uid);
+ detail.icon = pm.getDefaultActivityIcon();
+
+ // handle special case labels
+ switch (uid) {
+ case android.os.Process.SYSTEM_UID:
+ detail.label = res.getString(R.string.process_kernel_label);
+ detail.icon = pm.getDefaultActivityIcon();
+ return detail;
+ case TrafficStats.UID_REMOVED:
+ detail.label = res.getString(UserManager.supportsMultipleUsers()
+ ? R.string.data_usage_uninstalled_apps_users
+ : R.string.data_usage_uninstalled_apps);
+ detail.icon = pm.getDefaultActivityIcon();
+ return detail;
+ case TrafficStats.UID_TETHERING:
+ final ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ detail.label = res.getString(Utils.getTetheringLabel(cm));
+ detail.icon = pm.getDefaultActivityIcon();
+ return detail;
+ }
+
+ final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+
+ // Handle keys that are actually user handles
+ if (isKeyForUser(uid)) {
+ final int userHandle = getUserIdForKey(uid);
+ final UserInfo info = um.getUserInfo(userHandle);
+ if (info != null) {
+ detail.label = Utils.getUserLabel(mContext, info);
+ detail.icon = Utils.getUserIcon(mContext, um, info);
+ return detail;
+ }
+ }
+
+ // otherwise fall back to using packagemanager labels
+ final String[] packageNames = pm.getPackagesForUid(uid);
+ final int length = packageNames != null ? packageNames.length : 0;
+ try {
+ final int userId = UserHandle.getUserId(uid);
+ UserHandle userHandle = new UserHandle(userId);
+ IPackageManager ipm = AppGlobals.getPackageManager();
+ if (length == 1) {
+ final ApplicationInfo info = ipm.getApplicationInfo(packageNames[0],
+ 0 /* no flags */, userId);
+ if (info != null) {
+ detail.label = info.loadLabel(pm).toString();
+ detail.icon = um.getBadgedIconForUser(info.loadIcon(pm),
+ new UserHandle(userId));
+ }
+ } else if (length > 1) {
+ detail.detailLabels = new CharSequence[length];
+ detail.detailContentDescriptions = new CharSequence[length];
+ for (int i = 0; i < length; i++) {
+ final String packageName = packageNames[i];
+ final PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
+ final ApplicationInfo appInfo = ipm.getApplicationInfo(packageName,
+ 0 /* no flags */, userId);
+
+ if (appInfo != null) {
+ detail.detailLabels[i] = appInfo.loadLabel(pm).toString();
+ detail.detailContentDescriptions[i] = um.getBadgedLabelForUser(
+ detail.detailLabels[i], userHandle);
+ if (packageInfo.sharedUserLabel != 0) {
+ detail.label = pm.getText(packageName, packageInfo.sharedUserLabel,
+ packageInfo.applicationInfo).toString();
+ detail.icon = um.getBadgedIconForUser(appInfo.loadIcon(pm), userHandle);
+ }
+ }
+ }
+ }
+ detail.contentDescription = um.getBadgedLabelForUser(detail.label, userHandle);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Error while building UI detail for uid "+uid, e);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error while building UI detail for uid "+uid, e);
+ }
+
+ if (TextUtils.isEmpty(detail.label)) {
+ detail.label = Integer.toString(uid);
+ }
+
+ return detail;
+ }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 345f682..efacd2c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -104,6 +104,7 @@
<uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
<uses-permission android:name="android.permission.REGISTER_CONNECTION_MANAGER" />
<uses-permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION" />
+ <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
<application android:label="@string/app_label">
<provider
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 8ab53b8..e4fc075 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -168,14 +168,14 @@
android:permission="android.permission.BIND_WALLPAPER"
android:exported="true" />
- <receiver android:name=".BootReceiver" androidprv:primaryUserOnly="true">
+ <receiver android:name=".BootReceiver" androidprv:systemUserOnly="true">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".qs.tiles.HotspotTile$APChangedReceiver"
- androidprv:primaryUserOnly="true">
+ androidprv:systemUserOnly="true">
<intent-filter>
<action android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
</intent-filter>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 4e57f2b5..f99447c 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -435,6 +435,6 @@
<string name="remove_from_settings" msgid="8389591916603406378">"Supprimer des paramètres"</string>
<string name="remove_from_settings_prompt" msgid="6069085993355887748">"Supprimer « System UI Tuner » des paramètres et arrêter d\'utiliser toutes ses fonctionnalités?"</string>
<string name="activity_not_found" msgid="348423244327799974">"L\'application n\'est pas installée sur votre appareil"</string>
- <string name="clock_seconds" msgid="7689554147579179507">"Afficher les secondes de l\'horloge"</string>
- <string name="clock_seconds_desc" msgid="6282693067130470675">"Afficher les secondes de l\'horloge dans la barre d\'état. Cela peut influer sur l\'autonomie de la pile."</string>
+ <string name="clock_seconds" msgid="7689554147579179507">"Afficher les secondes sur l\'horloge"</string>
+ <string name="clock_seconds_desc" msgid="6282693067130470675">"Afficher les secondes sur l\'horloge dans la barre d\'état. Cela peut réduire l\'autonomie de la pile."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index b9bfba9..9cb2fc8 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -88,7 +88,7 @@
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"音声アシスト"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"ロック解除"</string>
<string name="accessibility_unlock_button_fingerprint" msgid="8214125623493923751">"ロック解除ボタン、指紋を待っています"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"指紋を使用せずにロック解除"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"指紋認証を使用せずにロック解除"</string>
<string name="unlock_label" msgid="8779712358041029439">"ロック解除"</string>
<string name="phone_label" msgid="2320074140205331708">"電話を起動"</string>
<string name="voice_assist_label" msgid="3956854378310019854">"音声アシストを開く"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 2a84362..adc9b36 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.graphics.Typeface;
import android.media.projection.MediaProjectionManager;
import android.media.projection.IMediaProjectionManager;
import android.media.projection.IMediaProjection;
@@ -29,7 +30,14 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.text.BidiFormatter;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.StyleSpan;
import android.util.Log;
+import android.util.TypedValue;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
@@ -39,6 +47,8 @@
implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener,
DialogInterface.OnCancelListener {
private static final String TAG = "MediaProjectionPermissionActivity";
+ private static final float MAX_APP_NAME_SIZE_PX = 500f;
+ private static final String ELLIPSIS = "\u2026";
private boolean mPermanentGrant;
private String mPackageName;
@@ -84,11 +94,49 @@
return;
}
- String appName = aInfo.loadLabel(packageManager).toString();
+ TextPaint paint = new TextPaint();
+ paint.setTextSize(42);
+
+ String label = aInfo.loadLabel(packageManager).toString();
+
+ // If the label contains new line characters it may push the security
+ // message below the fold of the dialog. Labels shouldn't have new line
+ // characters anyways, so just truncate the message the first time one
+ // is seen.
+ final int labelLength = label.length();
+ int offset = 0;
+ while (offset < labelLength) {
+ final int codePoint = label.codePointAt(offset);
+ final int type = Character.getType(codePoint);
+ if (type == Character.LINE_SEPARATOR
+ || type == Character.CONTROL
+ || type == Character.PARAGRAPH_SEPARATOR) {
+ label = label.substring(0, offset) + ELLIPSIS;
+ break;
+ }
+ offset += Character.charCount(codePoint);
+ }
+
+ if (label.isEmpty()) {
+ label = mPackageName;
+ }
+
+ String unsanitizedAppName = TextUtils.ellipsize(label,
+ paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString();
+ String appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
+
+ String actionText = getString(R.string.media_projection_dialog_text, appName);
+ SpannableString message = new SpannableString(actionText);
+
+ int appNameIndex = actionText.indexOf(appName);
+ if (appNameIndex >= 0) {
+ message.setSpan(new StyleSpan(Typeface.BOLD),
+ appNameIndex, appNameIndex + appName.length(), 0);
+ }
mDialog = new AlertDialog.Builder(this)
.setIcon(aInfo.loadIcon(packageManager))
- .setMessage(getString(R.string.media_projection_dialog_text, appName))
+ .setMessage(message)
.setPositiveButton(R.string.media_projection_action_text, this)
.setNegativeButton(android.R.string.cancel, this)
.setView(R.layout.remember_permission_checkbox)
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 803a014..728d558 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -21,6 +21,7 @@
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
import android.net.Uri;
import android.os.Looper;
import android.os.PowerManager;
@@ -36,7 +37,7 @@
* - whenever audio is played, audio focus is requested,
* - whenever audio playback is stopped or the playback completed, audio focus is abandoned.
*/
-public class NotificationPlayer implements OnCompletionListener {
+public class NotificationPlayer implements OnCompletionListener, OnErrorListener {
private static final int PLAY = 1;
private static final int STOP = 2;
private static final boolean mDebug = false;
@@ -112,6 +113,7 @@
// done playing. This class should be modified to use a single thread, on which
// command are issued, and on which it receives the completion callbacks.
player.setOnCompletionListener(NotificationPlayer.this);
+ player.setOnErrorListener(NotificationPlayer.this);
player.start();
if (mPlayer != null) {
mPlayer.release();
@@ -245,6 +247,13 @@
}
}
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ Log.e(mTag, "error " + what + " (extra=" + extra + ") playing notification");
+ // error happened, handle it just like a completion
+ onCompletion(mp);
+ return true;
+ }
+
private String mTag;
private CmdThread mThread;
private CreationAndCompletionThread mCompletionThread;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index d0cd6cb..b01a2a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -64,7 +64,8 @@
private static final int MSG_APP_TRANSITION_STARTING = 21 << MSG_SHIFT;
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_SHOW_KEYBOARD_SHORTCUTS = 24 << MSG_SHIFT;
+ private static final int MSG_CAMERA_LAUNCH_GESTURE = 24 << MSG_SHIFT;
+ private static final int MSG_SHOW_KEYBOARD_SHORTCUTS = 25 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -111,6 +112,7 @@
public void appTransitionStarting(long startTime, long duration);
public void showAssistDisclosure();
public void startAssist(Bundle args);
+ public void onCameraLaunchGestureDetected();
}
public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -303,6 +305,14 @@
}
}
+ @Override
+ public void onCameraLaunchGestureDetected() {
+ synchronized (mList) {
+ mHandler.removeMessages(MSG_CAMERA_LAUNCH_GESTURE);
+ mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
public void handleMessage(Message msg) {
final int what = msg.what & MSG_MASK;
@@ -404,6 +414,9 @@
case MSG_START_ASSIST:
mCallbacks.startAssist((Bundle) msg.obj);
break;
+ case MSG_CAMERA_LAUNCH_GESTURE:
+ mCallbacks.onCameraLaunchGestureDetected();
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 2f6aec7..564a60a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -24,6 +24,7 @@
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.view.MotionEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 164c496..8058933 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -36,6 +36,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.ImageView;
+
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -79,6 +80,7 @@
private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
private boolean mSupportHardware;
private boolean mFinishing;
+ private boolean mLaunchingAffordance;
private CanvasProperty<Float> mHwCircleRadius;
private CanvasProperty<Float> mHwCenterX;
@@ -152,7 +154,7 @@
@Override
protected void onDraw(Canvas canvas) {
- mSupportHardware = canvas.isHardwareAccelerated();
+ mSupportHardware = false;//canvas.isHardwareAccelerated();
drawBackgroundCircle(canvas);
canvas.save();
canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2);
@@ -161,9 +163,11 @@
}
public void setPreviewView(View v) {
+ View oldPreviewView = mPreviewView;
mPreviewView = v;
if (mPreviewView != null) {
- mPreviewView.setVisibility(INVISIBLE);
+ mPreviewView.setVisibility(mLaunchingAffordance
+ ? oldPreviewView.getVisibility() : INVISIBLE);
}
}
@@ -176,7 +180,7 @@
}
private void drawBackgroundCircle(Canvas canvas) {
- if (mCircleRadius > 0) {
+ if (mCircleRadius > 0 || mFinishing) {
if (mFinishing && mSupportHardware) {
DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
displayListCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
@@ -207,11 +211,12 @@
cancelAnimator(mPreviewClipper);
mFinishing = true;
mCircleStartRadius = mCircleRadius;
- float maxCircleSize = getMaxCircleSize();
+ final float maxCircleSize = getMaxCircleSize();
Animator animatorToRadius;
if (mSupportHardware) {
initHwProperties();
animatorToRadius = getRtAnimatorToRadius(maxCircleSize);
+ startRtAlphaFadeIn();
} else {
animatorToRadius = getAnimatorToRadius(maxCircleSize);
}
@@ -222,6 +227,8 @@
public void onAnimationEnd(Animator animation) {
mAnimationEndRunnable.run();
mFinishing = false;
+ mCircleRadius = maxCircleSize;
+ invalidate();
}
});
animatorToRadius.start();
@@ -241,6 +248,36 @@
}
}
+ /**
+ * Fades in the Circle on the RenderThread. It's used when finishing the circle when it had
+ * alpha 0 in the beginning.
+ */
+ private void startRtAlphaFadeIn() {
+ if (mCircleRadius == 0 && mPreviewView == null) {
+ Paint modifiedPaint = new Paint(mCirclePaint);
+ modifiedPaint.setColor(mCircleColor);
+ modifiedPaint.setAlpha(0);
+ mHwCirclePaint = CanvasProperty.createPaint(modifiedPaint);
+ RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
+ RenderNodeAnimator.PAINT_ALPHA, 255);
+ animator.setTarget(this);
+ animator.setInterpolator(PhoneStatusBar.ALPHA_IN);
+ animator.setDuration(250);
+ animator.start();
+ }
+ }
+
+ public void instantFinishAnimation() {
+ cancelAnimator(mPreviewClipper);
+ if (mPreviewView != null) {
+ mPreviewView.setClipBounds(null);
+ mPreviewView.setVisibility(View.VISIBLE);
+ }
+ mCircleRadius = getMaxCircleSize();
+ setImageAlpha(0, false);
+ invalidate();
+ }
+
private void startRtCircleFadeOut(long duration) {
RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
RenderNodeAnimator.PAINT_ALPHA, 0);
@@ -443,6 +480,7 @@
public void setImageAlpha(float alpha, boolean animate, long duration,
Interpolator interpolator, Runnable runnable) {
cancelAnimator(mAlphaAnimator);
+ alpha = mLaunchingAffordance ? 0 : alpha;
int endAlpha = (int) (alpha * 255);
final Drawable background = getBackground();
if (!animate) {
@@ -509,4 +547,8 @@
return false;
}
}
+
+ public void setLaunchingAffordance(boolean launchingAffordance) {
+ mLaunchingAffordance = launchingAffordance;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index 01aa8d1..8688c28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -86,6 +86,9 @@
if (mBackground != null) {
mBackground.setCallback(this);
}
+ if (mBackground instanceof RippleDrawable) {
+ ((RippleDrawable) mBackground).setForceSoftware(true);
+ }
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppInfo.java
index 5ec5147..e34c821 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppInfo.java
@@ -17,24 +17,26 @@
package com.android.systemui.statusbar.phone;
import android.content.ComponentName;
+import android.os.UserHandle;
/**
* Navigation bar app information.
*/
class AppInfo {
private final ComponentName mComponentName;
- private final long mUserSerialNumber;
+ private final UserHandle mUser;
- public AppInfo(ComponentName componentName, long userSerialNumber) {
+ public AppInfo(ComponentName componentName, UserHandle user) {
+ if (componentName == null || user == null) throw new IllegalArgumentException();
mComponentName = componentName;
- mUserSerialNumber = userSerialNumber;
+ mUser = user;
}
public ComponentName getComponentName() {
return mComponentName;
}
- public long getUserSerialNumber() {
- return mUserSerialNumber;
+ public UserHandle getUser() {
+ return mUser;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java
index 076378e..f46d1a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/GetActivityIconTask.java
@@ -16,20 +16,21 @@
package com.android.systemui.statusbar.phone;
-import android.content.ComponentName;
+import android.app.AppGlobals;
+import android.content.pm.ActivityInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
+import android.os.RemoteException;
import android.util.Slog;
-import android.view.View;
import android.widget.ImageView;
/**
* Retrieves the icon for an activity and sets it as the Drawable on an ImageView. The ImageView
* is hidden if the activity isn't recognized or if there is no icon.
*/
-class GetActivityIconTask extends AsyncTask<ComponentName, Void, Drawable> {
+class GetActivityIconTask extends AsyncTask<AppInfo, Void, Drawable> {
private final static String TAG = "GetActivityIconTask";
private final PackageManager mPackageManager;
@@ -43,15 +44,27 @@
}
@Override
- protected Drawable doInBackground(ComponentName... params) {
+ protected Drawable doInBackground(AppInfo... params) {
if (params.length != 1) {
throw new IllegalArgumentException("Expected one parameter");
}
- ComponentName activityName = params[0];
+ AppInfo appInfo = params[0];
try {
- return mPackageManager.getActivityIcon(activityName);
- } catch (NameNotFoundException e) {
- Slog.w(TAG, "Icon not found for " + activityName);
+ IPackageManager mPM = AppGlobals.getPackageManager();
+ ActivityInfo ai = mPM.getActivityInfo(
+ appInfo.getComponentName(),
+ 0,
+ appInfo.getUser().getIdentifier());
+
+ if (ai == null) {
+ Slog.w(TAG, "Icon not found for " + appInfo);
+ return null;
+ }
+
+ Drawable unbadgedIcon = ai.loadIcon(mPackageManager);
+ return mPackageManager.getUserBadgedIcon(unbadgedIcon, appInfo.getUser());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Icon not found for " + appInfo, e);
return null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 10019f9..60ebfdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -86,9 +86,9 @@
mContext = context;
mCallback = callback;
initIcons();
- updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false, true);
- updateIcon(mCenterIcon, 0.0f, mCenterIcon.getRestingAlpha(), false, false, true);
- updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false, true);
+ updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false, true, false);
+ updateIcon(mCenterIcon, 0.0f, mCenterIcon.getRestingAlpha(), false, false, true, false);
+ updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false, true, false);
initDimens();
}
@@ -144,9 +144,7 @@
} else {
mTouchSlopExeeded = false;
}
- mCallback.onSwipingStarted(targetView == mRightIcon);
- mSwipingInProgress = true;
- mTargetedView = targetView;
+ startSwiping(targetView);
mInitialTouchX = x;
mInitialTouchY = y;
mTranslationOnDown = mTranslation;
@@ -192,6 +190,12 @@
return true;
}
+ private void startSwiping(View targetView) {
+ mCallback.onSwipingStarted(targetView == mRightIcon);
+ mSwipingInProgress = true;
+ mTargetedView = targetView;
+ }
+
private View getIconAtPosition(float x, float y) {
if (leftSwipePossible() && isOnIcon(mLeftIcon, x, y)) {
return mLeftIcon;
@@ -324,7 +328,7 @@
boolean velIsInWrongDirection = vel * mTranslation < 0;
snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
- fling(vel, snapBack || forceSnapBack);
+ fling(vel, snapBack || forceSnapBack, mTranslation < 0);
}
private boolean isBelowFalsingThreshold() {
@@ -336,9 +340,8 @@
return (int) (mMinTranslationAmount * factor);
}
- private void fling(float vel, final boolean snapBack) {
- float target = mTranslation < 0
- ? -mCallback.getMaxTranslationDistance()
+ private void fling(float vel, final boolean snapBack, boolean right) {
+ float target = right ? -mCallback.getMaxTranslationDistance()
: mCallback.getMaxTranslationDistance();
target = snapBack ? 0 : target;
@@ -352,8 +355,8 @@
});
animator.addListener(mFlingEndListener);
if (!snapBack) {
- startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable);
- mCallback.onAnimationToSideStarted(mTranslation < 0, mTranslation, vel);
+ startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable, right);
+ mCallback.onAnimationToSideStarted(right, mTranslation, vel);
} else {
reset(true);
}
@@ -364,8 +367,9 @@
}
}
- private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable) {
- KeyguardAffordanceView targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
+ private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable,
+ boolean right) {
+ KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
targetView.finishAnimation(velocity, mAnimationEndRunnable);
}
@@ -383,19 +387,20 @@
fadeOutAlpha = Math.max(fadeOutAlpha, 0.0f);
boolean animateIcons = isReset && animateReset;
+ boolean forceNoCircleAnimation = isReset && !animateReset;
float radius = getRadiusFromTranslation(absTranslation);
boolean slowAnimation = isReset && isBelowFalsingThreshold();
if (!isReset) {
updateIcon(targetView, radius, alpha + fadeOutAlpha * targetView.getRestingAlpha(),
- false, false, false);
+ false, false, false, false);
} else {
updateIcon(targetView, 0.0f, fadeOutAlpha * targetView.getRestingAlpha(),
- animateIcons, slowAnimation, false);
+ animateIcons, slowAnimation, false, forceNoCircleAnimation);
}
updateIcon(otherView, 0.0f, fadeOutAlpha * otherView.getRestingAlpha(),
- animateIcons, slowAnimation, false);
+ animateIcons, slowAnimation, false, forceNoCircleAnimation);
updateIcon(mCenterIcon, 0.0f, fadeOutAlpha * mCenterIcon.getRestingAlpha(),
- animateIcons, slowAnimation, false);
+ animateIcons, slowAnimation, false, forceNoCircleAnimation);
mTranslation = translation;
}
@@ -431,16 +436,21 @@
public void animateHideLeftRightIcon() {
cancelAnimation();
- updateIcon(mRightIcon, 0f, 0f, true, false, false);
- updateIcon(mLeftIcon, 0f, 0f, true, false, false);
+ updateIcon(mRightIcon, 0f, 0f, true, false, false, false);
+ updateIcon(mLeftIcon, 0f, 0f, true, false, false, false);
}
private void updateIcon(KeyguardAffordanceView view, float circleRadius, float alpha,
- boolean animate, boolean slowRadiusAnimation, boolean force) {
+ boolean animate, boolean slowRadiusAnimation, boolean force,
+ boolean forceNoCircleAnimation) {
if (view.getVisibility() != View.VISIBLE && !force) {
return;
}
- view.setCircleRadius(circleRadius, slowRadiusAnimation);
+ if (forceNoCircleAnimation) {
+ view.setCircleRadiusWithoutAnimation(circleRadius);
+ } else {
+ view.setCircleRadius(circleRadius, slowRadiusAnimation);
+ }
updateIconAlpha(view, alpha, animate);
}
@@ -503,8 +513,36 @@
mMotionCancelled = true;
if (mSwipingInProgress) {
mCallback.onSwipingAborted();
+ mSwipingInProgress = false;
}
- mSwipingInProgress = false;
+ }
+
+ public boolean isSwipingInProgress() {
+ return mSwipingInProgress;
+ }
+
+ public void launchAffordance(boolean animate, boolean left) {
+ if (mSwipingInProgress) {
+ // We don't want to mess with the state if the user is actually swiping already.
+ return;
+ }
+ KeyguardAffordanceView targetView = left ? mLeftIcon : mRightIcon;
+ KeyguardAffordanceView otherView = left ? mRightIcon : mLeftIcon;
+ startSwiping(targetView);
+ if (animate) {
+ fling(0, false, !left);
+ updateIcon(otherView, 0.0f, 0, true, false, true, false);
+ updateIcon(mCenterIcon, 0.0f, 0, true, false, true, false);
+ } else {
+ mCallback.onAnimationToSideStarted(!left, mTranslation, 0);
+ mTranslation = left ? mCallback.getMaxTranslationDistance()
+ : mCallback.getMaxTranslationDistance();
+ updateIcon(mCenterIcon, 0.0f, 0.0f, false, false, true, false);
+ updateIcon(otherView, 0.0f, 0.0f, false, false, true, false);
+ targetView.instantFinishAnimation();
+ mFlingEndListener.onAnimationEnd(null);
+ mAnimationEndRunnable.run();
+ }
}
public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 579889d..d30411a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -80,7 +80,7 @@
private static final Intent SECURE_CAMERA_INTENT =
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- private static final Intent INSECURE_CAMERA_INTENT =
+ public static final Intent INSECURE_CAMERA_INTENT =
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index cb5c125..adcec98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -28,14 +28,12 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.Slog;
import android.view.DragEvent;
import android.view.LayoutInflater;
@@ -48,8 +46,6 @@
import com.android.internal.content.PackageMonitor;
import com.android.systemui.R;
-import java.util.List;
-
/**
* Container for application icons that appear in the navigation bar. Their appearance is similar
* to the launcher hotseat. Clicking an icon launches the associated activity. A long click will
@@ -84,8 +80,6 @@
// When the user is not dragging this member is null.
private View mDragView;
- private long mCurrentUserSerialNumber = -1;
-
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -93,6 +87,9 @@
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
int currentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
onUserSwitched(currentUserId);
+ } else if (Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+ UserHandle removedProfile = intent.getParcelableExtra(Intent.EXTRA_USER);
+ onManagedProfileRemoved(removedProfile);
}
}
};
@@ -173,17 +170,17 @@
}
private void removeIfUnlauncheable(String packageName, UserHandle user) {
- long appUserSerialNumber = mUserManager.getSerialNumberForUser(user);
-
// Remove icons for all apps that match a package that perhaps became unlauncheable.
for(int i = sAppsModel.getAppCount() - 1; i >= 0; --i) {
AppInfo appInfo = sAppsModel.getApp(i);
- if (appInfo.getUserSerialNumber() != appUserSerialNumber) continue;
+ if (!appInfo.getUser().equals(user)) continue;
ComponentName appComponentName = appInfo.getComponentName();
if (!appComponentName.getPackageName().equals(packageName)) continue;
- if (sAppsModel.buildAppLaunchIntent(appComponentName, user) != null) continue;
+ if (sAppsModel.buildAppLaunchIntent(appInfo) != null) {
+ continue;
+ }
removeViewAt(i);
sAppsModel.removeApp(i);
@@ -206,14 +203,12 @@
transition.enableTransitionType(LayoutTransition.CHANGING);
parent.setLayoutTransition(transition);
- int currentUserId = ActivityManager.getCurrentUser();
- mCurrentUserSerialNumber = mUserManager.getSerialNumberForUser(
- new UserHandle(currentUserId));
- sAppsModel.setCurrentUser(currentUserId);
+ sAppsModel.setCurrentUser(ActivityManager.getCurrentUser());
recreateAppButtons();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
mContext.registerReceiver(mBroadcastReceiver, filter);
mAppPackageMonitor.register(mContext, null, UserHandle.ALL, true);
@@ -244,7 +239,7 @@
button.setContentDescription(appLabel);
// Load the icon asynchronously.
- new GetActivityIconTask(mPackageManager, button).execute(app.getComponentName());
+ new GetActivityIconTask(mPackageManager, button).execute(app);
}
}
@@ -280,7 +275,7 @@
*/
@Nullable
static CharSequence getAppLabel(PackageManager packageManager,
- ComponentName activityName) {
+ ComponentName activityName) {
String packageName = activityName.getPackageName();
ApplicationInfo info;
try {
@@ -296,7 +291,10 @@
static void startAppDrag(ImageView icon, AppInfo appInfo) {
// The drag data is an Intent to launch the activity.
Intent mainIntent = Intent.makeMainActivity(appInfo.getComponentName());
- mainIntent.putExtra(EXTRA_PROFILE, appInfo.getUserSerialNumber());
+ UserManager userManager =
+ (UserManager) icon.getContext().getSystemService(Context.USER_SERVICE);
+ long userSerialNumber = userManager.getSerialNumberForUser(appInfo.getUser());
+ mainIntent.putExtra(EXTRA_PROFILE, userSerialNumber);
ClipData dragData = ClipData.newIntent("", mainIntent);
// Use the ImageView to create the shadow.
View.DragShadowBuilder shadow = new AppIconDragShadowBuilder(icon);
@@ -462,27 +460,38 @@
if (data.getItemCount() != 1) {
return null;
}
- Intent intent = data.getItemAt(0).getIntent();
+ ClipData.Item item = data.getItemAt(0);
+ if (item == null) {
+ return null;
+ }
+ Intent intent = item.getIntent();
if (intent == null) {
return null;
}
-
long userSerialNumber = intent.getLongExtra(EXTRA_PROFILE, -1);
-
- // Validate the received user serial number.
+ if (userSerialNumber == -1) {
+ return null;
+ }
UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber);
if (appUser == null) {
- userSerialNumber = mCurrentUserSerialNumber;
+ return null;
}
-
- return new AppInfo(intent.getComponent(), userSerialNumber);
+ ComponentName componentName = intent.getComponent();
+ if (componentName == null) {
+ return null;
+ }
+ AppInfo appInfo = new AppInfo(componentName, appUser);
+ if (sAppsModel.buildAppLaunchIntent(appInfo) == null) {
+ return null;
+ }
+ return appInfo;
}
/** Updates the app at a given view index. */
private void updateAppAt(int index, AppInfo appInfo) {
sAppsModel.setApp(index, appInfo);
ImageView button = (ImageView) getChildAt(index);
- new GetActivityIconTask(mPackageManager, button).execute(appInfo.getComponentName());
+ new GetActivityIconTask(mPackageManager, button).execute(appInfo);
}
/** Removes the empty placeholder view and cleans up the data model. */
@@ -540,20 +549,10 @@
@Override
public void onClick(View v) {
AppInfo appInfo = sAppsModel.getApp(indexOfChild(v));
- ComponentName component = appInfo.getComponentName();
-
- long appUserSerialNumber = appInfo.getUserSerialNumber();
- UserHandle appUser = mUserManager.getUserForSerialNumber(appUserSerialNumber);
- if (appUser == null) {
- Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Can't start activity " + component +
- " because its user doesn't exist.");
- return;
- }
-
- Intent launchIntent = sAppsModel.buildAppLaunchIntent(component, appUser);
+ Intent launchIntent = sAppsModel.buildAppLaunchIntent(appInfo);
if (launchIntent == null) {
- Toast.makeText(getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Toast.makeText(
+ getContext(), R.string.activity_not_found, Toast.LENGTH_SHORT).show();
return;
}
@@ -568,18 +567,25 @@
Bundle optsBundle = opts.toBundle();
launchIntent.setSourceBounds(sourceBounds);
- mContext.startActivityAsUser(launchIntent, optsBundle, appUser);
+ mContext.startActivityAsUser(launchIntent, optsBundle, appInfo.getUser());
}
}
private void onUserSwitched(int currentUserId) {
- final long newUserSerialNumber =
- mUserManager.getSerialNumberForUser(new UserHandle(currentUserId));
+ sAppsModel.setCurrentUser(currentUserId);
+ recreateAppButtons();
+ }
- if (newUserSerialNumber != mCurrentUserSerialNumber) {
- mCurrentUserSerialNumber = newUserSerialNumber;
- sAppsModel.setCurrentUser(currentUserId);
- recreateAppButtons();
+ private void onManagedProfileRemoved(UserHandle removedProfile) {
+ boolean removedApps = false;
+ for(int i = sAppsModel.getAppCount() - 1; i >= 0; --i) {
+ AppInfo appInfo = sAppsModel.getApp(i);
+ if (!appInfo.getUser().equals(removedProfile)) continue;
+
+ removeViewAt(i);
+ sAppsModel.removeApp(i);
+ removedApps = true;
}
+ if (removedApps) sAppsModel.savePrefs();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
index c4c31fd..c7b9e1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
@@ -106,9 +106,24 @@
return AppGlobals.getPackageManager();
}
- // Returns a launch intent for a given component, or null if the component is unlauncheable.
- public Intent buildAppLaunchIntent(ComponentName component, UserHandle appUser) {
- int appUserId = appUser.getIdentifier();
+ // Returns a launch intent for a given app info, or null if the app info is unlauncheable.
+ public Intent buildAppLaunchIntent(AppInfo appInfo) {
+ ComponentName component = appInfo.getComponentName();
+ int appUserId = appInfo.getUser().getIdentifier();
+
+ if (mCurrentUserId != appUserId) {
+ // Check if app user is a profile of current user and the app user is enabled.
+ UserInfo appUserInfo = mUserManager.getUserInfo(appUserId);
+ UserInfo currentUserInfo = mUserManager.getUserInfo(mCurrentUserId);
+ if (appUserInfo == null || currentUserInfo == null
+ || appUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
+ || appUserInfo.profileGroupId != currentUserInfo.profileGroupId
+ || !appUserInfo.isEnabled()) {
+ Log.e(TAG, "User " + appUserId +
+ " is is not a profile of the current user, or is disabled.");
+ return null;
+ }
+ }
// This code is based on LauncherAppsService.startActivityAsUser code.
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
@@ -164,7 +179,7 @@
int appCount = mPrefs.getInt(userPrefixed(APP_COUNT_PREF), -1);
if (appCount >= 0) {
- loadAppsFromPrefs();
+ loadAppsFromPrefs(appCount);
} else {
// We switched to this user for the first time ever. This is a good opportunity to clean
// prefs for users deleted in the past.
@@ -250,42 +265,52 @@
final AppInfo appInfo = mApps.get(i);
String componentNameString = appInfo.getComponentName().flattenToString();
edit.putString(prefNameForApp(i), componentNameString);
- edit.putLong(prefUserForApp(i), appInfo.getUserSerialNumber());
+ long userSerialNumber = mUserManager.getSerialNumberForUser(appInfo.getUser());
+ edit.putLong(prefUserForApp(i), userSerialNumber);
}
// Start an asynchronous disk write.
edit.apply();
}
+ /** Loads AppInfo from prefs. Returns null if something is wrong. */
+ private AppInfo loadAppFromPrefs(int index) {
+ String prefValue = mPrefs.getString(prefNameForApp(index), null);
+ if (prefValue == null) {
+ Slog.w(TAG, "Couldn't find pref " + prefNameForApp(index));
+ return null;
+ }
+ ComponentName componentName = ComponentName.unflattenFromString(prefValue);
+ if (componentName == null) {
+ Slog.w(TAG, "Invalid component name " + prefValue);
+ return null;
+ }
+ long userSerialNumber = mPrefs.getLong(prefUserForApp(index), -1);
+ if (userSerialNumber == -1) {
+ Slog.w(TAG, "Couldn't find pref " + prefUserForApp(index));
+ return null;
+ }
+ UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber);
+ if (appUser == null) {
+ Slog.w(TAG, "No user for serial " + userSerialNumber);
+ return null;
+ }
+ AppInfo appInfo = new AppInfo(componentName, appUser);
+ if (buildAppLaunchIntent(appInfo) == null) {
+ return null;
+ }
+ return appInfo;
+ }
+
/** Loads the list of apps from SharedPreferences. */
- private void loadAppsFromPrefs() {
- UserManager mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-
- boolean hadUnlauncheableApps = false;
-
- int appCount = mPrefs.getInt(userPrefixed(APP_COUNT_PREF), -1);
+ private void loadAppsFromPrefs(int appCount) {
for (int i = 0; i < appCount; i++) {
- String prefValue = mPrefs.getString(prefNameForApp(i), null);
- if (prefValue == null) {
- Slog.w(TAG, "Couldn't find pref " + prefNameForApp(i));
- // Couldn't find the saved state. Just skip this item.
- continue;
- }
- ComponentName componentName = ComponentName.unflattenFromString(prefValue);
- long userSerialNumber = mPrefs.getLong(prefUserForApp(i), -1);
- if (userSerialNumber == -1) {
- Slog.w(TAG, "Couldn't find pref " + prefUserForApp(i));
- // Couldn't find the saved state. Just skip this item.
- continue;
- }
- UserHandle appUser = mUserManager.getUserForSerialNumber(userSerialNumber);
- if (appUser != null && buildAppLaunchIntent(componentName, appUser) != null) {
- mApps.add(new AppInfo(componentName, userSerialNumber));
- } else {
- hadUnlauncheableApps = true;
+ AppInfo appInfo = loadAppFromPrefs(i);
+ if (appInfo != null) {
+ mApps.add(appInfo);
}
}
- if (hadUnlauncheableApps) savePrefs();
+ if (appCount != mApps.size()) savePrefs();
}
/** Adds the first few apps from the owner profile. Used for demo purposes. */
@@ -301,7 +326,7 @@
ResolveInfo ri = apps.get(i);
ComponentName componentName = new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name);
- mApps.add(new AppInfo(componentName, mCurrentUserSerialNumber));
+ mApps.add(new AppInfo(componentName, new UserHandle(mCurrentUserId)));
}
savePrefs();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarRecents.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarRecents.java
index b024ec4..1f71bc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarRecents.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarRecents.java
@@ -25,11 +25,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.UserManager;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
@@ -160,7 +161,8 @@
button.setVisibility(View.VISIBLE);
// Load the activity icon on a background thread.
- new GetActivityIconTask(mPackageManager, button).execute(getRealActivityForTask(task));
+ AppInfo app = new AppInfo(activityName, new UserHandle(task.userId));
+ new GetActivityIconTask(mPackageManager, button).execute(app);
final int taskPersistentId = task.persistentId;
button.setOnClickListener(new View.OnClickListener() {
@@ -171,7 +173,9 @@
try {
manager.startActivityFromRecents(taskPersistentId, null /* options */);
} catch (RemoteException e) {
- e.printStackTrace();
+ Log.e(TAG, "Exception when activating a recent task", e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Exception when activating a recent task", e);
}
}
});
@@ -218,6 +222,35 @@
mContext = context;
}
+ private ComponentName getLaunchComponentForPackage(String packageName, int userId) {
+ // This code is based on ApplicationPackageManager.getLaunchIntentForPackage.
+ PackageManager packageManager = mContext.getPackageManager();
+
+ // First see if the package has an INFO activity; the existence of
+ // such an activity is implied to be the desired front-door for the
+ // overall package (such as if it has multiple launcher entries).
+ Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+ intentToResolve.addCategory(Intent.CATEGORY_INFO);
+ intentToResolve.setPackage(packageName);
+ List<ResolveInfo> ris = packageManager.queryIntentActivitiesAsUser(
+ intentToResolve, 0, userId);
+
+ // Otherwise, try to find a main launcher activity.
+ if (ris == null || ris.size() <= 0) {
+ // reuse the intent instance
+ intentToResolve.removeCategory(Intent.CATEGORY_INFO);
+ intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+ intentToResolve.setPackage(packageName);
+ ris = packageManager.queryIntentActivitiesAsUser(intentToResolve, 0, userId);
+ }
+ if (ris == null || ris.size() <= 0) {
+ Log.e(TAG, "Failed to build intent for " + packageName);
+ return null;
+ }
+ return new ComponentName(ris.get(0).activityInfo.packageName,
+ ris.get(0).activityInfo.name);
+ }
+
@Override
public boolean onLongClick(View v) {
ImageView icon = (ImageView) v;
@@ -226,17 +259,15 @@
// for the task's package.
RecentTaskInfo task = (RecentTaskInfo) v.getTag();
String packageName = getRealActivityForTask(task).getPackageName();
- Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(packageName);
- if (intent == null) {
+ ComponentName component = getLaunchComponentForPackage(packageName, task.userId);
+ if (component == null) {
return false;
}
- if (DEBUG) Slog.d(TAG, "Start drag with " + intent);
+ if (DEBUG) Slog.d(TAG, "Start drag with " + component);
- UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- long userSerialNumber = userManager.getSerialNumberForUser(new UserHandle(task.userId));
NavigationBarApps.startAppDrag(
- icon, new AppInfo(intent.getComponent(), userSerialNumber));
+ icon, new AppInfo(component, new UserHandle(task.userId)));
return true;
}
}
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 24ae47d..3ad7246 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -204,6 +204,7 @@
private int mLastOrientation = -1;
private boolean mClosingWithAlphaFadeOut;
private boolean mHeadsUpAnimatingAway;
+ private boolean mLaunchingAffordance;
private LockedPhoneAnalytics mLockedPhoneAnalytics;
private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
@@ -491,7 +492,9 @@
mIsLaunchTransitionFinished = false;
mBlockTouches = false;
mUnlockIconActive = false;
- mAfforanceHelper.reset(true);
+ if (!mLaunchingAffordance) {
+ mAfforanceHelper.reset(false);
+ }
closeQs();
mStatusBar.dismissPopups();
mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
@@ -2430,4 +2433,36 @@
public boolean hasOverlappingRendering() {
return !mDozing;
}
+
+ public void launchCamera(boolean animate) {
+ // If we are launching it when we are occluded already we don't want it to animate,
+ // nor setting these flags, since the occluded state doesn't change anymore, hence it's
+ // never reset.
+ if (!isFullyCollapsed()) {
+ mLaunchingAffordance = true;
+ setLaunchingAffordance(true);
+ } else {
+ animate = false;
+ }
+ mAfforanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL);
+ }
+
+ public void onAffordanceLaunchEnded() {
+ mLaunchingAffordance = false;
+ setLaunchingAffordance(false);
+ }
+
+ /**
+ * Set whether we are currently launching an affordance. This is currently only set when
+ * launched via a camera gesture.
+ */
+ private void setLaunchingAffordance(boolean launchingAffordance) {
+ getLeftIcon().setLaunchingAffordance(launchingAffordance);
+ getRightIcon().setLaunchingAffordance(launchingAffordance);
+ getCenterIcon().setLaunchingAffordance(launchingAffordance);
+ }
+
+ public boolean canCameraGestureBeLaunched() {
+ return !mAfforanceHelper.isSwipingInProgress();
+ }
}
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 278a7e7..7b04da0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -66,6 +66,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -485,6 +486,9 @@
private Runnable mLaunchTransitionEndRunnable;
private boolean mLaunchTransitionFadingAway;
private ExpandableNotificationRow mDraggedDownRow;
+ private boolean mLaunchCameraOnScreenTurningOn;
+ private PowerManager.WakeLock mGestureWakeLock;
+ private Vibrator mVibrator;
// Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
private int mLastLoggedStateFingerprint;
@@ -919,7 +923,9 @@
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBroadcastReceiver.onReceive(mContext,
new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
-
+ mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
+ "GestureWakeLock");
+ mVibrator = mContext.getSystemService(Vibrator.class);
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -3425,6 +3431,8 @@
private void onLaunchTransitionFadingEnded() {
mNotificationPanel.setAlpha(1.0f);
+ mNotificationPanel.onAffordanceLaunchEnded();
+ releaseGestureWakeLock();
runLaunchTransitionEndRunnable();
mLaunchTransitionFadingAway = false;
mScrimController.forceHideScrims(false /* hide */);
@@ -3512,6 +3520,8 @@
private void onLaunchTransitionTimeout() {
Log.w(TAG, "Launch transition: Timeout!");
+ mNotificationPanel.onAffordanceLaunchEnded();
+ releaseGestureWakeLock();
mNotificationPanel.resetViews();
}
@@ -3563,10 +3573,18 @@
mQSPanel.refreshAllTiles();
}
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+ releaseGestureWakeLock();
+ mNotificationPanel.onAffordanceLaunchEnded();
mNotificationPanel.setAlpha(1f);
return staying;
}
+ private void releaseGestureWakeLock() {
+ if (mGestureWakeLock.isHeld()) {
+ mGestureWakeLock.release();
+ }
+ }
+
public long calculateGoingToFullShadeDelay() {
return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
}
@@ -3704,6 +3722,11 @@
return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed();
}
+ public void endAffordanceLaunch() {
+ releaseGestureWakeLock();
+ mNotificationPanel.onAffordanceLaunchEnded();
+ }
+
public boolean onBackPressed() {
if (mStatusBarKeyguardViewManager.onBackPressed()) {
return true;
@@ -3939,6 +3962,9 @@
}
public void onFinishedGoingToSleep() {
+ mNotificationPanel.onAffordanceLaunchEnded();
+ releaseGestureWakeLock();
+ mLaunchCameraOnScreenTurningOn = false;
mDeviceInteractive = false;
mWakeUpComingFromTouch = false;
mWakeUpTouchLocation = null;
@@ -3957,6 +3983,14 @@
public void onScreenTurningOn() {
mNotificationPanel.onScreenTurningOn();
+ if (mLaunchCameraOnScreenTurningOn) {
+ mNotificationPanel.launchCamera(false);
+ mLaunchCameraOnScreenTurningOn = false;
+ }
+ }
+
+ private void vibrateForCameraGesture() {
+ mVibrator.vibrate(1000L);
}
public void onScreenTurnedOn() {
@@ -4114,6 +4148,39 @@
}
}
+ @Override
+ public void onCameraLaunchGestureDetected() {
+ if (!mNotificationPanel.canCameraGestureBeLaunched()) {
+ return;
+ }
+ if (!mDeviceInteractive) {
+ PowerManager pm = mContext.getSystemService(PowerManager.class);
+ pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:CAMERA_GESTURE");
+ mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
+ }
+ vibrateForCameraGesture();
+ if (!mStatusBarKeyguardViewManager.isShowing()) {
+ startActivity(KeyguardBottomAreaView.INSECURE_CAMERA_INTENT,
+ true /* dismissShade */);
+ } else {
+ if (!mDeviceInteractive) {
+ // Avoid flickering of the scrim when we instant launch the camera and the bouncer
+ // comes on.
+ mScrimController.dontAnimateBouncerChangesUntilNextFrame();
+ mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+ }
+ if (mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
+ mNotificationPanel.launchCamera(mDeviceInteractive /* animate */);
+ } else {
+ // We need to defer the camera launch until the screen comes on, since otherwise
+ // we will dismiss us too early since we are waiting on an activity to be drawn and
+ // incorrectly get notified because of the screen on event (which resumes and pauses
+ // some activities)
+ mLaunchCameraOnScreenTurningOn = true;
+ }
+ }
+ }
+
public void notifyFpAuthModeChanged() {
updateDozing();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index cc6f396..b9e9292 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -87,6 +87,7 @@
private View mDraggedHeadsUpView;
private boolean mForceHideScrims;
private boolean mSkipFirstFrame;
+ private boolean mDontAnimateBouncerChanges;
public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
boolean scrimSrcEnabled) {
@@ -125,7 +126,7 @@
public void setBouncerShowing(boolean showing) {
mBouncerShowing = showing;
- mAnimateChange = !mExpanding;
+ mAnimateChange = !mExpanding && !mDontAnimateBouncerChanges;
scheduleUpdate();
}
@@ -360,6 +361,9 @@
public boolean onPreDraw() {
mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
mUpdatePending = false;
+ if (mDontAnimateBouncerChanges) {
+ mDontAnimateBouncerChanges = false;
+ }
updateScrims();
mDurationOverride = -1;
mAnimationDelay = 0;
@@ -496,4 +500,8 @@
mAnimateChange = false;
scheduleUpdate();
}
+
+ public void dontAnimateBouncerChangesUntilNextFrame() {
+ mDontAnimateBouncerChanges = true;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index a1e9ece..18db5b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -19,6 +19,7 @@
import android.animation.Animator.AnimatorListener;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.graphics.drawable.RippleDrawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 7ee47df..e9c4e49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -26,6 +26,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.TypedValue;
@@ -184,6 +185,12 @@
}
});
requestCaptureValues();
+
+ // RenderThread is doing more harm than good when touching the header (to expand quick
+ // settings), so disable it for this view
+ ((RippleDrawable) getBackground()).setForceSoftware(true);
+ ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
+ ((RippleDrawable) mSystemIconsSuperContainer.getBackground()).setForceSoftware(true);
}
@Override
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 59ee5c5..d0604c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -179,6 +179,10 @@
mPhoneStatusBar.onScreenTurningOn();
}
+ public boolean isScreenTurnedOn() {
+ return mScreenTurnedOn;
+ }
+
public void onScreenTurnedOn() {
mScreenTurnedOn = true;
if (mDeferScrimFadeOut) {
@@ -385,6 +389,7 @@
*/
public boolean onBackPressed() {
if (mBouncer.isShowing()) {
+ mPhoneStatusBar.endAffordanceLaunch();
reset();
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 920b682..2587b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -171,6 +171,10 @@
}
@Override
+ public void onCameraLaunchGestureDetected() {
+ }
+
+ @Override
protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldInterrupt,
boolean alertAgain) {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
index 4d0e28b..fce6b29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
@@ -72,7 +72,7 @@
mMockEdit = mock(SharedPreferences.Editor.class);
mMockUserManager = mock(UserManager.class);
- when (context.getSharedPreferences(
+ when(context.getSharedPreferences(
"com.android.systemui.navbarapps", Context.MODE_PRIVATE)).thenReturn(mMockPrefs);
when(context.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager);
when(context.getPackageManager()).thenReturn(mMockPackageManager);
@@ -84,9 +84,22 @@
when(mMockPrefs.getInt("version", -1)).thenReturn(3);
when(mMockPrefs.edit()).thenReturn(mMockEdit);
- when(mMockUserManager.getSerialNumberForUser(new UserHandle(2))).thenReturn(22L);
- when(mMockUserManager.getUserForSerialNumber(45L)).thenReturn(new UserHandle(4));
- when(mMockUserManager.getUserForSerialNumber(239L)).thenReturn(new UserHandle(5));
+ when(mMockUserManager.getSerialNumberForUser(new UserHandle(2))).thenReturn(222L);
+ when(mMockUserManager.getSerialNumberForUser(new UserHandle(4))).thenReturn(444L);
+ when(mMockUserManager.getSerialNumberForUser(new UserHandle(5))).thenReturn(555L);
+ when(mMockUserManager.getUserForSerialNumber(222L)).thenReturn(new UserHandle(2));
+ when(mMockUserManager.getUserForSerialNumber(444L)).thenReturn(new UserHandle(4));
+ when(mMockUserManager.getUserForSerialNumber(555L)).thenReturn(new UserHandle(5));
+
+ UserInfo ui2 = new UserInfo();
+ ui2.profileGroupId = 999;
+ UserInfo ui4 = new UserInfo();
+ ui4.profileGroupId = 999;
+ UserInfo ui5 = new UserInfo();
+ ui5.profileGroupId = 999;
+ when(mMockUserManager.getUserInfo(2)).thenReturn(ui2);
+ when(mMockUserManager.getUserInfo(4)).thenReturn(ui4);
+ when(mMockUserManager.getUserInfo(5)).thenReturn(ui5);
mModel = new NavigationBarAppsModel(context) {
@Override
@@ -134,19 +147,24 @@
.queryIntentActivitiesAsUser(any(Intent.class), eq(0), any(int.class)))
.thenReturn(Arrays.asList(ri0, ri1));
+ mModel.setCurrentUser(3);
// Unlauncheable (for various reasons) apps.
assertEquals(null, mModel.buildAppLaunchIntent(
- new ComponentName("package0", "class0"), new UserHandle(3)));
+ new AppInfo(new ComponentName("package0", "class0"), new UserHandle(3))));
+ mModel.setCurrentUser(4);
assertEquals(null, mModel.buildAppLaunchIntent(
- new ComponentName("package1", "class1"), new UserHandle(4)));
+ new AppInfo(new ComponentName("package1", "class1"), new UserHandle(4))));
+ mModel.setCurrentUser(5);
assertEquals(null, mModel.buildAppLaunchIntent(
- new ComponentName("package2", "class2"), new UserHandle(5)));
+ new AppInfo(new ComponentName("package2", "class2"), new UserHandle(5))));
+ mModel.setCurrentUser(6);
assertEquals(null, mModel.buildAppLaunchIntent(
- new ComponentName("package3", "class3"), new UserHandle(6)));
+ new AppInfo(new ComponentName("package3", "class3"), new UserHandle(6))));
// A launcheable app.
+ mModel.setCurrentUser(7);
Intent intent = mModel.buildAppLaunchIntent(
- new ComponentName("package4", "class4"), new UserHandle(7));
+ new AppInfo(new ComponentName("package4", "class4"), new UserHandle(7)));
assertNotNull(intent);
assertEquals(new ComponentName("package4", "class4"), intent.getComponent());
assertEquals("package4", intent.getPackage());
@@ -155,13 +173,11 @@
/** Initializes the model from SharedPreferences for a few app activites. */
private void initializeModelFromPrefs() {
// Assume several apps are stored.
- when(mMockPrefs.getInt("22|app_count", -1)).thenReturn(3);
- when(mMockPrefs.getString("22|app_0", null)).thenReturn("package0/class0");
- when(mMockPrefs.getLong("22|app_user_0", -1)).thenReturn(-1L);
- when(mMockPrefs.getString("22|app_1", null)).thenReturn("package1/class1");
- when(mMockPrefs.getLong("22|app_user_1", -1)).thenReturn(45L);
- when(mMockPrefs.getString("22|app_2", null)).thenReturn("package2/class2");
- when(mMockPrefs.getLong("22|app_user_2", -1)).thenReturn(239L);
+ when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2);
+ when(mMockPrefs.getString("222|app_0", null)).thenReturn("package1/class1");
+ when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(444L);
+ when(mMockPrefs.getString("222|app_1", null)).thenReturn("package2/class2");
+ when(mMockPrefs.getLong("222|app_user_1", -1)).thenReturn(555L);
ActivityInfo mockActivityInfo = new ActivityInfo();
mockActivityInfo.exported = true;
@@ -204,15 +220,15 @@
initializeModelFromPrefs();
assertEquals(2, mModel.getAppCount());
assertEquals("package1/class1", mModel.getApp(0).getComponentName().flattenToString());
- assertEquals(45L, mModel.getApp(0).getUserSerialNumber());
+ assertEquals(new UserHandle(4), mModel.getApp(0).getUser());
assertEquals("package2/class2", mModel.getApp(1).getComponentName().flattenToString());
- assertEquals(239L, mModel.getApp(1).getUserSerialNumber());
+ assertEquals(new UserHandle(5), mModel.getApp(1).getUser());
}
/** Tests initializing the model when the SharedPreferences aren't available. */
public void testInitializeDefaultApps() {
// Assume the user's app count pref isn't available.
- when(mMockPrefs.getInt("22|app_count", -1)).thenReturn(-1);
+ when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(-1);
// Assume some installed activities.
ActivityInfo ai1 = new ActivityInfo();
@@ -233,16 +249,16 @@
mModel.setCurrentUser(2);
assertEquals(2, mModel.getAppCount());
assertEquals("package1/class1", mModel.getApp(0).getComponentName().flattenToString());
- assertEquals(22L, mModel.getApp(0).getUserSerialNumber());
+ assertEquals(new UserHandle(2), mModel.getApp(0).getUser());
assertEquals("package2/class2", mModel.getApp(1).getComponentName().flattenToString());
- assertEquals(22L, mModel.getApp(1).getUserSerialNumber());
+ assertEquals(new UserHandle(2), mModel.getApp(1).getUser());
InOrder order = inOrder(mMockEdit);
order.verify(mMockEdit).apply();
- order.verify(mMockEdit).putInt("22|app_count", 2);
- order.verify(mMockEdit).putString("22|app_0", "package1/class1");
- order.verify(mMockEdit).putLong("22|app_user_0", 22L);
- order.verify(mMockEdit).putString("22|app_1", "package2/class2");
- order.verify(mMockEdit).putLong("22|app_user_1", 22L);
+ order.verify(mMockEdit).putInt("222|app_count", 2);
+ order.verify(mMockEdit).putString("222|app_0", "package1/class1");
+ order.verify(mMockEdit).putLong("222|app_user_0", 222L);
+ order.verify(mMockEdit).putString("222|app_1", "package2/class2");
+ order.verify(mMockEdit).putLong("222|app_user_1", 222L);
order.verify(mMockEdit).apply();
verifyNoMoreInteractions(mMockEdit);
}
@@ -250,12 +266,12 @@
/** Tests initializing the model if one of the prefs is missing. */
public void testInitializeWithMissingPref() {
// Assume two apps are nominally stored.
- when(mMockPrefs.getInt("22|app_count", -1)).thenReturn(2);
- when(mMockPrefs.getString("22|app_0", null)).thenReturn("package0/class0");
- when(mMockPrefs.getLong("22|app_user_0", -1)).thenReturn(239L);
+ when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2);
+ when(mMockPrefs.getString("222|app_0", null)).thenReturn("package0/class0");
+ when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(555L);
// But assume one pref is missing.
- when(mMockPrefs.getString("22|app_1", null)).thenReturn(null);
+ when(mMockPrefs.getString("222|app_1", null)).thenReturn(null);
ActivityInfo mockActivityInfo = new ActivityInfo();
mockActivityInfo.exported = true;
@@ -279,18 +295,23 @@
mModel.setCurrentUser(2);
assertEquals(1, mModel.getAppCount());
assertEquals("package0/class0", mModel.getApp(0).getComponentName().flattenToString());
- assertEquals(239L, mModel.getApp(0).getUserSerialNumber());
+ assertEquals(new UserHandle(5), mModel.getApp(0).getUser());
+ InOrder order = inOrder(mMockEdit);
+ order.verify(mMockEdit).putInt("222|app_count", 1);
+ order.verify(mMockEdit).putString("222|app_0", "package0/class0");
+ order.verify(mMockEdit).putLong("222|app_user_0", 555L);
+ order.verify(mMockEdit).apply();
verifyNoMoreInteractions(mMockEdit);
}
/** Tests initializing the model if one of the apps is unlauncheable. */
public void testInitializeWithUnlauncheableApp() {
// Assume two apps are nominally stored.
- when(mMockPrefs.getInt("22|app_count", -1)).thenReturn(2);
- when(mMockPrefs.getString("22|app_0", null)).thenReturn("package0/class0");
- when(mMockPrefs.getLong("22|app_user_0", -1)).thenReturn(239L);
- when(mMockPrefs.getString("22|app_1", null)).thenReturn("package1/class1");
- when(mMockPrefs.getLong("22|app_user_1", -1)).thenReturn(45L);
+ when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(2);
+ when(mMockPrefs.getString("222|app_0", null)).thenReturn("package0/class0");
+ when(mMockPrefs.getLong("222|app_user_0", -1)).thenReturn(555L);
+ when(mMockPrefs.getString("222|app_1", null)).thenReturn("package1/class1");
+ when(mMockPrefs.getLong("222|app_user_1", -1)).thenReturn(444L);
ActivityInfo mockActivityInfo = new ActivityInfo();
mockActivityInfo.exported = true;
@@ -314,13 +335,13 @@
mModel.setCurrentUser(2);
assertEquals(1, mModel.getAppCount());
assertEquals("package0/class0", mModel.getApp(0).getComponentName().flattenToString());
- assertEquals(239L, mModel.getApp(0).getUserSerialNumber());
+ assertEquals(new UserHandle(5), mModel.getApp(0).getUser());
// Once an unlauncheable app is detected, the model should save all apps excluding the
// unlauncheable one.
- verify(mMockEdit).putInt("22|app_count", 1);
- verify(mMockEdit).putString("22|app_0", "package0/class0");
- verify(mMockEdit).putLong("22|app_user_0", 239L);
+ verify(mMockEdit).putInt("222|app_count", 1);
+ verify(mMockEdit).putString("222|app_0", "package0/class0");
+ verify(mMockEdit).putLong("222|app_user_0", 555L);
verify(mMockEdit).apply();
verifyNoMoreInteractions(mMockEdit);
}
@@ -330,11 +351,11 @@
initializeModelFromPrefs();
mModel.savePrefs();
- verify(mMockEdit).putInt("22|app_count", 2);
- verify(mMockEdit).putString("22|app_0", "package1/class1");
- verify(mMockEdit).putLong("22|app_user_0", 45L);
- verify(mMockEdit).putString("22|app_1", "package2/class2");
- verify(mMockEdit).putLong("22|app_user_1", 239L);
+ verify(mMockEdit).putInt("222|app_count", 2);
+ verify(mMockEdit).putString("222|app_0", "package1/class1");
+ verify(mMockEdit).putLong("222|app_user_0", 444L);
+ verify(mMockEdit).putString("222|app_1", "package2/class2");
+ verify(mMockEdit).putLong("222|app_user_1", 555L);
verify(mMockEdit).apply();
verifyNoMoreInteractions(mMockEdit);
}
@@ -367,7 +388,7 @@
// Assume the user's app count pref isn't available. This will trigger clearing deleted
// users' prefs.
- when(mMockPrefs.getInt("22|app_count", -1)).thenReturn(-1);
+ when(mMockPrefs.getInt("222|app_count", -1)).thenReturn(-1);
final Map allPrefs = new HashMap<String, Object>();
allPrefs.put("version", null);
diff --git a/packages/WallpaperCropper/AndroidManifest.xml b/packages/WallpaperCropper/AndroidManifest.xml
index 81d1085..e558d7e 100644
--- a/packages/WallpaperCropper/AndroidManifest.xml
+++ b/packages/WallpaperCropper/AndroidManifest.xml
@@ -21,7 +21,10 @@
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
- <application android:requiredForAllUsers="true">
+ <application
+ android:requiredForAllUsers="true"
+ android:largeHeap="true">
+
<activity
android:name="WallpaperCropActivity"
android:theme="@style/Theme.WallpaperCropper"
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 56ebed6..c373fb8 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1569,6 +1569,8 @@
if ("-h".equals(arg)) {
dumpHelp(pw);
return;
+ } else if ("-a".equals(arg)) {
+ // dump all data
} else if ("write-settings".equals(arg)) {
long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 13b3f8d..b826cfd 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -246,3 +246,8 @@
# ---------------------------
40000 volume_changed (stream|1), (prev_level|1), (level|1), (max_level|1), (caller|3)
40001 stream_devices_changed (stream|1), (prev_devices|1), (devices|1)
+
+# ---------------------------
+# GestureLauncherService.java
+# ---------------------------
+40100 camera_gesture_triggered (gesture_on_time|2|3), (sensor1_on_time|2|3), (sensor2_on_time|2|3), (event_extra|1|1)
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 55af9f0..342a3ef 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -19,12 +19,9 @@
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.Sensor;
@@ -34,13 +31,14 @@
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.os.Vibrator;
-import android.provider.MediaStore;
import android.provider.Settings;
import android.util.Slog;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
/**
* The service that listens for gestures detected in sensor firmware and starts the intent
* accordingly.
@@ -56,8 +54,6 @@
private final GestureEventListener mGestureListener = new GestureEventListener();
private Sensor mCameraLaunchSensor;
- private Vibrator mVibrator;
- private KeyguardManager mKeyGuard;
private Context mContext;
/** The wake lock held when a gesture is detected. */
@@ -65,6 +61,36 @@
private boolean mRegistered;
private int mUserId;
+ // Below are fields used for event logging only.
+ /** Elapsed real time when the camera gesture is turned on. */
+ private long mCameraGestureOnTimeMs = 0L;
+
+ /** Elapsed real time when the last camera gesture was detected. */
+ private long mCameraGestureLastEventTime = 0L;
+
+ /**
+ * How long the sensor 1 has been turned on since camera launch sensor was
+ * subscribed to and when the last camera launch gesture was detected.
+ * <p>Sensor 1 is the main sensor used to detect camera launch gesture.</p>
+ */
+ private long mCameraGestureSensor1LastOnTimeMs = 0L;
+
+ /**
+ * If applicable, how long the sensor 2 has been turned on since camera
+ * launch sensor was subscribed to and when the last camera launch
+ * gesture was detected.
+ * <p>Sensor 2 is the secondary sensor used to detect camera launch gesture.
+ * This is optional and if only sensor 1 is used for detect camera launch
+ * gesture, this value would always be 0.</p>
+ */
+ private long mCameraGestureSensor2LastOnTimeMs = 0L;
+
+ /**
+ * Extra information about the event when the last camera launch gesture
+ * was detected.
+ */
+ private int mCameraLaunchLastEventExtra = 0;
+
public GestureLauncherService(Context context) {
super(context);
mContext = context;
@@ -82,12 +108,9 @@
return;
}
- mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
- mKeyGuard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
PowerManager powerManager = (PowerManager) mContext.getSystemService(
Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(
- PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"GestureLauncherService");
updateCameraRegistered();
@@ -115,6 +138,12 @@
private void unregisterCameraLaunchGesture() {
if (mRegistered) {
mRegistered = false;
+ mCameraGestureOnTimeMs = 0L;
+ mCameraGestureLastEventTime = 0L;
+ mCameraGestureSensor1LastOnTimeMs = 0;
+ mCameraGestureSensor2LastOnTimeMs = 0;
+ mCameraLaunchLastEventExtra = 0;
+
SensorManager sensorManager = (SensorManager) mContext.getSystemService(
Context.SENSOR_SERVICE);
sensorManager.unregisterListener(mGestureListener);
@@ -128,6 +157,8 @@
if (mRegistered) {
return;
}
+ mCameraGestureOnTimeMs = SystemClock.elapsedRealtime();
+ mCameraGestureLastEventTime = mCameraGestureOnTimeMs;
SensorManager sensorManager = (SensorManager) mContext.getSystemService(
Context.SENSOR_SERVICE);
int cameraLaunchGestureId = resources.getInteger(
@@ -208,14 +239,22 @@
private final class GestureEventListener implements SensorEventListener {
@Override
public void onSensorChanged(SensorEvent event) {
+ if (!mRegistered) {
+ if (DBG) Slog.d(TAG, "Ignoring gesture event because it's unregistered.");
+ return;
+ }
if (event.sensor == mCameraLaunchSensor) {
- handleCameraLaunchGesture();
+ handleCameraLaunchGesture(event);
return;
}
}
- private void handleCameraLaunchGesture() {
- if (DBG) Slog.d(TAG, "Received a camera launch event.");
+ private void handleCameraLaunchGesture(SensorEvent event) {
+ if (DBG) {
+ float[] values = event.values;
+ Slog.d(TAG, String.format("Received a camera launch event: " +
+ "values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2]));
+ }
boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
if (!userSetupComplete) {
@@ -227,28 +266,13 @@
if (DBG) Slog.d(TAG, String.format(
"userSetupComplete = %s, performing camera launch gesture.",
userSetupComplete));
- boolean locked = mKeyGuard != null && mKeyGuard.inKeyguardRestrictedInputMode();
- String action = locked
- ? MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE
- : MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
- Intent intent = new Intent(action);
- PackageManager pm = mContext.getPackageManager();
- ResolveInfo componentInfo = pm.resolveActivity(intent,
- PackageManager.MATCH_DEFAULT_ONLY);
- if (componentInfo == null) {
- if (DBG) Slog.d(TAG, "Couldn't find an app to process the camera intent.");
- return;
- }
- if (mVibrator != null && mVibrator.hasVibrator()) {
- mVibrator.vibrate(1000L);
- }
- // Turn on the screen before the camera launches.
+ // Make sure we don't sleep too early
mWakeLock.acquire(500L);
- intent.setComponent(new ComponentName(componentInfo.activityInfo.packageName,
- componentInfo.activityInfo.name));
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ StatusBarManagerInternal service = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ service.onCameraLaunchGestureDetected();
+ trackCameraLaunchEvent(event);
mWakeLock.release();
}
@@ -256,5 +280,50 @@
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Ignored.
}
+
+ private void trackCameraLaunchEvent(SensorEvent event) {
+ long now = SystemClock.elapsedRealtime();
+ long totalDuration = now - mCameraGestureOnTimeMs;
+ // values[0]: ratio between total time duration when accel is turned on and time
+ // duration since camera launch gesture is subscribed.
+ // values[1]: ratio between total time duration when gyro is turned on and time duration
+ // since camera launch gesture is subscribed.
+ // values[2]: extra information
+ float[] values = event.values;
+
+ long sensor1OnTime = (long) (totalDuration * (double) values[0]);
+ long sensor2OnTime = (long) (totalDuration * (double) values[1]);
+ int extra = (int) values[2];
+
+ // We only log the difference in the event log to make aggregation easier.
+ long gestureOnTimeDiff = now - mCameraGestureLastEventTime;
+ long sensor1OnTimeDiff = sensor1OnTime - mCameraGestureSensor1LastOnTimeMs;
+ long sensor2OnTimeDiff = sensor2OnTime - mCameraGestureSensor2LastOnTimeMs;
+ int extraDiff = extra - mCameraLaunchLastEventExtra;
+
+ // Gating against negative time difference. This doesn't usually happen, but it may
+ // happen because of numeric errors.
+ if (gestureOnTimeDiff < 0 || sensor1OnTimeDiff < 0 || sensor2OnTimeDiff < 0) {
+ if (DBG) Slog.d(TAG, "Skipped event logging because negative numbers.");
+ return;
+ }
+
+ if (DBG) Slog.d(TAG, String.format("totalDuration: %d, sensor1OnTime: %s, " +
+ "sensor2OnTime: %d, extra: %d",
+ gestureOnTimeDiff,
+ sensor1OnTimeDiff,
+ sensor2OnTimeDiff,
+ extraDiff));
+ EventLogTags.writeCameraGestureTriggered(
+ gestureOnTimeDiff,
+ sensor1OnTimeDiff,
+ sensor2OnTimeDiff,
+ extraDiff);
+
+ mCameraGestureLastEventTime = now;
+ mCameraGestureSensor1LastOnTimeMs = sensor1OnTime;
+ mCameraGestureSensor2LastOnTimeMs = sensor2OnTime;
+ mCameraLaunchLastEventExtra = extra;
+ }
}
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 0a0ee3f..b418aba 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -15,8 +15,6 @@
package com.android.server;
-import android.annotation.NonNull;
-import android.content.pm.PackageManagerInternal;
import com.android.internal.content.PackageMonitor;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
@@ -27,21 +25,21 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethod;
-import com.android.internal.view.IInputSessionCallback;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.IInputSessionCallback;
import com.android.internal.view.InputBindResult;
import com.android.server.statusbar.StatusBarManagerService;
-import com.android.server.wm.WindowManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.annotation.NonNull;
import android.app.ActivityManagerNative;
-import android.app.AppGlobals;
import android.app.AlertDialog;
+import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IUserSwitchObserver;
import android.app.KeyguardManager;
@@ -61,6 +59,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
@@ -80,8 +79,8 @@
import android.os.IInterface;
import android.os.IRemoteCallback;
import android.os.Message;
-import android.os.Process;
import android.os.Parcel;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -90,7 +89,6 @@
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -109,6 +107,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.WindowManagerInternal;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethod;
@@ -182,11 +181,11 @@
final InputMethodSettings mSettings;
final SettingsObserver mSettingsObserver;
final IWindowManager mIWindowManager;
+ final WindowManagerInternal mWindowManagerInternal;
final HandlerCaller mCaller;
final boolean mHasFeature;
private InputMethodFileManager mFileManager;
private final HardKeyboardListener mHardKeyboardListener;
- private final WindowManagerService mWindowManagerService;
private final AppOpsManager mAppOpsManager;
final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1, -1);
@@ -710,7 +709,7 @@
}
private class HardKeyboardListener
- implements WindowManagerService.OnHardKeyboardStatusChangeListener {
+ implements WindowManagerInternal.OnHardKeyboardStatusChangeListener {
@Override
public void onHardKeyboardStatusChange(boolean available) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
@@ -732,7 +731,7 @@
}
}
- public InputMethodManagerService(Context context, WindowManagerService windowManager) {
+ public InputMethodManagerService(Context context) {
mIPackageManager = AppGlobals.getPackageManager();
mContext = context;
mRes = context.getResources();
@@ -741,13 +740,13 @@
mSettingsObserver = new SettingsObserver(mHandler);
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
+ mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
@Override
public void executeMessage(Message msg) {
handleMessage(msg);
}
}, true /*asyncHandler*/);
- mWindowManagerService = windowManager;
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mHardKeyboardListener = new HardKeyboardListener();
mHasFeature = context.getPackageManager().hasSystemFeature(
@@ -1045,7 +1044,7 @@
mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
com.android.internal.R.bool.show_ongoing_ime_switcher);
if (mShowOngoingImeSwitcherForPhones) {
- mWindowManagerService.setOnHardKeyboardStatusChangeListener(
+ mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(
mHardKeyboardListener);
}
buildInputMethodListLocked(mMethodList, mMethodMap,
@@ -1507,7 +1506,7 @@
if (DEBUG) Slog.v(TAG, "Removing window token: " + mCurToken);
if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0 && savePosition) {
// The current IME is shown. Hence an IME switch (transition) is happening.
- mWindowManagerService.saveLastInputMethodWindowForTransition();
+ mWindowManagerInternal.saveLastInputMethodWindowForTransition();
}
mIWindowManager.removeWindowToken(mCurToken);
} catch (RemoteException e) {
@@ -1641,7 +1640,7 @@
if (mSwitchingDialog != null) return false;
if (isScreenLocked()) return false;
if ((visibility & InputMethodService.IME_ACTIVE) == 0) return false;
- if (mWindowManagerService.isHardKeyboardAvailable()) {
+ if (mWindowManagerInternal.isHardKeyboardAvailable()) {
// When physical keyboard is attached, we show the ime switcher (or notification if
// NavBar is not available) because SHOW_IME_WITH_HARD_KEYBOARD settings currently
// exists in the IME switcher dialog. Might be OK to remove this condition once
@@ -1753,15 +1752,18 @@
mImeSwitcherNotification.setContentTitle(title)
.setContentText(summary)
.setContentIntent(mImeSwitchPendingIntent);
- if ((mNotificationManager != null)
- && !mWindowManagerService.hasNavigationBar()) {
- if (DEBUG) {
- Slog.d(TAG, "--- show notification: label = " + summary);
+ try {
+ if ((mNotificationManager != null)
+ && !mIWindowManager.hasNavigationBar()) {
+ if (DEBUG) {
+ Slog.d(TAG, "--- show notification: label = " + summary);
+ }
+ mNotificationManager.notifyAsUser(null,
+ com.android.internal.R.string.select_input_method,
+ mImeSwitcherNotification.build(), UserHandle.ALL);
+ mNotificationShown = true;
}
- mNotificationManager.notifyAsUser(null,
- com.android.internal.R.string.select_input_method,
- mImeSwitcherNotification.build(), UserHandle.ALL);
- mNotificationShown = true;
+ } catch (RemoteException e) {
}
} else {
if (mNotificationShown && mNotificationManager != null) {
@@ -2527,7 +2529,7 @@
@Override
public int getInputMethodWindowVisibleHeight() {
- return mWindowManagerService.getInputMethodWindowVisibleHeight();
+ return mWindowManagerInternal.getInputMethodWindowVisibleHeight();
}
@Override
@@ -3041,7 +3043,7 @@
mSwitchingDialogTitleView = tv;
mSwitchingDialogTitleView
.findViewById(com.android.internal.R.id.hard_keyboard_section)
- .setVisibility(mWindowManagerService.isHardKeyboardAvailable()
+ .setVisibility(mWindowManagerInternal.isHardKeyboardAvailable()
? View.VISIBLE : View.GONE);
final Switch hardKeySwitch = (Switch) mSwitchingDialogTitleView.findViewById(
com.android.internal.R.id.hard_keyboard_switch);
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 7e4bcdc..c7e0c98 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -29,6 +29,8 @@
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
import static android.content.Context.USER_SERVICE;
import static android.Manifest.permission.READ_CONTACTS;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
+
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
import android.os.IBinder;
@@ -664,6 +666,10 @@
if (shouldReEnroll) {
credentialUtil.setCredential(credential, credential, userId);
}
+ } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
+ if (response.getTimeout() > 0) {
+ requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
+ }
}
return response;
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index cd61347..86183af 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -99,6 +99,12 @@
// URL-handling state upon factory reset.
final ArraySet<String> mLinkedApps = new ArraySet<>();
+ // These are the packages that are whitelisted to be able to run as system user
+ final ArraySet<String> mSystemUserWhitelistedApps = new ArraySet<>();
+
+ // These are the packages that should not run under system user
+ final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();
+
public static SystemConfig getInstance() {
synchronized (SystemConfig.class) {
if (sInstance == null) {
@@ -144,6 +150,14 @@
return mLinkedApps;
}
+ public ArraySet<String> getSystemUserWhitelistedApps() {
+ return mSystemUserWhitelistedApps;
+ }
+
+ public ArraySet<String> getSystemUserBlacklistedApps() {
+ return mSystemUserBlacklistedApps;
+ }
+
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
@@ -380,7 +394,24 @@
mLinkedApps.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
-
+ } else if ("system-user-whitelisted-app".equals(name)) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<system-user-whitelisted-app> without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mSystemUserWhitelistedApps.add(pkgname);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } else if ("system-user-blacklisted-app".equals(name)) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<system-user-blacklisted-app without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mSystemUserBlacklistedApps.add(pkgname);
+ }
+ XmlUtils.skipCurrentTag(parser);
} else {
XmlUtils.skipCurrentTag(parser);
continue;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 007b14b..8e24127 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -54,6 +54,7 @@
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
+import android.content.pm.AppsQueryHelper;
import android.content.pm.PermissionInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -101,6 +102,7 @@
import com.android.server.IntentResolver;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.Watchdog;
@@ -208,7 +210,6 @@
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.SELinux;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -11983,6 +11984,8 @@
}
}
+ enableSystemUserApps();
+
// Start up initial activity.
mBooting = true;
startHomeActivityLocked(mCurrentUserId, "systemReady");
@@ -12033,6 +12036,42 @@
}
}
+ private void enableSystemUserApps() {
+ // For system user, enable apps based on the following conditions:
+ // - app is whitelisted; or has no launcher icons; or has INTERACT_ACROSS_USERS permission
+ // - app is not in the blacklist
+ if (UserManager.isSplitSystemUser()) {
+ AppsQueryHelper queryHelper = new AppsQueryHelper(mContext);
+ Set<String> enableApps = new HashSet<>();
+ enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
+ | AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM,
+ /* systemAppsOnly */ true, UserHandle.SYSTEM));
+ ArraySet<String> wlApps = SystemConfig.getInstance().getSystemUserWhitelistedApps();
+ enableApps.addAll(wlApps);
+ ArraySet<String> blApps = SystemConfig.getInstance().getSystemUserBlacklistedApps();
+ enableApps.removeAll(blApps);
+
+ List<String> systemApps = queryHelper.queryApps(0, /* systemAppsOnly */ true,
+ UserHandle.SYSTEM);
+ final int systemAppsSize = systemApps.size();
+ for (int i = 0; i < systemAppsSize; i++) {
+ String pName = systemApps.get(i);
+ boolean enable = enableApps.contains(pName);
+ try {
+ if (enable) {
+ AppGlobals.getPackageManager().installExistingPackageAsUser(pName,
+ UserHandle.USER_SYSTEM);
+ } else {
+ AppGlobals.getPackageManager().deletePackageAsUser(pName, null,
+ UserHandle.USER_SYSTEM, PackageManager.DELETE_SYSTEM_APP);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error occured when processing package " + pName, e);
+ }
+ }
+ }
+ }
+
private boolean makeAppCrashingLocked(ProcessRecord app,
String shortMsg, String longMsg, String stackTrace) {
app.crashing = true;
@@ -16541,12 +16580,12 @@
}
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
- if (user != UserHandle.USER_OWNER && newReceivers != null) {
- // If this is not the primary user, we need to check for
+ if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
+ // If this is not the system user, we need to check for
// any receivers that should be filtered out.
for (int i=0; i<newReceivers.size(); i++) {
ResolveInfo ri = newReceivers.get(i);
- if ((ri.activityInfo.flags&ActivityInfo.FLAG_PRIMARY_USER_ONLY) != 0) {
+ if ((ri.activityInfo.flags&ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
newReceivers.remove(i);
i--;
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 965f5b6..f50df3a 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -902,7 +902,14 @@
prev.task.touchActiveTime();
clearLaunchTime(prev);
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
- if (mService.mHasRecents && (next == null || next.noDisplay || next.task != prev.task || uiSleeping)) {
+ // In freeform mode we only update the thumbnail when there is no thumbnail yet since every
+ // focus change will request a thumbnail to be taken.
+ // Note furthermore that since windows can change their content in freeform mode all the
+ // time a thumbnail is possibly constantly outdated.
+ if (mService.mHasRecents &&
+ (next == null || next.noDisplay || next.task != prev.task || uiSleeping) &&
+ (!prev.task.hasThumbnail() ||
+ prev.task.stack.mStackId != FREEFORM_WORKSPACE_STACK_ID)) {
prev.updateThumbnailLocked(screenshotActivities(prev), null);
}
stopFullyDrawnTraceIfNeeded();
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9bf4f5f..335288d 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1263,8 +1263,8 @@
Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + result);
}
- // There is some accuracy error in reports so allow 30 milliseconds of error.
- final long SAMPLE_ERROR_MILLIS = 30;
+ // There is some accuracy error in reports so allow some slop in the results.
+ final long SAMPLE_ERROR_MILLIS = 750;
final long totalTimeMs = result.mControllerIdleTimeMs + result.mControllerRxTimeMs +
result.mControllerTxTimeMs;
if (totalTimeMs > timePeriodMs + SAMPLE_ERROR_MILLIS) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 712b8cc..9cbaec5 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -478,6 +478,14 @@
}
/**
+ * Returns true when we have a thumbnail.
+ * @return Returns true if there is a thumbnail.
+ */
+ boolean hasThumbnail() {
+ return mLastThumbnail != null;
+ }
+
+ /**
* Sets the last thumbnail.
* @return whether the thumbnail was set
*/
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 932662f..470bd5a 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -298,6 +298,7 @@
final int result = daemon.enroll(cryptoToken, groupId, timeout);
if (result != 0) {
Slog.w(TAG, "startEnroll failed, result=" + result);
+ dispatchError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
} catch (RemoteException e) {
Slog.e(TAG, "startEnroll failed", e);
@@ -391,6 +392,7 @@
final int result = daemon.authenticate(opId, groupId);
if (result != 0) {
Slog.w(TAG, "startAuthentication failed, result=" + result);
+ dispatchError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
} catch (RemoteException e) {
Slog.e(TAG, "startAuthentication failed", e);
@@ -433,12 +435,14 @@
return;
}
+ stopPendingOperations(true);
mRemoveClient = new ClientMonitor(token, receiver, userId, restricted);
// The fingerprint template ids will be removed when we get confirmation from the HAL
try {
final int result = daemon.remove(fingerId, userId);
if (result != 0) {
Slog.w(TAG, "startRemove with id = " + fingerId + " failed, result=" + result);
+ dispatchError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
} catch (RemoteException e) {
Slog.e(TAG, "startRemove failed", e);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index edd274b..0a12d5a 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -413,8 +413,7 @@
applyRestrictions(muteNotifications, USAGE_NOTIFICATION);
// call restrictions
- final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
- || mEffectsSuppressed;
+ final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers;
applyRestrictions(muteCalls, USAGE_NOTIFICATION_RINGTONE);
// alarm restrictions
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a559116..9e878e7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4554,7 +4554,7 @@
if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {
List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
result.add(xpResolveInfo);
- return filterIfNotPrimaryUser(result, userId);
+ return filterIfNotSystemUser(result, userId);
}
// Check for results in the current profile.
@@ -4568,7 +4568,7 @@
result.add(xpResolveInfo);
Collections.sort(result, mResolvePrioritySorter);
}
- result = filterIfNotPrimaryUser(result, userId);
+ result = filterIfNotSystemUser(result, userId);
if (hasWebURI(intent)) {
CrossProfileDomainInfo xpDomainInfo = null;
final UserInfo parent = getProfileParent(userId);
@@ -4597,7 +4597,7 @@
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return filterIfNotPrimaryUser(
+ return filterIfNotSystemUser(
mActivities.queryIntentForPackage(
intent, resolvedType, flags, pkg.activities, userId),
userId);
@@ -4644,7 +4644,7 @@
if (result == null) {
result = new CrossProfileDomainInfo();
result.resolveInfo =
- createForwardingResolveInfo(null, sourceUserId, parentUserId);
+ createForwardingResolveInfo(new IntentFilter(), sourceUserId, parentUserId);
result.bestDomainVerificationStatus = status;
} else {
result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
@@ -4684,17 +4684,17 @@
}
/**
- * Filter out activities with primaryUserOnly flag set, when current user is not the owner.
+ * Filter out activities with systemUserOnly flag set, when current user is not System.
*
* @return filtered list
*/
- private List<ResolveInfo> filterIfNotPrimaryUser(List<ResolveInfo> resolveInfos, int userId) {
- if (userId == UserHandle.USER_OWNER) {
+ private List<ResolveInfo> filterIfNotSystemUser(List<ResolveInfo> resolveInfos, int userId) {
+ if (userId == UserHandle.USER_SYSTEM) {
return resolveInfos;
}
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
ResolveInfo info = resolveInfos.get(i);
- if ((info.activityInfo.flags & ActivityInfo.FLAG_PRIMARY_USER_ONLY) != 0) {
+ if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
resolveInfos.remove(i);
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 715dd2c..61d2676 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -23,7 +23,6 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.PACKAGE_INFO_GID;
@@ -39,13 +38,10 @@
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
-import android.os.IBinder;
import android.os.Handler;
import android.os.Message;
import android.os.PatternMatcher;
import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e0da33f..4aaba14 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4455,7 +4455,7 @@
if (mAppsToBeHidden.isEmpty()) {
if (dismissKeyguard && !mKeyguardSecure) {
mAppsThatDismissKeyguard.add(appToken);
- } else {
+ } else if (win.isDrawnLw()) {
mWinShowWhenLocked = win;
mHideLockScreen = true;
mForceStatusBarFromKeyguard = false;
@@ -4489,7 +4489,7 @@
mWinDismissingKeyguard = win;
mSecureDismissingKeyguard = mKeyguardSecure;
mForceStatusBarFromKeyguard = mShowingLockscreen && mKeyguardSecure;
- } else if (mAppsToBeHidden.isEmpty() && showWhenLocked) {
+ } else if (mAppsToBeHidden.isEmpty() && showWhenLocked && win.isDrawnLw()) {
if (DEBUG_LAYOUT) Slog.v(TAG,
"Setting mHideLockScreen to true by win " + win);
mHideLockScreen = true;
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 8b3c036..9916223 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -217,6 +217,8 @@
* It is called each time the orientation determination transitions from being
* uncertain to being certain again, even if it is the same orientation as before.
*
+ * This should only be called on the Handler thread.
+ *
* @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
* @see android.view.Surface
*/
@@ -995,9 +997,13 @@
@Override
public void onSensorChanged(SensorEvent event) {
+ int newRotation;
synchronized (mLock) {
mDesiredRotation = (int) event.values[0];
- evaluateRotationChangeLocked();
+ newRotation = evaluateRotationChangeLocked();
+ }
+ if (newRotation >=0) {
+ onProposedRotationChanged(newRotation);
}
}
@@ -1023,18 +1029,19 @@
unscheduleRotationEvaluationLocked();
}
- public void evaluateRotationChangeLocked() {
+ public int evaluateRotationChangeLocked() {
unscheduleRotationEvaluationLocked();
if (mDesiredRotation == mProposedRotation) {
- return;
+ return -1;
}
final long now = SystemClock.elapsedRealtimeNanos();
if (isDesiredRotationAcceptableLocked(now)) {
mProposedRotation = mDesiredRotation;
- onProposedRotationChanged(mProposedRotation);
+ return mProposedRotation;
} else {
scheduleRotationEvaluationIfNecessaryLocked(now);
}
+ return -1;
}
private boolean isDesiredRotationAcceptableLocked(long now) {
@@ -1090,9 +1097,13 @@
private Runnable mRotationEvaluator = new Runnable() {
@Override
public void run() {
+ int newRotation;
synchronized (mLock) {
mRotationEvaluationScheduled = false;
- evaluateRotationChangeLocked();
+ newRotation = evaluateRotationChangeLocked();
+ }
+ if (newRotation >= 0) {
+ onProposedRotationChanged(newRotation);
}
}
};
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 130815e..5d01931 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -28,4 +28,5 @@
void showScreenPinningRequest();
void showAssistDisclosure();
void startAssist(Bundle args);
+ void onCameraLaunchGestureDetected();
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 8663254..87dc6c4 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -176,6 +176,16 @@
}
}
}
+
+ @Override
+ public void onCameraLaunchGestureDetected() {
+ if (mBar != null) {
+ try {
+ mBar.onCameraLaunchGestureDetected();
+ } catch (RemoteException e) {
+ }
+ }
+ }
};
// ================================================================================
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d1145d0..de7c07e 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -870,10 +870,10 @@
}
private Animation createAspectScaledThumbnailEnterNonFullscreenAnimationLocked(
- Rect containingFrame, @Nullable Rect surfaceInsets, int taskId) {
+ Rect frame, @Nullable Rect surfaceInsets, int taskId) {
getNextAppTransitionStartRect(taskId, mTmpStartRect);
- float width = containingFrame.width();
- float height = containingFrame.height();
+ float width = frame.width();
+ float height = frame.height();
float scaleWidth = mTmpStartRect.width() / width;
float scaleHeight = mTmpStartRect.height() / height;
AnimationSet set = new AnimationSet(true);
@@ -886,9 +886,9 @@
ScaleAnimation scale = new ScaleAnimation(scaleWidth, 1, scaleHeight, 1,
(width + surfaceInsetsHorizontal) / 2, (height + surfaceInsetsVertical) / 2);
int fromX = mTmpStartRect.left + mTmpStartRect.width() / 2
- - (containingFrame.left + containingFrame.width() / 2);
+ - (frame.left + frame.width() / 2);
int fromY = mTmpStartRect.top + mTmpStartRect.height() / 2
- - (containingFrame.top + containingFrame.height() / 2);
+ - (frame.top + frame.height() / 2);
TranslateAnimation translation = new TranslateAnimation(fromX, 0, fromY, 0);
set.addAnimation(scale);
set.addAnimation(translation);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7b4c2a0..cf690a5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -552,7 +552,7 @@
boolean mHardKeyboardAvailable;
boolean mShowImeWithHardKeyboard;
- OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
+ WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
SettingsObserver mSettingsObserver;
private final class SettingsObserver extends ContentObserver {
@@ -2806,12 +2806,17 @@
Rect appFrame = new Rect(0, 0, width, height);
Rect surfaceInsets = null;
final boolean fullscreen = win != null && win.isFullscreen(width, height);
- // Dialog activities have windows with containing frame being very large, but not
- // exactly fullscreen and much smaller mFrame. We use this distinction to identify
- // dialog activities.
- final boolean dialogWindow = win != null && !win.mContainingFrame.equals(win.mFrame);
+ final boolean freeform = win != null && win.inFreeformWorkspace();
if (win != null) {
- containingFrame.set(win.mContainingFrame);
+ // Containing frame will usually cover the whole screen, including dialog windows.
+ // For freeform workspace windows it will not cover the whole screen and it also
+ // won't exactly match the final freeform window frame (e.g. when overlapping with
+ // the status bar). In that case we need to use the final frame.
+ if (freeform) {
+ containingFrame.set(win.mFrame);
+ } else {
+ containingFrame.set(win.mContainingFrame);
+ }
surfaceInsets = win.getAttrs().surfaceInsets;
if (fullscreen) {
// For fullscreen windows use the window frames and insets to set the thumbnail
@@ -2830,11 +2835,9 @@
// screen gets the enter animation. Both appear in the mOpeningApps set.
enter = false;
}
- final boolean resizedWindow = !fullscreen && !dialogWindow;
Animation a = mAppTransition.loadAnimation(lp, transit, enter, containingWidth,
containingHeight, mCurConfiguration.orientation, containingFrame, contentInsets,
- surfaceInsets, appFrame, isVoiceInteraction, resizedWindow,
- atoken.mTask.mTaskId);
+ surfaceInsets, appFrame, isVoiceInteraction, freeform, atoken.mTask.mTaskId);
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = null;
@@ -6808,12 +6811,6 @@
mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
}
- public boolean isHardKeyboardAvailable() {
- synchronized (mWindowMap) {
- return mHardKeyboardAvailable;
- }
- }
-
public void updateShowImeWithHardKeyboard() {
synchronized (mWindowMap) {
final boolean showImeWithHardKeyboard = Settings.Secure.getIntForUser(
@@ -6826,16 +6823,9 @@
}
}
- public void setOnHardKeyboardStatusChangeListener(
- OnHardKeyboardStatusChangeListener listener) {
- synchronized (mWindowMap) {
- mHardKeyboardStatusChangeListener = listener;
- }
- }
-
void notifyHardKeyboardStatusChange() {
final boolean available;
- final OnHardKeyboardStatusChangeListener listener;
+ final WindowManagerInternal.OnHardKeyboardStatusChangeListener listener;
synchronized (mWindowMap) {
listener = mHardKeyboardStatusChangeListener;
available = mHardKeyboardAvailable;
@@ -10431,23 +10421,6 @@
}
}
- // It is assumed that this method is called only by InputMethodManagerService.
- public void saveLastInputMethodWindowForTransition() {
- synchronized (mWindowMap) {
- // TODO(multidisplay): Pass in the displayID.
- DisplayContent displayContent = getDefaultDisplayContentLocked();
- if (mInputMethodWindow != null) {
- mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget);
- }
- }
- }
-
- public int getInputMethodWindowVisibleHeight() {
- synchronized (mWindowMap) {
- return mPolicy.getInputMethodWindowVisibleHeightLw();
- }
- }
-
@Override
public boolean hasNavigationBar() {
return mPolicy.hasNavigationBar();
@@ -11037,10 +11010,6 @@
synchronized (mWindowMap) { }
}
- public interface OnHardKeyboardStatusChangeListener {
- public void onHardKeyboardStatusChange(boolean available);
- }
-
void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) {
Slog.v(TAG, "Layouts looping: " + msg + ", mPendingLayoutChanges = 0x" +
@@ -11348,5 +11317,39 @@
mAppTransition.registerListenerLocked(listener);
}
}
+
+ @Override
+ public int getInputMethodWindowVisibleHeight() {
+ synchronized (mWindowMap) {
+ return mPolicy.getInputMethodWindowVisibleHeightLw();
+ }
+ }
+
+ @Override
+ public void saveLastInputMethodWindowForTransition() {
+ synchronized (mWindowMap) {
+ // TODO(multidisplay): Pass in the displayID.
+ DisplayContent displayContent = getDefaultDisplayContentLocked();
+ if (mInputMethodWindow != null) {
+ mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget);
+ }
+ }
+ }
+
+ @Override
+ public boolean isHardKeyboardAvailable() {
+ synchronized (mWindowMap) {
+ return mHardKeyboardAvailable;
+ }
+ }
+
+ @Override
+ public void setOnHardKeyboardStatusChangeListener(
+ OnHardKeyboardStatusChangeListener listener) {
+ synchronized (mWindowMap) {
+ mHardKeyboardStatusChangeListener = listener;
+ }
+ }
+
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 092a6d1..761e5eb 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1629,7 +1629,7 @@
}
}
- private boolean inFreeformWorkspace() {
+ boolean inFreeformWorkspace() {
final Task task = getTask();
return task != null && task.mStack != null &&
task.mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0f60cc8..8788908 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4236,6 +4236,17 @@
mDeviceOwner.clearDeviceOwner();
mDeviceOwner.writeOwnerFile();
updateDeviceOwnerLocked();
+ // Reactivate backup service.
+ long ident = Binder.clearCallingIdentity();
+ try {
+ IBackupManager ibm = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ ibm.setBackupServiceActive(UserHandle.USER_OWNER, true);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed reactivating backup service.", e);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ed32c76..21dd647b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -546,7 +546,7 @@
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
try {
Slog.i(TAG, "Input Method Service");
- imm = new InputMethodManagerService(context, wm);
+ imm = new InputMethodManagerService(context);
ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
} catch (Throwable e) {
reportWtf("starting Input Manager Service", e);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index fb9a3a3..b021e80 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -61,6 +61,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Scanner;
+import java.util.Set;
/**
* UsbDeviceManager manages USB state in device mode.
@@ -148,6 +149,7 @@
private String[] mAccessoryStrings;
private UsbDebuggingManager mDebuggingManager;
private final UsbAlsaManager mUsbAlsaManager;
+ private Intent mBroadcastedIntent;
private class AdbSettingsObserver extends ContentObserver {
public AdbSettingsObserver() {
@@ -427,7 +429,7 @@
if (DEBUG) Slog.d(TAG, "setUsbDataUnlocked: " + enable);
mUsbDataUnlocked = enable;
updateUsbNotification();
- updateUsbStateBroadcast();
+ updateUsbStateBroadcastIfNeeded();
setEnabledFunctions(mCurrentFunctions, true);
}
@@ -574,7 +576,33 @@
}
}
- private void updateUsbStateBroadcast() {
+ private boolean isUsbStateChanged(Intent intent) {
+ final Set<String> keySet = intent.getExtras().keySet();
+ if (mBroadcastedIntent == null) {
+ for (String key : keySet) {
+ if (intent.getBooleanExtra(key, false)) {
+ // MTP function is enabled by default.
+ if (UsbManager.USB_FUNCTION_MTP.equals(key)) {
+ continue;
+ }
+ return true;
+ }
+ }
+ } else {
+ if (!keySet.equals(mBroadcastedIntent.getExtras().keySet())) {
+ return true;
+ }
+ for (String key : keySet) {
+ if (intent.getBooleanExtra(key, false) !=
+ mBroadcastedIntent.getBooleanExtra(key, false)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void updateUsbStateBroadcastIfNeeded() {
// send a sticky broadcast containing current USB state
Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -586,13 +614,25 @@
if (mCurrentFunctions != null) {
String[] functions = mCurrentFunctions.split(",");
for (int i = 0; i < functions.length; i++) {
- intent.putExtra(functions[i], true);
+ final String function = functions[i];
+ if (UsbManager.USB_FUNCTION_NONE.equals(function)) {
+ continue;
+ }
+ intent.putExtra(function, true);
}
}
- if (DEBUG) Slog.d(TAG, "broadcasting " + intent + " connected: " + mConnected
- + " configured: " + mConfigured);
+ // send broadcast intent only if the USB state has changed
+ if (!isUsbStateChanged(intent)) {
+ if (DEBUG) {
+ Slog.d(TAG, "skip broadcasting " + intent + " extras: " + intent.getExtras());
+ }
+ return;
+ }
+
+ if (DEBUG) Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ mBroadcastedIntent = intent;
}
private void updateUsbFunctions() {
@@ -670,7 +710,7 @@
setEnabledFunctions(null, false);
}
if (mBootCompleted) {
- updateUsbStateBroadcast();
+ updateUsbStateBroadcastIfNeeded();
updateUsbFunctions();
}
break;
@@ -694,7 +734,7 @@
case MSG_SYSTEM_READY:
updateUsbNotification();
updateAdbNotification();
- updateUsbStateBroadcast();
+ updateUsbStateBroadcastIfNeeded();
updateUsbFunctions();
break;
case MSG_BOOT_COMPLETED:
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 31f1d7f..e3f3c75 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -266,6 +266,13 @@
public static final String KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL =
"carrier_instant_lettering_available_bool";
+ /*
+ * Flag specifying whether IMS should be the first phone attempted for E911 even if the
+ * phone is not in service.
+ */
+ public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL
+ = "carrier_use_ims_first_for_emergency_bool";
+
/**
* If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
* this is the value that should be used instead. A configuration value of
@@ -425,6 +432,7 @@
sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false);
sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 79146f3..273cc93 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1135,6 +1135,8 @@
"VI", // U.S. Virgin Islands
};
+ private static final String KOREA_ISO_COUNTRY_CODE = "KR";
+
/**
* Breaks the given number down and formats it according to the rules
* for the country the number is from.
@@ -1455,7 +1457,14 @@
String result = null;
try {
PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
- result = util.formatInOriginalFormat(pn, defaultCountryIso);
+ if (KOREA_ISO_COUNTRY_CODE.equals(defaultCountryIso) &&
+ (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE))) {
+ // Format local Korean phone numbers with country code to corresponding national
+ // format which would replace the leading +82 with 0.
+ result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+ } else {
+ result = util.formatInOriginalFormat(pn, defaultCountryIso);
+ }
} catch (NumberParseException e) {
}
return result;
diff --git a/tests/UiBench/.gitignore b/tests/UiBench/.gitignore
new file mode 100644
index 0000000..c39eac2
--- /dev/null
+++ b/tests/UiBench/.gitignore
@@ -0,0 +1,5 @@
+.gradle
+.idea
+*.iml
+build
+local.properties
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
new file mode 100644
index 0000000..ce9db3d
--- /dev/null
+++ b/tests/UiBench/Android.mk
@@ -0,0 +1,23 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# omit gradle 'build' dir
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+# use appcompat/support lib from the tree, so improvements/
+# regressions are reflected in test data
+LOCAL_RESOURCE_DIR := \
+ frameworks/support/v7/appcompat/res
+
+LOCAL_AAPT_FLAGS := \
+ --extra-packages android.support.v7.appcompat
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-v4 \
+ android-support-v7-appcompat
+
+LOCAL_PACKAGE_NAME := UiBench
+
+include $(BUILD_PACKAGE)
diff --git a/tests/UiBench/AndroidManifest.xml b/tests/UiBench/AndroidManifest.xml
new file mode 100644
index 0000000..b19e109
--- /dev/null
+++ b/tests/UiBench/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<!--
+ ~ 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
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.uibench">
+
+ <application
+ android:allowBackup="false"
+ android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
+ <uses-library android:name="android.test.runner" />
+
+ <!-- Root navigation activity -->
+ <activity
+ android:name=".MainActivity"
+ android:label="UiBench">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <!-- Tests -->
+ <activity
+ android:name=".TrivialListActivity"
+ android:label="Microbenchmarks/Trivial ListActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/UiBench/build.gradle b/tests/UiBench/build.gradle
new file mode 100644
index 0000000..bf0ed11
--- /dev/null
+++ b/tests/UiBench/build.gradle
@@ -0,0 +1,37 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.3.0'
+
+ }
+}
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "22.0.0"
+
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ }
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
+}
+
+dependencies {
+ // Dependencies enumerated specifically for platform-independent / reproducible builds.
+ compile 'com.android.support:support-v4:23.0.0'
+ compile 'com.android.support:appcompat-v7:23.0.0'
+}
diff --git a/tests/UiBench/gradle/wrapper/gradle-wrapper.jar b/tests/UiBench/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/tests/UiBench/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tests/UiBench/gradle/wrapper/gradle-wrapper.properties b/tests/UiBench/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..12582f8
--- /dev/null
+++ b/tests/UiBench/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Aug 26 10:51:13 PDT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
diff --git a/tests/UiBench/src/com/android/test/uibench/MainActivity.java b/tests/UiBench/src/com/android/test/uibench/MainActivity.java
new file mode 100644
index 0000000..7c3391d
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/MainActivity.java
@@ -0,0 +1,166 @@
+/*
+ * 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.test.uibench;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.ListFragment;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MainActivity extends AppCompatActivity {
+ private static final String EXTRA_PATH = "activity_path";
+ private static final String CATEGORY_HWUI_TEST = "com.android.test.uibench.TEST";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ String path = intent.getStringExtra(EXTRA_PATH);
+
+ if (path == null) {
+ path = "";
+ } else {
+ // not root level, display where we are in the hierarchy
+ setTitle(path);
+ }
+
+ FragmentManager fm = getSupportFragmentManager();
+ if (fm.findFragmentById(android.R.id.content) == null) {
+ ListFragment listFragment = new ListFragment() {
+ @Override
+ @SuppressWarnings({ "unchecked" })
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ Map<String, Object> map = (Map<String, Object>)l.getItemAtPosition(position);
+
+ Intent intent = (Intent) map.get("intent");
+ startActivity(intent);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setTextFilterEnabled(true);
+ }
+ };
+ listFragment.setListAdapter(new SimpleAdapter(this, getData(path),
+ android.R.layout.simple_list_item_1, new String[] { "title" },
+ new int[] { android.R.id.text1 }));
+ fm.beginTransaction().add(android.R.id.content, listFragment).commit();
+ }
+ }
+
+ protected List<Map<String, Object>> getData(String prefix) {
+ List<Map<String, Object>> myData = new ArrayList<>();
+
+ Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(CATEGORY_HWUI_TEST);
+
+ PackageManager pm = getPackageManager();
+ List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0);
+
+ if (null == list)
+ return myData;
+
+ String[] prefixPath;
+ String prefixWithSlash = prefix;
+
+ if (prefix.equals("")) {
+ prefixPath = null;
+ } else {
+ prefixPath = prefix.split("/");
+ prefixWithSlash = prefix + "/";
+ }
+
+ int len = list.size();
+
+ Map<String, Boolean> entries = new HashMap<>();
+
+ for (int i = 0; i < len; i++) {
+ ResolveInfo info = list.get(i);
+ CharSequence labelSeq = info.loadLabel(pm);
+ String label = labelSeq != null
+ ? labelSeq.toString()
+ : info.activityInfo.name;
+
+ if (prefixWithSlash.length() == 0 || label.startsWith(prefixWithSlash)) {
+
+ String[] labelPath = label.split("/");
+
+ String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length];
+
+ if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1) {
+ addItem(myData, nextLabel, activityIntent(
+ info.activityInfo.applicationInfo.packageName,
+ info.activityInfo.name));
+ } else {
+ if (entries.get(nextLabel) == null) {
+ addItem(myData, nextLabel, browseIntent(prefix.equals("") ?
+ nextLabel : prefix + "/" + nextLabel));
+ entries.put(nextLabel, true);
+ }
+ }
+ }
+ }
+
+ Collections.sort(myData, sDisplayNameComparator);
+
+ return myData;
+ }
+
+ private final static Comparator<Map<String, Object>> sDisplayNameComparator =
+ new Comparator<Map<String, Object>>() {
+ private final Collator collator = Collator.getInstance();
+
+ public int compare(Map<String, Object> map1, Map<String, Object> map2) {
+ return collator.compare(map1.get("title"), map2.get("title"));
+ }
+ };
+
+ protected Intent activityIntent(String pkg, String componentName) {
+ Intent result = new Intent();
+ result.setClassName(pkg, componentName);
+ return result;
+ }
+
+ protected Intent browseIntent(String path) {
+ Intent result = new Intent();
+ result.setClass(this, MainActivity.class);
+ result.putExtra(EXTRA_PATH, path);
+ return result;
+ }
+
+ protected void addItem(List<Map<String, Object>> data, String name, Intent intent) {
+ Map<String, Object> temp = new HashMap<>();
+ temp.put("title", name);
+ temp.put("intent", intent);
+ data.add(temp);
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/TrivialListActivity.java b/tests/UiBench/src/com/android/test/uibench/TrivialListActivity.java
new file mode 100644
index 0000000..0af3471
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/TrivialListActivity.java
@@ -0,0 +1,55 @@
+/*
+ * 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.test.uibench;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.ListFragment;
+import android.support.v7.app.AppCompatActivity;
+import android.widget.ArrayAdapter;
+
+import java.util.Random;
+
+public class TrivialListActivity extends AppCompatActivity {
+ static final int STRING_LENGTH = 10;
+
+ static String[] buildStringList() {
+ String[] strings = new String[200];
+ Random random = new Random(0);
+ for (int i = 0; i < strings.length; i++) {
+ String result = "";
+ for (int j = 0; j < STRING_LENGTH; j++) {
+ // add random letter
+ result += (char)(random.nextInt(26) + 65);
+ }
+ strings[i] = result;
+ }
+ return strings;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FragmentManager fm = getSupportFragmentManager();
+ if (fm.findFragmentById(android.R.id.content) == null) {
+ ListFragment listFragment = new ListFragment();
+ listFragment.setListAdapter(new ArrayAdapter<>(TrivialListActivity.this,
+ android.R.layout.simple_list_item_1, buildStringList()));
+ fm.beginTransaction().add(android.R.id.content, listFragment).commit();
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index b76ec17..567002e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -33,6 +33,7 @@
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
@@ -227,16 +228,18 @@
* Find the background color for this bar from the theme attributes. Only relevant to StatusBar
* and NavigationBar.
* <p/>
- * Returns 0 if not found.
+ * Returns null if not found.
*
* @param colorAttrName the attribute name for the background color
* @param translucentAttrName the attribute name for the translucency property of the bar.
*
* @throws NumberFormatException if color resolved to an invalid string.
*/
- protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) {
+ @Nullable
+ protected Integer getBarColor(@NonNull String colorAttrName,
+ @NonNull String translucentAttrName) {
if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) {
- return 0;
+ return null;
}
RenderResources renderResources = getContext().getRenderResources();
// First check if the bar is translucent.
@@ -251,10 +254,11 @@
if (transparent) {
return getColor(renderResources, colorAttrName);
}
- return 0;
+ return null;
}
- private static int getColor(RenderResources renderResources, String attr) {
+ @Nullable
+ private static Integer getColor(RenderResources renderResources, String attr) {
// From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
ResourceValue resource = renderResources.findItemInTheme(attr, true);
// Form @color/bar to the #AARRGGBB
@@ -275,7 +279,7 @@
}
}
}
- return 0;
+ return null;
}
private ResourceValue getResourceValue(String reference) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index 9c89bfe2..d50ce23 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -65,8 +65,8 @@
super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML,
"navigation_bar.xml", simulatedPlatformVersion);
- int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
- setBackgroundColor(color == 0 ? 0xFF000000 : color);
+ Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
+ setBackgroundColor(color == null ? 0xFF000000 : color);
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index 2dc7c65..95a5a58 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -71,8 +71,9 @@
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
- int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
- setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
+ Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
+ setBackgroundColor(
+ color == null ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index ac7c409..24e1ce7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -421,8 +421,7 @@
gc.setComposite(AlphaComposite.Src);
gc.setColor(new Color(0x00000000, true));
- gc.fillRect(0, 0,
- mMeasuredScreenWidth, mMeasuredScreenHeight);
+ gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
// done
gc.dispose();