Merge "Revert "Temporarily switch off returning only enabled profiles to support dogfooding""
diff --git a/Android.mk b/Android.mk
index be7e055..232f5bf 100644
--- a/Android.mk
+++ b/Android.mk
@@ -180,6 +180,8 @@
core/java/android/os/IUserManager.aidl \
core/java/android/os/IVibratorService.aidl \
core/java/android/service/notification/INotificationListener.aidl \
+ core/java/android/service/notification/IConditionListener.aidl \
+ core/java/android/service/notification/IConditionProvider.aidl \
core/java/android/print/ILayoutResultCallback.aidl \
core/java/android/print/IPrinterDiscoveryObserver.aidl \
core/java/android/print/IPrintDocumentAdapter.aidl \
@@ -197,6 +199,9 @@
core/java/android/service/dreams/IDreamService.aidl \
core/java/android/service/trust/ITrustAgentService.aidl \
core/java/android/service/trust/ITrustAgentServiceCallback.aidl \
+ core/java/android/service/voice/IVoiceInteractionService.aidl \
+ core/java/android/service/voice/IVoiceInteractionSession.aidl \
+ core/java/android/service/voice/IVoiceInteractionSessionService.aidl \
core/java/android/service/wallpaper/IWallpaperConnection.aidl \
core/java/android/service/wallpaper/IWallpaperEngine.aidl \
core/java/android/service/wallpaper/IWallpaperService.aidl \
@@ -230,6 +235,10 @@
core/java/com/android/internal/app/IBatteryStats.aidl \
core/java/com/android/internal/app/IProcessStats.aidl \
core/java/com/android/internal/app/IUsageStats.aidl \
+ core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \
+ core/java/com/android/internal/app/IVoiceInteractor.aidl \
+ core/java/com/android/internal/app/IVoiceInteractorCallback.aidl \
+ core/java/com/android/internal/app/IVoiceInteractorRequest.aidl \
core/java/com/android/internal/app/IMediaContainerService.aidl \
core/java/com/android/internal/appwidget/IAppWidgetService.aidl \
core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
diff --git a/api/current.txt b/api/current.txt
index 30c9f98..7615d4f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31,6 +31,7 @@
field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
field public static final java.lang.String BIND_TRUST_AGENT_SERVICE = "android.permission.BIND_TRUST_AGENT_SERVICE";
field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
+ field public static final java.lang.String BIND_VOICE_INTERACTION = "android.permission.BIND_VOICE_INTERACTION";
field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER";
field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH";
@@ -490,6 +491,7 @@
field public static final int editTextStyle = 16842862; // 0x101006e
field public static final deprecated int editable = 16843115; // 0x101016b
field public static final int editorExtras = 16843300; // 0x1010224
+ field public static final int elevation = 16843852; // 0x101044c
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
field public static final int enabled = 16842766; // 0x101000e
@@ -499,7 +501,9 @@
field public static final int entries = 16842930; // 0x10100b2
field public static final int entryValues = 16843256; // 0x10101f8
field public static final int eventsInterceptionEnabled = 16843389; // 0x101027d
+ field public static final int excludeClass = 16843854; // 0x101044e
field public static final int excludeFromRecents = 16842775; // 0x1010017
+ field public static final int excludeId = 16843853; // 0x101044d
field public static final int exitFadeDuration = 16843533; // 0x101030d
field public static final int expandableListPreferredChildIndicatorLeft = 16842834; // 0x1010052
field public static final int expandableListPreferredChildIndicatorRight = 16842835; // 0x1010053
@@ -1008,6 +1012,7 @@
field public static final int selectedDateVerticalBar = 16843591; // 0x1010347
field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
field public static final int sequence = 16843815; // 0x1010427
+ field public static final int sessionService = 16843850; // 0x101044a
field public static final int settingsActivity = 16843301; // 0x1010225
field public static final int shadowColor = 16843105; // 0x1010161
field public static final int shadowDx = 16843106; // 0x1010162
@@ -1103,6 +1108,7 @@
field public static final int switchMinWidth = 16843632; // 0x1010370
field public static final int switchPadding = 16843633; // 0x1010371
field public static final int switchPreferenceStyle = 16843629; // 0x101036d
+ field public static final int switchStyle = 16843851; // 0x101044b
field public static final int switchTextAppearance = 16843630; // 0x101036e
field public static final int switchTextOff = 16843628; // 0x101036c
field public static final int switchTextOn = 16843627; // 0x101036b
@@ -1626,6 +1632,7 @@
field public static final int selectAll = 16908319; // 0x102001f
field public static final int selectTextMode = 16908333; // 0x102002d
field public static final int selectedIcon = 16908302; // 0x102000e
+ field public static final int shared_element = 16908355; // 0x1020043
field public static final int shared_element_name = 16908353; // 0x1020041
field public static final int startSelectingText = 16908328; // 0x1020028
field public static final int stopSelectingText = 16908329; // 0x1020029
@@ -1662,10 +1669,14 @@
field public static final int decelerate_cubic = 17563651; // 0x10c0003
field public static final int decelerate_quad = 17563649; // 0x10c0001
field public static final int decelerate_quint = 17563653; // 0x10c0005
- field public static final int fast_out_linear_in = 17563663; // 0x10c000f
- field public static final int fast_out_slow_in = 17563661; // 0x10c000d
+ field public static final int fast_out_linear_in = 17563667; // 0x10c0013
+ field public static final int fast_out_slow_in = 17563665; // 0x10c0011
+ field public static final int l_resource_pad1 = 17563664; // 0x10c0010
+ field public static final int l_resource_pad2 = 17563663; // 0x10c000f
+ field public static final int l_resource_pad3 = 17563662; // 0x10c000e
+ field public static final int l_resource_pad4 = 17563661; // 0x10c000d
field public static final int linear = 17563659; // 0x10c000b
- field public static final int linear_out_slow_in = 17563662; // 0x10c000e
+ field public static final int linear_out_slow_in = 17563666; // 0x10c0012
field public static final int overshoot = 17563656; // 0x10c0008
}
@@ -3202,6 +3213,7 @@
method public int getTaskId();
method public final java.lang.CharSequence getTitle();
method public final int getTitleColor();
+ method public android.app.VoiceInteractor getVoiceInteractor();
method public final int getVolumeControlStream();
method public android.view.Window getWindow();
method public android.view.WindowManager getWindowManager();
@@ -3213,6 +3225,7 @@
method public boolean isFinishing();
method public boolean isImmersive();
method public boolean isTaskRoot();
+ method public boolean isVoiceInteraction();
method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public boolean moveTaskToBack(boolean);
method public boolean navigateUpTo(android.content.Intent);
@@ -3523,8 +3536,9 @@
public static class ActivityOptions.ActivityTransitionListener {
ctor public ActivityOptions.ActivityTransitionListener();
method public android.util.Pair<android.view.View, java.lang.String>[] getSharedElementsMapping();
- method public void onCaptureSharedElementEnd();
- method public void onCaptureSharedElementStart();
+ method public boolean handleRejectedSharedElements(java.util.List<android.view.View>);
+ method public void onCaptureSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
+ method public void onCaptureSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
method public void onEnterReady();
method public void onExitTransitionComplete();
method public void onRemoteExitComplete();
@@ -4838,6 +4852,29 @@
field public static final int MODE_NIGHT_YES = 2; // 0x2
}
+ public class VoiceInteractor {
+ method public boolean submitRequest(android.app.VoiceInteractor.Request);
+ method public boolean[] supportsCommands(java.lang.String[]);
+ }
+
+ public static class VoiceInteractor.CommandRequest extends android.app.VoiceInteractor.Request {
+ ctor public VoiceInteractor.CommandRequest(java.lang.String, android.os.Bundle);
+ method public void onCommandResult(android.os.Bundle);
+ }
+
+ public static class VoiceInteractor.ConfirmationRequest extends android.app.VoiceInteractor.Request {
+ ctor public VoiceInteractor.ConfirmationRequest(java.lang.CharSequence, android.os.Bundle);
+ method public void onConfirmationResult(boolean, android.os.Bundle);
+ }
+
+ public static abstract class VoiceInteractor.Request {
+ ctor public VoiceInteractor.Request();
+ method public void cancel();
+ method public android.app.Activity getActivity();
+ method public android.content.Context getContext();
+ method public void onCancel();
+ }
+
public final class WallpaperInfo implements android.os.Parcelable {
ctor public WallpaperInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
@@ -4947,7 +4984,11 @@
public class DevicePolicyManager {
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
+ method public void addUserRestriction(android.content.ComponentName, java.lang.String);
+ method public void clearForwardingIntentFilters(android.content.ComponentName);
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
+ method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
+ method public void forwardMatchingIntents(android.content.ComponentName, android.content.IntentFilter, int);
method public java.util.List<android.content.ComponentName> getActiveAdmins();
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5007,9 +5048,14 @@
field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
field public static final java.lang.String EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME = "defaultManagedProfileName";
field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "deviceAdminPackageName";
+ field public static int FLAG_TO_MANAGED_PROFILE;
+ field public static int FLAG_TO_PRIMARY_USER;
field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0
field public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 2; // 0x2
+ field public static final int KEYGUARD_DISABLE_SECURE_NOTIFICATIONS = 4; // 0x4
+ field public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 16; // 0x10
+ field public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 8; // 0x8
field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1; // 0x1
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
@@ -6997,6 +7043,7 @@
field public static final java.lang.String CATEGORY_TAB = "android.intent.category.TAB";
field public static final java.lang.String CATEGORY_TEST = "android.intent.category.TEST";
field public static final java.lang.String CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST";
+ field public static final java.lang.String CATEGORY_VOICE = "android.intent.category.VOICE";
field public static final android.os.Parcelable.Creator CREATOR;
field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
field public static final java.lang.String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE";
@@ -7712,12 +7759,15 @@
method public long getFirstInstallTime();
method public android.graphics.drawable.Drawable getIcon(int);
method public java.lang.CharSequence getLabel();
+ method public java.lang.String getName();
method public android.os.UserHandle getUser();
}
public class LauncherApps {
method public synchronized void addOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
+ method public boolean isActivityEnabledForProfile(android.content.ComponentName, android.os.UserHandle);
+ method public boolean isPackageEnabledForProfile(java.lang.String, android.os.UserHandle);
method public synchronized void removeOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
method public void startActivityForProfile(android.content.ComponentName, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
@@ -10023,11 +10073,18 @@
method public void setPaint(android.graphics.Paint);
}
- public class Outline {
+ public final class Outline {
ctor public Outline();
- method public final boolean isValid();
+ ctor public Outline(android.graphics.Outline);
+ method public boolean isValid();
method public void set(android.graphics.Outline);
+ method public void setConvexPath(android.graphics.Path);
+ method public void setOval(int, int, int, int);
+ method public void setOval(android.graphics.Rect);
+ method public void setRect(int, int, int, int);
+ method public void setRect(android.graphics.Rect);
method public void setRoundRect(int, int, int, int, float);
+ method public void setRoundRect(android.graphics.Rect, float);
}
public class Paint {
@@ -10196,6 +10253,7 @@
method public void addArc(android.graphics.RectF, float, float);
method public void addCircle(float, float, float, android.graphics.Path.Direction);
method public void addOval(android.graphics.RectF, android.graphics.Path.Direction);
+ method public void addOval(float, float, float, float, android.graphics.Path.Direction);
method public void addPath(android.graphics.Path, float, float);
method public void addPath(android.graphics.Path);
method public void addPath(android.graphics.Path, android.graphics.Matrix);
@@ -10750,7 +10808,7 @@
method public int getMinimumHeight();
method public int getMinimumWidth();
method public abstract int getOpacity();
- method public android.graphics.Outline getOutline();
+ method public boolean getOutline(android.graphics.Outline);
method public boolean getPadding(android.graphics.Rect);
method public int[] getState();
method public android.graphics.Region getTransparentRegion();
@@ -11123,6 +11181,7 @@
method public android.graphics.drawable.shapes.Shape clone() throws java.lang.CloneNotSupportedException;
method public abstract void draw(android.graphics.Canvas, android.graphics.Paint);
method public final float getHeight();
+ method public boolean getOutline(android.graphics.Outline);
method public final float getWidth();
method public boolean hasAlpha();
method protected void onResize(float, float);
@@ -23622,7 +23681,11 @@
public final class TvContract {
method public static final android.net.Uri buildChannelUri(long);
+ method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName);
+ method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName, boolean);
method public static final android.net.Uri buildProgramUri(long);
+ method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
+ method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
field public static final java.lang.String AUTHORITY = "com.android.tv";
}
@@ -24878,6 +24941,42 @@
}
+package android.service.voice {
+
+ public class VoiceInteractionService extends android.app.Service {
+ ctor public VoiceInteractionService();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public void startVoiceActivity(android.content.Intent, android.os.Bundle);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
+ field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
+ }
+
+ public abstract class VoiceInteractionSession {
+ ctor public VoiceInteractionSession(android.content.Context);
+ ctor public VoiceInteractionSession(android.content.Context, android.os.Handler);
+ method public abstract void onCancel(android.service.voice.VoiceInteractionSession.Request);
+ method public abstract void onCommand(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
+ method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
+ method public abstract boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
+ }
+
+ public static class VoiceInteractionSession.Caller {
+ }
+
+ public static class VoiceInteractionSession.Request {
+ method public void sendCancelResult();
+ method public void sendCommandResult(boolean, android.os.Bundle);
+ method public void sendConfirmResult(boolean, android.os.Bundle);
+ }
+
+ public abstract class VoiceInteractionSessionService extends android.app.Service {
+ ctor public VoiceInteractionSessionService();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract android.service.voice.VoiceInteractionSession onNewSession(android.os.Bundle);
+ }
+
+}
+
package android.service.wallpaper {
public abstract class WallpaperService extends android.app.Service {
@@ -27865,6 +27964,18 @@
method public void setResizeClip(boolean);
}
+ public class ChangeClipBounds extends android.transition.Transition {
+ ctor public ChangeClipBounds();
+ method public void captureEndValues(android.transition.TransitionValues);
+ method public void captureStartValues(android.transition.TransitionValues);
+ }
+
+ public class ChangeTransform extends android.transition.Transition {
+ ctor public ChangeTransform();
+ method public void captureEndValues(android.transition.TransitionValues);
+ method public void captureStartValues(android.transition.TransitionValues);
+ }
+
public class CircularPropagation extends android.transition.VisibilityPropagation {
ctor public CircularPropagation();
method public long getStartDelay(android.view.ViewGroup, android.transition.Transition, android.transition.TransitionValues, android.transition.TransitionValues);
@@ -27925,6 +28036,7 @@
ctor public Transition();
method public android.transition.Transition addListener(android.transition.Transition.TransitionListener);
method public android.transition.Transition addTarget(int);
+ method public android.transition.Transition addTarget(java.lang.Class);
method public android.transition.Transition addTarget(android.view.View);
method public boolean canRemoveViews();
method public abstract void captureEndValues(android.transition.TransitionValues);
@@ -28078,12 +28190,19 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.tv.TvInputService";
}
- public abstract class TvInputService.TvInputSessionImpl {
+ public abstract class TvInputService.TvInputSessionImpl implements android.view.KeyEvent.Callback {
ctor public TvInputService.TvInputSessionImpl();
method public android.view.View onCreateOverlayView();
+ method public boolean onGenericMotionEvent(android.view.MotionEvent);
+ method public boolean onKeyDown(int, android.view.KeyEvent);
+ method public boolean onKeyLongPress(int, android.view.KeyEvent);
+ method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
+ method public boolean onKeyUp(int, android.view.KeyEvent);
method public abstract void onRelease();
method public abstract boolean onSetSurface(android.view.Surface);
method public abstract void onSetVolume(float);
+ method public boolean onTouchEvent(android.view.MotionEvent);
+ method public boolean onTrackballEvent(android.view.MotionEvent);
method public abstract boolean onTune(android.net.Uri);
method public void setOverlayViewEnabled(boolean);
}
@@ -28093,9 +28212,16 @@
ctor public TvView(android.content.Context, android.util.AttributeSet);
ctor public TvView(android.content.Context, android.util.AttributeSet, int);
method public void bindTvInput(android.content.ComponentName, android.tv.TvInputManager.SessionCreateCallback);
+ method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
+ method public boolean onUnhandledInputEvent(android.view.InputEvent);
+ method public void setOnUnhandledInputEventListener(android.tv.TvView.OnUnhandledInputEventListener);
method public void unbindTvInput();
}
+ public static abstract interface TvView.OnUnhandledInputEventListener {
+ method public abstract boolean onUnhandledInputEvent(android.view.InputEvent);
+ }
+
}
package android.util {
@@ -30037,6 +30163,7 @@
method public int getDrawingCacheQuality();
method public void getDrawingRect(android.graphics.Rect);
method public long getDrawingTime();
+ method public float getElevation();
method public boolean getFilterTouchesWhenObscured();
method public boolean getFitsSystemWindows();
method public java.util.ArrayList<android.view.View> getFocusables(int);
@@ -30135,6 +30262,7 @@
method public void getWindowVisibleDisplayFrame(android.graphics.Rect);
method public float getX();
method public float getY();
+ method public float getZ();
method public boolean hasFocus();
method public boolean hasFocusable();
method public boolean hasNestedScrollingParent();
@@ -30301,6 +30429,7 @@
method public void setDrawingCacheEnabled(boolean);
method public void setDrawingCacheQuality(int);
method public void setDuplicateParentStateEnabled(boolean);
+ method public void setElevation(float);
method public void setEnabled(boolean);
method public void setFadingEdgeLength(int);
method public void setFilterTouchesWhenObscured(boolean);
@@ -30386,6 +30515,7 @@
method public void setWillNotDraw(boolean);
method public void setX(float);
method public void setY(float);
+ method public void setZ(float);
method public boolean showContextMenu();
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
method public void startAnimation(android.view.animation.Animation);
@@ -30519,6 +30649,7 @@
field protected static final int[] WINDOW_FOCUSED_STATE_SET;
field public static final android.util.Property X;
field public static final android.util.Property Y;
+ field public static final android.util.Property Z;
}
public static class View.AccessibilityDelegate {
@@ -30965,6 +31096,8 @@
method public android.view.ViewPropertyAnimator xBy(float);
method public android.view.ViewPropertyAnimator y(float);
method public android.view.ViewPropertyAnimator yBy(float);
+ method public android.view.ViewPropertyAnimator z(float);
+ method public android.view.ViewPropertyAnimator zBy(float);
}
public final class ViewStub extends android.view.View {
@@ -34446,6 +34579,7 @@
method public void setExcludeMimes(java.lang.String[]);
method public void setImageToDefault();
method public void setMode(int);
+ method public void setOverlay(android.graphics.drawable.Drawable);
field protected java.lang.String[] mExcludeMimes;
}
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 7df55a5..6b55b7b 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -749,6 +749,11 @@
"Error: Activity not started, you do not "
+ "have permission to access it.");
break;
+ case ActivityManager.START_NOT_VOICE_COMPATIBLE:
+ out.println(
+ "Error: Activity not started, voice control not allowed for: "
+ + intent);
+ break;
default:
out.println(
"Error: Activity not started, unknown error code " + res);
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 3481437..74ccbc2 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -15,10 +15,12 @@
#include <cutils/properties.h>
#include <cutils/trace.h>
#include <android_runtime/AndroidRuntime.h>
+#include <private/android_filesystem_config.h> // for AID_SYSTEM
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
+#include <sys/prctl.h>
namespace android {
@@ -136,6 +138,44 @@
return (end - start);
}
+static void maybeCreateDalvikCache() {
+#if defined(__aarch64__)
+ static const char kInstructionSet[] = "arm64";
+#elif defined(__x86_64__)
+ static const char kInstructionSet[] = "x86_64";
+#elif defined(__arm__)
+ static const char kInstructionSet[] = "arm";
+#elif defined(__i386__)
+ static const char kInstructionSet[] = "x86";
+#elif defined (__mips__)
+ static const char kInstructionSet[] = "mips";
+#else
+#error "Unknown instruction set"
+#endif
+ const char* androidRoot = getenv("ANDROID_DATA");
+ LOG_ALWAYS_FATAL_IF(androidRoot == NULL, "ANDROID_DATA environment variable unset");
+
+ char dalvikCacheDir[PATH_MAX];
+ const int numChars = snprintf(dalvikCacheDir, PATH_MAX,
+ "%s/dalvik-cache/%s", androidRoot, kInstructionSet);
+ LOG_ALWAYS_FATAL_IF((numChars >= PATH_MAX || numChars < 0),
+ "Error constructing dalvik cache : %s", strerror(errno));
+
+ int result = mkdir(dalvikCacheDir, 0771);
+ LOG_ALWAYS_FATAL_IF((result < 0 && errno != EEXIST),
+ "Error creating cache dir %s : %s", dalvikCacheDir, strerror(errno));
+
+ // We always perform these steps because the directory might
+ // already exist, with wider permissions and a different owner
+ // than we'd like.
+ result = chown(dalvikCacheDir, AID_SYSTEM, AID_SYSTEM);
+ LOG_ALWAYS_FATAL_IF((result < 0), "Error changing dalvik-cache ownership : %s", strerror(errno));
+
+ result = chmod(dalvikCacheDir, 0771);
+ LOG_ALWAYS_FATAL_IF((result < 0),
+ "Error changing dalvik-cache permissions : %s", strerror(errno));
+}
+
#if defined(__LP64__)
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
static const char ZYGOTE_NICE_NAME[] = "zygote64";
@@ -146,6 +186,15 @@
int main(int argc, char* const argv[])
{
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+ // Older kernels don't understand PR_SET_NO_NEW_PRIVS and return
+ // EINVAL. Don't die on such kernels.
+ if (errno != EINVAL) {
+ LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
+ return 12;
+ }
+ }
+
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments
// ignore argv[0]
@@ -213,6 +262,9 @@
args.add(application ? String8("application") : String8("tool"));
runtime.setClassNameAndArgs(className, argc - i, argv + i);
} else {
+ // We're in zygote mode.
+ maybeCreateDalvikCache();
+
if (startSystemServer) {
args.add(String8("start-system-server"));
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 41afa39..3dc024e 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -51,6 +51,7 @@
#include "BootAnimation.h"
+#define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
#define EXIT_PROP_NAME "service.bootanim.exit"
@@ -155,12 +156,12 @@
return NO_ERROR;
}
-status_t BootAnimation::initTexture(void* buffer, size_t len)
+status_t BootAnimation::initTexture(const Animation::Frame& frame)
{
//StopWatch watch("blah");
SkBitmap bitmap;
- SkMemoryStream stream(buffer, len);
+ SkMemoryStream stream(frame.map->getDataPtr(), frame.map->getDataLength());
SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
if (codec) {
codec->setDitherImage(false);
@@ -170,6 +171,11 @@
delete codec;
}
+ // FileMap memory is never released until application exit.
+ // Release it now as the texture is already loaded and the memory used for
+ // the packed resource can be released.
+ frame.map->release();
+
// ensure we can call getPixels(). No need to call unlock, since the
// bitmap will go out of scope when we return from this method.
bitmap.lockPixels();
@@ -283,6 +289,9 @@
(access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
+ ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
+ ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||
+
((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
mZip = zipFile;
@@ -405,6 +414,7 @@
String8 desString((char const*)descMap->getDataPtr(),
descMap->getDataLength());
+ descMap->release();
char const* s = desString.string();
Animation animation;
@@ -529,9 +539,7 @@
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
- initTexture(
- frame.map->getDataPtr(),
- frame.map->getDataLength());
+ initTexture(frame);
}
if (!clearReg.isEmpty()) {
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 22963c2..ba1c507 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -79,7 +79,7 @@
};
status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
- status_t initTexture(void* buffer, size_t len);
+ status_t initTexture(const Animation::Frame& frame);
bool android();
bool movie();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 599a608..8981c88 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -22,6 +22,7 @@
import android.util.ArrayMap;
import android.util.SuperNotCalledException;
import android.widget.Toolbar;
+import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.WindowDecorActionBar;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.policy.PolicyManager;
@@ -726,6 +727,8 @@
/*package*/ ActionBar mActionBar = null;
private boolean mEnableDefaultActionBarUp;
+ private VoiceInteractor mVoiceInteractor;
+
private CharSequence mTitle;
private int mTitleColor = 0;
@@ -1134,6 +1137,23 @@
}
/**
+ * Check whether this activity is running as part of a voice interaction with the user.
+ * If true, it should perform its interaction with the user through the
+ * {@link VoiceInteractor} returned by {@link #getVoiceInteractor}.
+ */
+ public boolean isVoiceInteraction() {
+ return mVoiceInteractor != null;
+ }
+
+ /**
+ * Retrieve the active {@link VoiceInteractor} that the user is going through to
+ * interact with this activity.
+ */
+ public VoiceInteractor getVoiceInteractor() {
+ return mVoiceInteractor;
+ }
+
+ /**
* This is called for activities that set launchMode to "singleTop" in
* their package, or if a client used the {@link Intent#FLAG_ACTIVITY_SINGLE_TOP}
* flag when calling {@link #startActivity}. In either case, when the
@@ -5397,7 +5417,7 @@
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config) {
attach(context, aThread, instr, token, ident, application, intent, info, title, parent, id,
- lastNonConfigurationInstances, config, null);
+ lastNonConfigurationInstances, config, null, null);
}
final void attach(Context context, ActivityThread aThread,
@@ -5405,7 +5425,7 @@
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
- Configuration config, Bundle options) {
+ Configuration config, Bundle options, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachActivity(this, mContainer, null);
@@ -5433,6 +5453,8 @@
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
+ mVoiceInteractor = voiceInteractor != null
+ ? new VoiceInteractor(this, this, voiceInteractor, Looper.myLooper()) : null;
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c027e99..9239faf 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -76,6 +76,13 @@
public static final String META_HOME_ALTERNATE = "android.app.home.alternate";
/**
+ * Result for IActivityManager.startActivity: trying to start an activity under voice
+ * control when that activity does not support the VOICE category.
+ * @hide
+ */
+ public static final int START_NOT_VOICE_COMPATIBLE = -7;
+
+ /**
* Result for IActivityManager.startActivity: an error where the
* start had to be canceled.
* @hide
@@ -1620,13 +1627,6 @@
public int lastTrimLevel;
/**
- * Constant for {@link #importance}: this is a persistent process.
- * Only used when reporting to process observers.
- * @hide
- */
- public static final int IMPORTANCE_PERSISTENT = 50;
-
- /**
* Constant for {@link #importance}: this process is running the
* foreground UI.
*/
@@ -1741,9 +1741,16 @@
*/
public int importanceReasonImportance;
+ /**
+ * Current process state, as per PROCESS_STATE_* constants.
+ * @hide
+ */
+ public int processState;
+
public RunningAppProcessInfo() {
importance = IMPORTANCE_FOREGROUND;
importanceReasonCode = REASON_UNKNOWN;
+ processState = PROCESS_STATE_IMPORTANT_FOREGROUND;
}
public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) {
@@ -1769,6 +1776,7 @@
dest.writeInt(importanceReasonPid);
ComponentName.writeToParcel(importanceReasonComponent, dest);
dest.writeInt(importanceReasonImportance);
+ dest.writeInt(processState);
}
public void readFromParcel(Parcel source) {
@@ -1784,6 +1792,7 @@
importanceReasonPid = source.readInt();
importanceReasonComponent = ComponentName.readFromParcel(source);
importanceReasonImportance = source.readInt();
+ processState = source.readInt();
}
public static final Creator<RunningAppProcessInfo> CREATOR =
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 10831f2..b1c37de 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -43,9 +43,11 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
+import android.service.voice.IVoiceInteractionSession;
import android.text.TextUtils;
import android.util.Log;
import android.util.Singleton;
+import com.android.internal.app.IVoiceInteractor;
import java.util.ArrayList;
import java.util.List;
@@ -242,6 +244,33 @@
return true;
}
+ case START_VOICE_ACTIVITY_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ String callingPackage = data.readString();
+ int callingPid = data.readInt();
+ int callingUid = data.readInt();
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ String resolvedType = data.readString();
+ IVoiceInteractionSession session = IVoiceInteractionSession.Stub.asInterface(
+ data.readStrongBinder());
+ IVoiceInteractor interactor = IVoiceInteractor.Stub.asInterface(
+ data.readStrongBinder());
+ int startFlags = data.readInt();
+ String profileFile = data.readString();
+ ParcelFileDescriptor profileFd = data.readInt() != 0
+ ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null;
+ Bundle options = data.readInt() != 0
+ ? Bundle.CREATOR.createFromParcel(data) : null;
+ int userId = data.readInt();
+ int result = startVoiceActivity(callingPackage, callingPid, callingUid,
+ intent, resolvedType, session, interactor, startFlags,
+ profileFile, profileFd, options, userId);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
+
case START_NEXT_MATCHING_ACTIVITY_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
@@ -2323,6 +2352,42 @@
data.recycle();
return result;
}
+ public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
+ Intent intent, String resolvedType, IVoiceInteractionSession session,
+ IVoiceInteractor interactor, int startFlags, String profileFile,
+ ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(callingPackage);
+ data.writeInt(callingPid);
+ data.writeInt(callingUid);
+ intent.writeToParcel(data, 0);
+ data.writeString(resolvedType);
+ data.writeStrongBinder(session.asBinder());
+ data.writeStrongBinder(interactor.asBinder());
+ data.writeInt(startFlags);
+ data.writeString(profileFile);
+ if (profileFd != null) {
+ data.writeInt(1);
+ profileFd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ } else {
+ data.writeInt(0);
+ }
+ if (options != null) {
+ data.writeInt(1);
+ options.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ data.writeInt(userId);
+ mRemote.transact(START_VOICE_ACTIVITY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 85464c47..a49359f 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -649,13 +649,31 @@
/**
* Called when the start state for shared elements is captured on enter.
+ *
+ * @param sharedElementNames The names of the shared elements that were accepted into
+ * the View hierarchy.
+ * @param sharedElements The shared elements that are part of the View hierarchy.
+ * @param sharedElementSnapshots The Views containing snap shots of the shared element
+ * from the launching Window. These elements will not
+ * be part of the scene, but will be positioned relative
+ * to the Window decor View.
*/
- public void onCaptureSharedElementStart() {}
+ public void onCaptureSharedElementStart(List<String> sharedElementNames,
+ List<View> sharedElements, List<View> sharedElementSnapshots) {}
/**
* Called when the end state for shared elements is captured on enter.
+ *
+ * @param sharedElementNames The names of the shared elements that were accepted into
+ * the View hierarchy.
+ * @param sharedElements The shared elements that are part of the View hierarchy.
+ * @param sharedElementSnapshots The Views containing snap shots of the shared element
+ * from the launching Window. These elements will not
+ * be part of the scene, but will be positioned relative
+ * to the Window decor View.
*/
- public void onCaptureSharedElementEnd() {}
+ public void onCaptureSharedElementEnd(List<String> sharedElementNames,
+ List<View> sharedElements, List<View> sharedElementSnapshots) {}
/**
* Called when the enter Transition has been started.
@@ -700,6 +718,22 @@
* call.
*/
public Pair<View, String>[] getSharedElementsMapping() { return null; }
+
+ /**
+ * Returns <code>true</code> if the ActivityTransitionListener will handle removing
+ * rejected shared elements from the scene. If <code>false</code> is returned, a default
+ * animation will be used to remove the rejected shared elements from the scene.
+ *
+ * @param rejectedSharedElements Views containing visual information of shared elements
+ * that are not part of the entering scene. These Views
+ * are positioned relative to the Window decor View.
+ * @return <code>false</code> if the default animation should be used to remove the
+ * rejected shared elements from the scene or <code>true</code> if the listener provides
+ * custom handling.
+ */
+ public boolean handleRejectedSharedElements(List<View> rejectedSharedElements) {
+ return false;
+ }
}
private static class SharedElementMappingListener extends ActivityTransitionListener {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4562d6e..7dc21b4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -94,6 +94,7 @@
import android.renderscript.RenderScript;
import android.security.AndroidKeyStoreProvider;
+import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SamplingProfilerIntegration;
@@ -265,6 +266,7 @@
IBinder token;
int ident;
Intent intent;
+ IVoiceInteractor voiceInteractor;
Bundle state;
Activity activity;
Window window;
@@ -603,6 +605,7 @@
// activity itself back to the activity manager. (matters more with ipc)
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
+ IVoiceInteractor voiceInteractor,
int procState, Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
@@ -615,6 +618,7 @@
r.token = token;
r.ident = ident;
r.intent = intent;
+ r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
@@ -2197,7 +2201,8 @@
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
- r.embeddedID, r.lastNonConfigurationInstances, config, options);
+ r.embeddedID, r.lastNonConfigurationInstances, config, options,
+ r.voiceInteractor);
if (customIntent != null) {
activity.mIntent = customIntent;
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index d8a356f..3c1455b 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -15,6 +15,12 @@
*/
package android.app;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -26,8 +32,10 @@
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewGroupOverlay;
import android.view.ViewTreeObserver;
import android.view.Window;
+import android.widget.ImageView;
import java.util.ArrayList;
import java.util.Collection;
@@ -129,6 +137,7 @@
private static final String KEY_WIDTH = "shared_element:width";
private static final String KEY_HEIGHT = "shared_element:height";
private static final String KEY_NAME = "shared_element:name";
+ private static final String KEY_BITMAP = "shared_element:bitmap";
/**
* Sent by the exiting coordinator (either EnterTransitionCoordinator
@@ -197,6 +206,7 @@
private ResultReceiver mRemoteResultReceiver;
private boolean mNotifiedSharedElementTransitionComplete;
private boolean mNotifiedExitTransitionComplete;
+ private boolean mSharedElementTransitionStarted;
private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
@@ -241,7 +251,11 @@
onPrepareRestore();
break;
case MSG_EXIT_TRANSITION_COMPLETE:
- onRemoteSceneExitComplete();
+ if (!mSharedElementTransitionStarted) {
+ send(resultCode, resultData);
+ } else {
+ onRemoteSceneExitComplete();
+ }
break;
case MSG_TAKE_SHARED_ELEMENTS:
ArrayList<String> sharedElementNames
@@ -305,16 +319,23 @@
setSharedElements();
reconcileSharedElements(sharedElementNames);
mEnteringViews.removeAll(mSharedElements);
- setSharedElementState(state);
+ final ArrayList<View> accepted = new ArrayList<View>();
+ final ArrayList<View> rejected = new ArrayList<View>();
+ createSharedElementImages(accepted, rejected, sharedElementNames, state);
+ setSharedElementState(state, accepted);
+ handleRejected(rejected);
+
if (getViewsTransition() != null) {
setViewVisibility(mEnteringViews, View.INVISIBLE);
}
setViewVisibility(mSharedElements, View.VISIBLE);
Transition transition = beginTransition(mEnteringViews, true, allowOverlappingTransitions(),
true);
+
if (allowOverlappingTransitions()) {
onStartEnterTransition(transition, mEnteringViews);
}
+
mRemoteResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
}
@@ -440,9 +461,13 @@
mTargetSharedNames.clear();
if (sharedElements == null) {
ArrayMap<String, View> map = new ArrayMap<String, View>();
- setViewVisibility(mEnteringViews, View.VISIBLE);
+ if (getViewsTransition() != null) {
+ setViewVisibility(mEnteringViews, View.VISIBLE);
+ }
getDecor().findSharedElements(map);
- setViewVisibility(mEnteringViews, View.INVISIBLE);
+ if (getViewsTransition() != null) {
+ setViewVisibility(mEnteringViews, View.INVISIBLE);
+ }
for (int i = 0; i < map.size(); i++) {
View view = map.valueAt(i);
String name = map.keyAt(i);
@@ -513,35 +538,60 @@
}
private void reconcileSharedElements(ArrayList<String> sharedElementNames) {
- Rect epicenter = null;
- for (int i = mTargetSharedNames.size() - 1; i >= 0; i--) {
- if (!sharedElementNames.contains(mTargetSharedNames.get(i))) {
- mTargetSharedNames.remove(i);
- mSharedElements.remove(i);
+ // keep only those that are in sharedElementNames.
+ int numSharedElements = sharedElementNames.size();
+ int targetIndex = 0;
+ for (int i = 0; i < numSharedElements; i++) {
+ String name = sharedElementNames.get(i);
+ int index = mTargetSharedNames.indexOf(name);
+ if (index >= 0) {
+ // Swap the items at the indexes if necessary.
+ if (index != targetIndex) {
+ View temp = mSharedElements.get(index);
+ mSharedElements.set(index, mSharedElements.get(targetIndex));
+ mSharedElements.set(targetIndex, temp);
+ mTargetSharedNames.set(index, mTargetSharedNames.get(targetIndex));
+ mTargetSharedNames.set(targetIndex, name);
+ }
+ targetIndex++;
}
}
- if (!mSharedElements.isEmpty()) {
+ for (int i = mSharedElements.size() - 1; i >= targetIndex; i--) {
+ mSharedElements.remove(i);
+ mTargetSharedNames.remove(i);
+ }
+ Rect epicenter = null;
+ if (!mTargetSharedNames.isEmpty()
+ && mTargetSharedNames.get(0).equals(sharedElementNames.get(0))) {
epicenter = calcEpicenter(mSharedElements.get(0));
}
mEpicenterCallback.setEpicenter(epicenter);
}
- private void setSharedElementState(Bundle sharedElementState) {
+ private void setSharedElementState(Bundle sharedElementState,
+ final ArrayList<View> acceptedOverlayViews) {
+ final int[] tempLoc = new int[2];
if (sharedElementState != null) {
- int[] tempLoc = new int[2];
for (int i = 0; i < mSharedElements.size(); i++) {
View sharedElement = mSharedElements.get(i);
+ View parent = (View) sharedElement.getParent();
+ parent.getLocationOnScreen(tempLoc);
String name = mTargetSharedNames.get(i);
setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
+ sharedElement.requestLayout();
}
}
- mListener.onCaptureSharedElementStart();
+ mListener.onCaptureSharedElementStart(mTargetSharedNames, mSharedElements,
+ acceptedOverlayViews);
+
getDecor().getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
- mListener.onCaptureSharedElementEnd();
+ mListener.onCaptureSharedElementEnd(mTargetSharedNames, mSharedElements,
+ acceptedOverlayViews);
+ mSharedElementTransitionStarted = true;
return true;
}
}
@@ -555,10 +605,10 @@
* @param name The shared element name given from the source Activity.
* @param transitionArgs A <code>Bundle</code> containing all placementinformation for named
* shared elements in the scene.
- * @param tempLoc A temporary int[2] for capturing the current location of views.
+ * @param parentLoc The x and y coordinates of the parent's screen position.
*/
private static void setSharedElementState(View view, String name, Bundle transitionArgs,
- int[] tempLoc) {
+ int[] parentLoc) {
Bundle sharedElementBundle = transitionArgs.getBundle(name);
if (sharedElementBundle == null) {
return;
@@ -576,15 +626,11 @@
int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
view.measure(widthSpec, heightSpec);
- ViewGroup parent = (ViewGroup) view.getParent();
- parent.getLocationOnScreen(tempLoc);
- int left = x - tempLoc[0];
- int top = y - tempLoc[1];
+ int left = x - parentLoc[0];
+ int top = y - parentLoc[1];
int right = left + width;
int bottom = top + height;
view.layout(left, top, right, bottom);
-
- view.requestLayout();
}
/**
@@ -615,6 +661,11 @@
sharedElementBundle.putString(KEY_NAME, view.getSharedElementName());
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
+
transitionArgs.putBundle(name, sharedElementBundle);
}
@@ -723,6 +774,61 @@
return transition;
}
+ private void handleRejected(final ArrayList<View> rejected) {
+ int numRejected = rejected.size();
+ if (numRejected == 0) {
+ return;
+ }
+ boolean rejectionHandled = mListener.handleRejectedSharedElements(rejected);
+ if (rejectionHandled) {
+ return;
+ }
+
+ ViewGroupOverlay overlay = getDecor().getOverlay();
+ ObjectAnimator animator = null;
+ for (int i = 0; i < numRejected; i++) {
+ View view = rejected.get(i);
+ overlay.add(view);
+ animator = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0);
+ animator.start();
+ }
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ViewGroupOverlay overlay = getDecor().getOverlay();
+ for (int i = rejected.size() - 1; i >= 0; i--) {
+ overlay.remove(rejected.get(i));
+ }
+ }
+ });
+ }
+
+ private void createSharedElementImages(ArrayList<View> accepted, ArrayList<View> rejected,
+ ArrayList<String> sharedElementNames, Bundle state) {
+ int numSharedElements = sharedElementNames.size();
+ Context context = getWindow().getContext();
+ int[] parentLoc = new int[2];
+ getDecor().getLocationOnScreen(parentLoc);
+ for (int i = 0; i < numSharedElements; i++) {
+ String name = sharedElementNames.get(i);
+ Bundle sharedElementBundle = state.getBundle(name);
+ if (sharedElementBundle != null) {
+ Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
+ ImageView imageView = new ImageView(context);
+ imageView.setId(com.android.internal.R.id.shared_element);
+ imageView.setScaleType(ImageView.ScaleType.CENTER);
+ imageView.setImageBitmap(bitmap);
+ imageView.setSharedElementName(name);
+ setSharedElementState(imageView, name, state, parentLoc);
+ if (mTargetSharedNames.contains(name)) {
+ accepted.add(imageView);
+ } else {
+ rejected.add(imageView);
+ }
+ }
+ }
+ }
+
private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
private Rect mEpicenter;
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index edf21dd..a810134 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -147,6 +147,7 @@
if (mSurface != null) {
mActivityContainer.startActivity(intent);
} else {
+ mActivityContainer.checkEmbeddedAllowed(intent);
mQueuedIntent = intent;
mQueuedPendingIntent = null;
}
@@ -162,6 +163,7 @@
if (mSurface != null) {
mActivityContainer.startActivityIntentSender(iIntentSender);
} else {
+ mActivityContainer.checkEmbeddedAllowedIntentSender(iIntentSender);
mQueuedPendingIntent = iIntentSender;
mQueuedIntent = null;
}
@@ -177,6 +179,7 @@
if (mSurface != null) {
mActivityContainer.startActivityIntentSender(iIntentSender);
} else {
+ mActivityContainer.checkEmbeddedAllowedIntentSender(iIntentSender);
mQueuedPendingIntent = iIntentSender;
mQueuedIntent = null;
}
@@ -326,6 +329,24 @@
}
}
+ void checkEmbeddedAllowed(Intent intent) {
+ try {
+ mIActivityContainer.checkEmbeddedAllowed(intent);
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "ActivityView: Unable to startActivity from Intent. " + e);
+ }
+ }
+
+ void checkEmbeddedAllowedIntentSender(IIntentSender intentSender) {
+ try {
+ mIActivityContainer.checkEmbeddedAllowedIntentSender(intentSender);
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "ActivityView: Unable to startActivity from IntentSender. " + e);
+ }
+ }
+
int getDisplayId() {
try {
return mIActivityContainer.getDisplayId();
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index f1c632e..fcc7f8e 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -33,6 +33,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import com.android.internal.app.IVoiceInteractor;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -136,6 +137,8 @@
ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
Configuration curConfig = Configuration.CREATOR.createFromParcel(data);
CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
+ IVoiceInteractor voiceInteractor = IVoiceInteractor.Stub.asInterface(
+ data.readStrongBinder());
int procState = data.readInt();
Bundle state = data.readBundle();
List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
@@ -147,7 +150,8 @@
? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null;
boolean autoStopProfiler = data.readInt() != 0;
Bundle resumeArgs = data.readBundle();
- scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, procState, state,
+ scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo,
+ voiceInteractor, procState, state,
ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler,
resumeArgs);
return true;
@@ -735,6 +739,7 @@
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
+ IVoiceInteractor voiceInteractor,
int procState, Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
@@ -748,6 +753,7 @@
info.writeToParcel(data, 0);
curConfig.writeToParcel(data, 0);
compatInfo.writeToParcel(data, 0);
+ data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
data.writeInt(procState);
data.writeBundle(state);
data.writeTypedList(pendingResults);
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index aa097e0..cbb8359 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -23,8 +23,6 @@
import android.os.Bundle;
import android.os.ResultReceiver;
import android.transition.Transition;
-import android.util.ArrayMap;
-import android.util.Pair;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.Window;
@@ -135,11 +133,6 @@
}
@Override
- protected void onRemoteSceneExitComplete() {
- super.onRemoteSceneExitComplete();
- }
-
- @Override
protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) {
mEnteringSharedElementNames = new ArrayList<String>();
mEnteringSharedElementNames.addAll(sharedElementNames);
@@ -149,6 +142,7 @@
@Override
protected void sharedElementTransitionComplete(Bundle bundle) {
notifySharedElementTransitionComplete(bundle);
+ exitAfterSharedElementTransition();
}
@Override
@@ -223,6 +217,7 @@
@Override
protected void startExitTransition(ArrayList<String> sharedElements) {
+ mMakeOpaque = false;
notifyPrepareRestore();
if (getDecor().getBackground() == null) {
@@ -264,7 +259,6 @@
mExitTransitionComplete = true;
exitAfterSharedElementTransition();
super.onExitTransitionEnd();
- clearConnections();
}
@Override
@@ -281,12 +275,13 @@
}
private void exitAfterSharedElementTransition() {
- if (mSharedElementTransitionComplete && mExitTransitionComplete) {
+ if (mSharedElementTransitionComplete && mExitTransitionComplete && mBackgroundFadedOut) {
mActivity.finish();
if (mSupportsTransition) {
mActivity.overridePendingTransition(0, 0);
}
notifyExitTransitionComplete();
+ clearConnections();
}
}
}
diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl
index cc3b10c..52884f7 100644
--- a/core/java/android/app/IActivityContainer.aidl
+++ b/core/java/android/app/IActivityContainer.aidl
@@ -29,6 +29,8 @@
void setSurface(in Surface surface, int width, int height, int density);
int startActivity(in Intent intent);
int startActivityIntentSender(in IIntentSender intentSender);
+ void checkEmbeddedAllowed(in Intent intent);
+ void checkEmbeddedAllowedIntentSender(in IIntentSender intentSender);
int getDisplayId();
boolean injectEvent(in InputEvent event);
void release();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 52003f1..6b94c4e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -47,6 +47,8 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.StrictMode;
+import android.service.voice.IVoiceInteractionSession;
+import com.android.internal.app.IVoiceInteractor;
import java.util.List;
@@ -77,6 +79,10 @@
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
int flagsMask, int flagsValues, Bundle options) throws RemoteException;
+ public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
+ Intent intent, String resolvedType, IVoiceInteractionSession session,
+ IVoiceInteractor interactor, int flags, String profileFile,
+ ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException;
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent, Bundle options) throws RemoteException;
public boolean finishActivity(IBinder token, int code, Intent data, boolean finishTask)
@@ -733,4 +739,5 @@
int STOP_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+215;
int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216;
int SET_ACTIVITY_LABEL_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
+ int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index ac8ac8f..f290e94 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -31,6 +31,8 @@
import android.os.RemoteException;
import android.os.IBinder;
import android.os.IInterface;
+import android.service.voice.IVoiceInteractionSession;
+import com.android.internal.app.IVoiceInteractor;
import java.io.FileDescriptor;
import java.util.List;
@@ -55,8 +57,9 @@
void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
- int procState, Bundle state, List<ResultInfo> pendingResults,
- List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
+ IVoiceInteractor voiceInteractor, int procState, Bundle state,
+ List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed,
+ boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
Bundle resumeArgs)
throws RemoteException;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 8681f5c..ad4027d 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -18,11 +18,15 @@
package android.app;
import android.app.ITransientNotification;
-import android.service.notification.StatusBarNotification;
import android.app.Notification;
import android.content.ComponentName;
import android.content.Intent;
+import android.net.Uri;
+import android.service.notification.Condition;
+import android.service.notification.IConditionListener;
+import android.service.notification.IConditionProvider;
import android.service.notification.INotificationListener;
+import android.service.notification.StatusBarNotification;
import android.service.notification.ZenModeConfig;
/** {@hide} */
@@ -53,4 +57,7 @@
ZenModeConfig getZenModeConfig();
boolean setZenModeConfig(in ZenModeConfig config);
+ oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
+ oneway void requestZenModeConditions(in IConditionListener callback, boolean requested);
+ oneway void setZenModeCondition(in Uri conditionId);
}
\ No newline at end of file
diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl
index e587912..ecf2c73 100644
--- a/core/java/android/app/IProcessObserver.aidl
+++ b/core/java/android/app/IProcessObserver.aidl
@@ -20,7 +20,7 @@
oneway interface IProcessObserver {
void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities);
- void onImportanceChanged(int pid, int uid, int importance);
+ void onProcessStateChanged(int pid, int uid, int procState);
void onProcessDied(int pid, int uid);
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 028fa68..e58ccb8 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1428,7 +1428,7 @@
}
/**
- * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
+ * Like {@link #execStartActivity},
* but accepts an array of activities to be started. Note that active
* {@link ActivityMonitor} objects only match against the first activity in
* the array.
@@ -1442,7 +1442,7 @@
}
/**
- * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
+ * Like {@link #execStartActivity},
* but accepts an array of activities to be started. Note that active
* {@link ActivityMonitor} objects only match against the first activity in
* the array.
@@ -1545,8 +1545,7 @@
}
/**
- * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
- * but for starting as a particular user.
+ * Like {@link #execStartActivity}, but for starting as a particular user.
*
* @param who The Context from which the activity is being started.
* @param contextThread The main thread of the Context from which the activity
@@ -1616,7 +1615,8 @@
mUiAutomationConnection = uiAutomationConnection;
}
- /*package*/ static void checkStartActivityResult(int res, Object intent) {
+ /** @hide */
+ public static void checkStartActivityResult(int res, Object intent) {
if (res >= ActivityManager.START_SUCCESS) {
return;
}
@@ -1640,6 +1640,9 @@
case ActivityManager.START_NOT_ACTIVITY:
throw new IllegalArgumentException(
"PendingIntent is not an activity");
+ case ActivityManager.START_NOT_VOICE_COMPATIBLE:
+ throw new SecurityException(
+ "Starting under voice control not allowed for: " + intent);
default:
throw new AndroidRuntimeException("Unknown error code "
+ res + " when starting " + intent);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fe629f6..25a1493 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -328,8 +328,8 @@
/**
* Bit to be bitwise-ored into the {@link #flags} field that should be
- * set if you want the sound and/or vibration play each time the
- * notification is sent, even if it has not been canceled before that.
+ * set if you would only like the sound, vibrate and ticker to be played
+ * if the notification was not already showing.
*/
public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 1b838fb..4427ce1 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -19,6 +19,9 @@
import android.content.SharedPreferences;
import android.os.FileUtils;
import android.os.Looper;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
import android.util.Log;
import com.google.android.collect.Maps;
@@ -43,10 +46,7 @@
import java.util.WeakHashMap;
import java.util.concurrent.CountDownLatch;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
-import libcore.io.Libcore;
-import libcore.io.StructStat;
final class SharedPreferencesImpl implements SharedPreferences {
private static final String TAG = "SharedPreferencesImpl";
@@ -110,7 +110,7 @@
Map map = null;
StructStat stat = null;
try {
- stat = Libcore.os.stat(mFile.getPath());
+ stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
@@ -172,7 +172,7 @@
* violation, but we explicitly want this one.
*/
BlockGuard.getThreadPolicy().onReadFromDisk();
- stat = Libcore.os.stat(mFile.getPath());
+ stat = Os.stat(mFile.getPath());
} catch (ErrnoException e) {
return true;
}
@@ -599,7 +599,7 @@
str.close();
ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
try {
- final StructStat stat = Libcore.os.stat(mFile.getPath());
+ final StructStat stat = Os.stat(mFile.getPath());
synchronized (this) {
mStatTimestamp = stat.st_mtime;
mStatSize = stat.st_size;
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
new file mode 100644
index 0000000..6dc48b0
--- /dev/null
+++ b/core/java/android/app/VoiceInteractor.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.IVoiceInteractorCallback;
+import com.android.internal.app.IVoiceInteractorRequest;
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
+
+import java.util.WeakHashMap;
+
+/**
+ * Interface for an {@link Activity} to interact with the user through voice.
+ */
+public class VoiceInteractor {
+ static final String TAG = "VoiceInteractor";
+ static final boolean DEBUG = true;
+
+ final Context mContext;
+ final Activity mActivity;
+ final IVoiceInteractor mInteractor;
+ final HandlerCaller mHandlerCaller;
+ final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() {
+ @Override
+ public void executeMessage(Message msg) {
+ SomeArgs args = (SomeArgs)msg.obj;
+ Request request;
+ switch (msg.what) {
+ case MSG_CONFIRMATION_RESULT:
+ request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
+ if (DEBUG) Log.d(TAG, "onConfirmResult: req="
+ + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
+ + " confirmed=" + msg.arg1 + " result=" + args.arg2);
+ if (request != null) {
+ ((ConfirmationRequest)request).onConfirmationResult(msg.arg1 != 0,
+ (Bundle) args.arg2);
+ request.clear();
+ }
+ break;
+ case MSG_COMMAND_RESULT:
+ request = pullRequest((IVoiceInteractorRequest)args.arg1, msg.arg1 != 0);
+ if (DEBUG) Log.d(TAG, "onCommandResult: req="
+ + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
+ + " result=" + args.arg2);
+ if (request != null) {
+ ((CommandRequest)request).onCommandResult((Bundle) args.arg2);
+ if (msg.arg1 != 0) {
+ request.clear();
+ }
+ }
+ break;
+ case MSG_CANCEL_RESULT:
+ request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
+ if (DEBUG) Log.d(TAG, "onCancelResult: req="
+ + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request);
+ if (request != null) {
+ request.onCancel();
+ request.clear();
+ }
+ break;
+ }
+ }
+ };
+
+ final IVoiceInteractorCallback.Stub mCallback = new IVoiceInteractorCallback.Stub() {
+ @Override
+ public void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed,
+ Bundle result) {
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(
+ MSG_CONFIRMATION_RESULT, confirmed ? 1 : 0, request, result));
+ }
+
+ @Override
+ public void deliverCommandResult(IVoiceInteractorRequest request, boolean complete,
+ Bundle result) {
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(
+ MSG_COMMAND_RESULT, complete ? 1 : 0, request, result));
+ }
+
+ @Override
+ public void deliverCancel(IVoiceInteractorRequest request) throws RemoteException {
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(
+ MSG_CANCEL_RESULT, request));
+ }
+ };
+
+ final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
+
+ static final int MSG_CONFIRMATION_RESULT = 1;
+ static final int MSG_COMMAND_RESULT = 2;
+ static final int MSG_CANCEL_RESULT = 3;
+
+ public static abstract class Request {
+ IVoiceInteractorRequest mRequestInterface;
+ Context mContext;
+ Activity mActivity;
+
+ public Request() {
+ }
+
+ public void cancel() {
+ try {
+ mRequestInterface.cancel();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Voice interactor has died", e);
+ }
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public Activity getActivity() {
+ return mActivity;
+ }
+
+ public void onCancel() {
+ }
+
+ void clear() {
+ mRequestInterface = null;
+ mContext = null;
+ mActivity = null;
+ }
+
+ abstract IVoiceInteractorRequest submit(IVoiceInteractor interactor,
+ String packageName, IVoiceInteractorCallback callback) throws RemoteException;
+ }
+
+ public static class ConfirmationRequest extends Request {
+ final CharSequence mPrompt;
+ final Bundle mExtras;
+
+ /**
+ * Confirms an operation with the user via the trusted system
+ * VoiceInteractionService. This allows an Activity to complete an unsafe operation that
+ * would require the user to touch the screen when voice interaction mode is not enabled.
+ * The result of the confirmation will be returned through an asynchronous call to
+ * either {@link #onConfirmationResult(boolean, android.os.Bundle)} or
+ * {@link #onCancel()}.
+ *
+ * <p>In some cases this may be a simple yes / no confirmation or the confirmation could
+ * include context information about how the action will be completed
+ * (e.g. booking a cab might include details about how long until the cab arrives)
+ * so the user can give a confirmation.
+ * @param prompt Optional confirmation text to read to the user as the action being
+ * confirmed.
+ * @param extras Additional optional information.
+ */
+ public ConfirmationRequest(CharSequence prompt, Bundle extras) {
+ mPrompt = prompt;
+ mExtras = extras;
+ }
+
+ public void onConfirmationResult(boolean confirmed, Bundle result) {
+ }
+
+ IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
+ IVoiceInteractorCallback callback) throws RemoteException {
+ return interactor.startConfirmation(packageName, callback, mPrompt.toString(), mExtras);
+ }
+ }
+
+ public static class CommandRequest extends Request {
+ final String mCommand;
+ final Bundle mArgs;
+
+ /**
+ * Execute a command using the trusted system VoiceInteractionService.
+ * This allows an Activity to request additional information from the user needed to
+ * complete an action (e.g. booking a table might have several possible times that the
+ * user could select from or an app might need the user to agree to a terms of service).
+ * The result of the confirmation will be returned through an asynchronous call to
+ * either {@link #onCommandResult(android.os.Bundle)} or
+ * {@link #onCancel()}.
+ *
+ * <p>The command is a string that describes the generic operation to be performed.
+ * The command will determine how the properties in extras are interpreted and the set of
+ * available commands is expected to grow over time. An example might be
+ * "com.google.voice.commands.REQUEST_NUMBER_BAGS" to request the number of bags as part of
+ * airline check-in. (This is not an actual working example.)
+ *
+ * @param command The desired command to perform.
+ * @param args Additional arguments to control execution of the command.
+ */
+ public CommandRequest(String command, Bundle args) {
+ mCommand = command;
+ mArgs = args;
+ }
+
+ public void onCommandResult(Bundle result) {
+ }
+
+ IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
+ IVoiceInteractorCallback callback) throws RemoteException {
+ return interactor.startConfirmation(packageName, callback, mCommand, mArgs);
+ }
+ }
+
+ VoiceInteractor(Context context, Activity activity, IVoiceInteractor interactor,
+ Looper looper) {
+ mContext = context;
+ mActivity = activity;
+ mInteractor = interactor;
+ mHandlerCaller = new HandlerCaller(context, looper, mHandlerCallerCallback, true);
+ }
+
+ Request pullRequest(IVoiceInteractorRequest request, boolean complete) {
+ synchronized (mActiveRequests) {
+ Request req = mActiveRequests.get(request.asBinder());
+ if (req != null && complete) {
+ mActiveRequests.remove(request.asBinder());
+ }
+ return req;
+ }
+ }
+
+ public boolean submitRequest(Request request) {
+ try {
+ IVoiceInteractorRequest ireq = request.submit(mInteractor,
+ mContext.getOpPackageName(), mCallback);
+ request.mRequestInterface = ireq;
+ request.mContext = mContext;
+ request.mActivity = mActivity;
+ synchronized (mActiveRequests) {
+ mActiveRequests.put(ireq.asBinder(), request);
+ }
+ return true;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Remove voice interactor service died", e);
+ return false;
+ }
+ }
+
+ /**
+ * Queries the supported commands available from the VoiceinteractionService.
+ * The command is a string that describes the generic operation to be performed.
+ * An example might be "com.google.voice.commands.REQUEST_NUMBER_BAGS" to request the number
+ * of bags as part of airline check-in. (This is not an actual working example.)
+ *
+ * @param commands
+ */
+ public boolean[] supportsCommands(String[] commands) {
+ try {
+ boolean[] res = mInteractor.supportsCommands(mContext.getOpPackageName(), commands);
+ if (DEBUG) Log.d(TAG, "supportsCommands: cmds=" + commands + " res=" + res);
+ return res;
+ } catch (RemoteException e) {
+ throw new RuntimeException("Voice interactor has died", e);
+ }
+ }
+}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index e6e0f35..58d707c 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -16,6 +16,7 @@
package android.app;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -44,10 +45,14 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.Log;
import android.view.WindowManagerGlobal;
import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -65,6 +70,11 @@
private float mWallpaperXStep = -1;
private float mWallpaperYStep = -1;
+ /** {@hide} */
+ private static final String PROP_WALLPAPER = "ro.config.wallpaper";
+ /** {@hide} */
+ private static final String PROP_WALLPAPER_COMPONENT = "ro.config.wallpaper_component";
+
/**
* Activity Action: Show settings for choosing wallpaper. Do not use directly to construct
* an intent; instead, use {@link #getCropAndSetWallpaperIntent}.
@@ -269,13 +279,15 @@
}
private Bitmap getCurrentWallpaperLocked(Context context) {
+ if (mService == null) {
+ Log.w(TAG, "WallpaperService not running");
+ return null;
+ }
+
try {
Bundle params = new Bundle();
ParcelFileDescriptor fd = mService.getWallpaper(this, params);
if (fd != null) {
- int width = params.getInt("width", 0);
- int height = params.getInt("height", 0);
-
try {
BitmapFactory.Options options = new BitmapFactory.Options();
return BitmapFactory.decodeFileDescriptor(
@@ -297,28 +309,20 @@
}
private Bitmap getDefaultWallpaperLocked(Context context) {
- try {
- InputStream is = context.getResources().openRawResource(
- com.android.internal.R.drawable.default_wallpaper);
- if (is != null) {
- int width = mService.getWidthHint();
- int height = mService.getHeightHint();
-
+ InputStream is = openDefaultWallpaper(context);
+ if (is != null) {
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ return BitmapFactory.decodeStream(is, null, options);
+ } catch (OutOfMemoryError e) {
+ Log.w(TAG, "Can't decode stream", e);
+ } finally {
try {
- BitmapFactory.Options options = new BitmapFactory.Options();
- return BitmapFactory.decodeStream(is, null, options);
- } catch (OutOfMemoryError e) {
- Log.w(TAG, "Can't decode stream", e);
- } finally {
- try {
- is.close();
- } catch (IOException e) {
- // Ignore
- }
+ is.close();
+ } catch (IOException e) {
+ // Ignore
}
}
- } catch (RemoteException e) {
- // Ignore
}
return null;
}
@@ -403,8 +407,7 @@
horizontalAlignment = Math.max(0, Math.min(1, horizontalAlignment));
verticalAlignment = Math.max(0, Math.min(1, verticalAlignment));
- InputStream is = new BufferedInputStream(
- resources.openRawResource(com.android.internal.R.drawable.default_wallpaper));
+ InputStream is = new BufferedInputStream(openDefaultWallpaper(mContext));
if (is == null) {
Log.e(TAG, "default wallpaper input stream is null");
@@ -429,8 +432,7 @@
}
}
- is = new BufferedInputStream(resources.openRawResource(
- com.android.internal.R.drawable.default_wallpaper));
+ is = new BufferedInputStream(openDefaultWallpaper(mContext));
RectF cropRectF;
@@ -479,8 +481,7 @@
if (crop == null) {
// BitmapRegionDecoder has failed, try to crop in-memory
- is = new BufferedInputStream(resources.openRawResource(
- com.android.internal.R.drawable.default_wallpaper));
+ is = new BufferedInputStream(openDefaultWallpaper(mContext));
Bitmap fullSize = null;
if (is != null) {
BitmapFactory.Options options = new BitmapFactory.Options();
@@ -650,6 +651,10 @@
* not "image/*"
*/
public Intent getCropAndSetWallpaperIntent(Uri imageUri) {
+ if (imageUri == null) {
+ throw new IllegalArgumentException("Image URI must not be null");
+ }
+
if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme())) {
throw new IllegalArgumentException("Image URI must be of the "
+ ContentResolver.SCHEME_CONTENT + " scheme type");
@@ -1009,6 +1014,53 @@
* wallpaper.
*/
public void clear() throws IOException {
- setResource(com.android.internal.R.drawable.default_wallpaper);
+ setStream(openDefaultWallpaper(mContext));
+ }
+
+ /**
+ * Open stream representing the default static image wallpaper.
+ *
+ * @hide
+ */
+ public static InputStream openDefaultWallpaper(Context context) {
+ final String path = SystemProperties.get(PROP_WALLPAPER);
+ if (!TextUtils.isEmpty(path)) {
+ final File file = new File(path);
+ if (file.exists()) {
+ try {
+ return new FileInputStream(file);
+ } catch (IOException e) {
+ // Ignored, fall back to platform default below
+ }
+ }
+ }
+ return context.getResources().openRawResource(
+ com.android.internal.R.drawable.default_wallpaper);
+ }
+
+ /**
+ * Return {@link ComponentName} of the default live wallpaper, or
+ * {@code null} if none is defined.
+ *
+ * @hide
+ */
+ public static ComponentName getDefaultWallpaperComponent(Context context) {
+ String flat = SystemProperties.get(PROP_WALLPAPER_COMPONENT);
+ if (!TextUtils.isEmpty(flat)) {
+ final ComponentName cn = ComponentName.unflattenFromString(flat);
+ if (cn != null) {
+ return cn;
+ }
+ }
+
+ flat = context.getString(com.android.internal.R.string.default_wallpaper_component);
+ if (!TextUtils.isEmpty(flat)) {
+ final ComponentName cn = ComponentName.unflattenFromString(flat);
+ if (cn != null) {
+ return cn;
+ }
+ }
+
+ return null;
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d7170e8..6f68dfb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -33,6 +33,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.service.trust.TrustAgentService;
import android.util.Log;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -171,6 +172,16 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SET_NEW_PASSWORD
= "android.app.action.SET_NEW_PASSWORD";
+ /**
+ * Flag for {@link #forwardMatchingIntents}: the intents will forwarded to the primary user.
+ */
+ public static int FLAG_TO_PRIMARY_USER = 0x0001;
+
+ /**
+ * Flag for {@link #forwardMatchingIntents}: the intents will be forwarded to the managed
+ * profile.
+ */
+ public static int FLAG_TO_MANAGED_PROFILE = 0x0002;
/**
* Return true if the given administrator component is currently
@@ -1267,7 +1278,7 @@
public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
/**
- * Disable all keyguard widgets
+ * Disable all keyguard widgets. Has no effect.
*/
public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1 << 0;
@@ -1277,6 +1288,22 @@
public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 1 << 1;
/**
+ * Disable showing all notifications on secure keyguard screens (e.g. PIN/Pattern/Password)
+ */
+ public static final int KEYGUARD_DISABLE_SECURE_NOTIFICATIONS = 1 << 2;
+
+ /**
+ * Only allow redacted notifications on secure keyguard screens (e.g. PIN/Pattern/Password)
+ */
+ public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 1 << 3;
+
+ /**
+ * Ignore {@link TrustAgentService} state on secure keyguard screens
+ * (e.g. PIN/Pattern/Password).
+ */
+ public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 1 << 4;
+
+ /**
* Disable all current and future keyguard customizations.
*/
public static final int KEYGUARD_DISABLE_FEATURES_ALL = 0x7fffffff;
@@ -1496,7 +1523,8 @@
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param which {@link #KEYGUARD_DISABLE_FEATURES_NONE} (default),
* {@link #KEYGUARD_DISABLE_WIDGETS_ALL}, {@link #KEYGUARD_DISABLE_SECURE_CAMERA},
- * {@link #KEYGUARD_DISABLE_FEATURES_ALL}
+ * {@link #KEYGUARD_DISABLE_SECURE_NOTIFICATIONS}, {@link #KEYGUARD_DISABLE_TRUST_AGENTS},
+ * {@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS}, {@link #KEYGUARD_DISABLE_FEATURES_ALL}
*/
public void setKeyguardDisabledFeatures(ComponentName admin, int which) {
if (mService != null) {
@@ -1760,7 +1788,7 @@
* Sets the enabled state of the profile. A profile should be enabled only once it is ready to
* be used. Only the profile owner can call this.
*
- * @see #isPRofileOwnerApp
+ * @see #isProfileOwnerApp
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
*/
@@ -1816,27 +1844,6 @@
/**
* @hide
- * @param userId the userId of a managed profile profile.
- *
- * @return whether or not the managed profile is enabled.
- * @throws IllegalArgumentException if the userId is invalid.
- */
- public boolean isProfileEnabled(int userId) throws IllegalArgumentException {
- if (mService != null) {
- try {
- return mService.isProfileEnabled(userId);
- } catch (RemoteException re) {
- Log.w(TAG, "Failed to get status for owner profile.");
- throw new IllegalArgumentException(
- "Failed to get status for owner profile.", re);
- }
- }
- return true;
- }
-
-
- /**
- * @hide
* @return the human readable name of the organisation associated with this DPM or null if
* one is not set.
* @throws IllegalArgumentException if the userId is invalid.
@@ -1935,6 +1942,39 @@
}
/**
+ * Called by a profile owner to forward intents sent from the managed profile to the owner, or
+ * from the owner to the managed profile.
+ * If an intent matches this intent filter, then activities belonging to the other user can
+ * respond to this intent.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param filter if an intent matches this IntentFilter, then it can be forwarded.
+ */
+ public void forwardMatchingIntents(ComponentName admin, IntentFilter filter, int flags) {
+ if (mService != null) {
+ try {
+ mService.forwardMatchingIntents(admin, filter, flags);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Called by a profile owner to remove all the forwarding intent filters from the current user
+ * and from the owner.
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ */
+ public void clearForwardingIntentFilters(ComponentName admin) {
+ if (mService != null) {
+ try {
+ mService.clearForwardingIntentFilters(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
* Called by a profile or device owner to get the application restrictions for a given target
* application running in the managed profile.
*
@@ -1957,4 +1997,48 @@
}
return null;
}
+
+ /**
+ * Called by a profile or device owner to set a user restriction specified
+ * by the key.
+ * <p>
+ * The calling device admin must be a profile or device owner; if it is not,
+ * a security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated
+ * with.
+ * @param key The key of the restriction. See the constants in
+ * {@link android.os.UserManager} for the list of keys.
+ */
+ public void addUserRestriction(ComponentName admin, String key) {
+ if (mService != null) {
+ try {
+ mService.setUserRestriction(admin, key, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Called by a profile or device owner to clear a user restriction specified
+ * by the key.
+ * <p>
+ * The calling device admin must be a profile or device owner; if it is not,
+ * a security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated
+ * with.
+ * @param key The key of the restriction. See the constants in
+ * {@link android.os.UserManager} for the list of keys.
+ */
+ public void clearUserRestriction(ComponentName admin, String key) {
+ if (mService != null) {
+ try {
+ mService.setUserRestriction(admin, key, false);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 85ba58b..495a5f9 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -109,7 +109,6 @@
String getProfileOwner(int userHandle);
String getProfileOwnerName(int userHandle);
void setProfileEnabled(in ComponentName who);
- boolean isProfileEnabled(int userHandle);
boolean installCaCert(in byte[] certBuffer);
void uninstallCaCert(in byte[] certBuffer);
@@ -119,4 +118,8 @@
void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings);
Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
+
+ void setUserRestriction(in ComponentName who, in String key, boolean enable);
+ void forwardMatchingIntents(in ComponentName admin, in IntentFilter filter, int flags);
+ void clearForwardingIntentFilters(in ComponentName admin);
}
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 3c31f8d..886f1a6 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -29,6 +29,10 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStat;
import android.util.Log;
import java.io.File;
@@ -38,11 +42,6 @@
import java.util.LinkedList;
import java.util.concurrent.CountDownLatch;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-import libcore.io.OsConstants;
-import libcore.io.StructStat;
-
/**
* Provides the central interface between an
* application and Android's data backup infrastructure. An application that wishes
@@ -195,7 +194,7 @@
* the key supplied as part of the entity. Writing an entity with a negative
* data size instructs the transport to delete whatever entity currently exists
* under that key from the remote data set.
- *
+ *
* @param oldState An open, read-only ParcelFileDescriptor pointing to the
* last backup state provided by the application. May be
* <code>null</code>, in which case no prior state is being
@@ -226,7 +225,7 @@
* onRestore() throws an exception, the OS will assume that the
* application's data may now be in an incoherent state, and will clear it
* before proceeding.
- *
+ *
* @param data A structured wrapper around an open, read-only
* file descriptor pointing to a full snapshot of the
* application's data. The application should consume every
@@ -416,7 +415,7 @@
}
// If it's a directory, enqueue its contents for scanning.
- StructStat stat = Libcore.os.lstat(filePath);
+ StructStat stat = Os.lstat(filePath);
if (OsConstants.S_ISLNK(stat.st_mode)) {
if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file);
continue;
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index cfd0a65..6ebb6c4 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -17,6 +17,8 @@
package android.app.backup;
import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
import android.util.Log;
import java.io.File;
@@ -24,9 +26,6 @@
import java.io.FileOutputStream;
import java.io.IOException;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-
/**
* Global constant definitions et cetera related to the full-backup-to-fd
* binary format. Nothing in this namespace is part of any API; it's all
@@ -147,7 +146,7 @@
try {
// explicitly prevent emplacement of files accessible by outside apps
mode &= 0700;
- Libcore.os.chmod(outFile.getPath(), (int)mode);
+ Os.chmod(outFile.getPath(), (int)mode);
} catch (ErrnoException e) {
e.rethrowAsIOException();
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cbb6cf5..de223a3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2442,6 +2442,14 @@
public static final String APPWIDGET_SERVICE = "appwidget";
/**
+ * Official published name of the (internal) voice interaction manager service.
+ *
+ * @hide
+ * @see #getSystemService
+ */
+ public static final String VOICE_INTERACTION_MANAGER_SERVICE = "voiceinteraction";
+
+ /**
* Use with {@link #getSystemService} to retrieve an
* {@link android.app.backup.IBackupManager IBackupManager} for communicating
* with the backup mechanism.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c0f04af..ae5437b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2804,6 +2804,14 @@
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE";
/**
+ * Categories for activities that can participate in voice interaction.
+ * An activity that supports this category must be prepared to run with
+ * no UI shown at all (though in some case it may have a UI shown), and
+ * rely on {@link android.app.VoiceInteractor} to interact with the user.
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_VOICE = "android.intent.category.VOICE";
+ /**
* Set if the activity should be considered as an alternative action to
* the data the user is currently viewing. See also
* {@link #CATEGORY_SELECTED_ALTERNATIVE} for an alternative action that
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 796b113..0acf043 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -35,4 +35,6 @@
ResolveInfo resolveActivity(in Intent intent, in UserHandle user);
void startActivityAsUser(in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
+ boolean isPackageEnabled(String packageName, in UserHandle user);
+ boolean isActivityEnabled(in ComponentName component, in UserHandle user);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ae0899f..cf9a296 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -74,6 +74,9 @@
ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);
+ boolean activitySupportsIntent(in ComponentName className, in Intent intent,
+ String resolvedType);
+
ActivityInfo getReceiverInfo(in ComponentName className, int flags, int userId);
ServiceInfo getServiceInfo(in ComponentName className, int flags, int userId);
@@ -108,6 +111,8 @@
ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId);
+ boolean canForwardTo(in Intent intent, String resolvedType, int userIdFrom, int userIdDest);
+
List<ResolveInfo> queryIntentActivities(in Intent intent,
String resolvedType, int flags, int userId);
@@ -242,6 +247,10 @@
void clearPackagePersistentPreferredActivities(String packageName, int userId);
+ void addForwardingIntentFilter(in IntentFilter filter, int userIdOrig, int userIdDest);
+
+ void clearForwardingIntentFilters(int userIdOrig);
+
/**
* Report the set of 'Home' activity candidates, plus (if any) which of them
* is the current "always use this one" setting.
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 92b9146..9087338 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -19,6 +19,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Bitmap.Config;
@@ -37,6 +39,8 @@
*/
public class LauncherActivityInfo {
private static final boolean DEBUG = false;
+ private static final String TAG = "LauncherActivityInfo";
+
private final PackageManager mPm;
private final UserManager mUm;
@@ -121,25 +125,42 @@
}
/**
+ * Returns the name for the acitivty from android:name in the manifest.
+ * @return the name from android:name for the acitivity.
+ */
+ public String getName() {
+ return mActivityInfo.name;
+ }
+
+ /**
* Returns the activity icon with badging appropriate for the profile.
* @param density Optional density for the icon, or 0 to use the default density.
* @return A badged icon for the activity.
*/
public Drawable getBadgedIcon(int density) {
- // TODO: Handle density
- if (mUser.equals(android.os.Process.myUserHandle())) {
- return mActivityInfo.loadIcon(mPm);
- }
- Drawable originalIcon = mActivityInfo.loadIcon(mPm);
- if (originalIcon == null) {
- if (DEBUG) {
- Log.w("LauncherActivityInfo", "Couldn't find icon for activity");
+ int iconRes = mActivityInfo.getIconResource();
+ Resources resources = null;
+ Drawable originalIcon = null;
+ try {
+ resources = mPm.getResourcesForApplication(mActivityInfo.applicationInfo);
+ try {
+ if (density != 0) {
+ originalIcon = resources.getDrawableForDensity(iconRes, density);
+ }
+ } catch (Resources.NotFoundException e) {
}
- originalIcon = mPm.getDefaultActivityIcon();
+ } catch (NameNotFoundException nnfe) {
}
+
+ if (originalIcon == null) {
+ originalIcon = mActivityInfo.loadIcon(mPm);
+ }
+
if (originalIcon instanceof BitmapDrawable) {
return mUm.getBadgedDrawableForUser(
originalIcon, mUser);
+ } else {
+ Log.e(TAG, "Unable to create badged icon for " + mActivityInfo);
}
return originalIcon;
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 5187181..8025b60 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -187,6 +187,39 @@
}
/**
+ * Checks if the package is installed and enabled for a profile.
+ *
+ * @param packageName The package to check.
+ * @param user The UserHandle of the profile.
+ *
+ * @return true if the package exists and is enabled.
+ */
+ public boolean isPackageEnabledForProfile(String packageName, UserHandle user) {
+ try {
+ return mService.isPackageEnabled(packageName, user);
+ } catch (RemoteException re) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the activity exists and it enabled for a profile.
+ *
+ * @param component The activity to check.
+ * @param user The UserHandle of the profile.
+ *
+ * @return true if the activity exists and is enabled.
+ */
+ public boolean isActivityEnabledForProfile(ComponentName component, UserHandle user) {
+ try {
+ return mService.isActivityEnabled(component, user);
+ } catch (RemoteException re) {
+ return false;
+ }
+ }
+
+
+ /**
* Adds a listener for changes to packages in current and managed profiles.
*
* @param listener The listener to add.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d981cc1..484a2a1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1235,7 +1235,6 @@
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper";
-
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports app widgets.
@@ -1244,6 +1243,17 @@
public static final String FEATURE_APP_WIDGETS = "android.software.app_widgets";
/**
+ * @hide
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports
+ * {@link android.service.voice.VoiceInteractionService} and
+ * {@link android.app.VoiceInteractor}.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
+
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports a home screen that is replaceable
* by third party applications.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8f19f01..080b37b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -459,7 +459,7 @@
return pi;
}
- private Certificate[] loadCertificates(StrictJarFile jarFile, ZipEntry je,
+ private Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry je,
byte[] readBuffer) {
try {
// We must read the stream for the JarEntry to retrieve
@@ -469,7 +469,7 @@
// not using
}
is.close();
- return je != null ? jarFile.getCertificates(je) : null;
+ return je != null ? jarFile.getCertificateChains(je) : null;
} catch (IOException e) {
Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e);
} catch (RuntimeException e) {
@@ -632,7 +632,7 @@
try {
StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath);
- Certificate[] certs = null;
+ Certificate[][] certs = null;
if ((flags&PARSE_IS_SYSTEM) != 0) {
// If this package comes from the system image, then we
@@ -656,8 +656,8 @@
final int N = certs.length;
for (int i=0; i<N; i++) {
Slog.i(TAG, " Public key: "
- + certs[i].getPublicKey().getEncoded()
- + " " + certs[i].getPublicKey());
+ + certs[i][0].getPublicKey().getEncoded()
+ + " " + certs[i][0].getPublicKey());
}
}
}
@@ -677,7 +677,7 @@
ManifestDigest.fromInputStream(jarFile.getInputStream(je));
}
- final Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer);
+ final Certificate[][] localCerts = loadCertificates(jarFile, je, readBuffer);
if (DEBUG_JAR) {
Slog.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName()
+ ": certs=" + certs + " ("
@@ -726,8 +726,7 @@
final int N = certs.length;
pkg.mSignatures = new Signature[certs.length];
for (int i=0; i<N; i++) {
- pkg.mSignatures[i] = new Signature(
- certs[i].getEncoded());
+ pkg.mSignatures[i] = new Signature(certs[i]);
}
} else {
Slog.e(TAG, "Package " + pkg.packageName
@@ -739,7 +738,7 @@
// Add the signing KeySet to the system
pkg.mSigningKeys = new HashSet<PublicKey>();
for (int i=0; i < certs.length; i++) {
- pkg.mSigningKeys.add(certs[i].getPublicKey());
+ pkg.mSigningKeys.add(certs[i][0].getPublicKey());
}
} catch (CertificateEncodingException e) {
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index 752bf8b..f4e7dc3 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -25,6 +25,7 @@
import java.lang.ref.SoftReference;
import java.security.PublicKey;
import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
@@ -38,12 +39,28 @@
private int mHashCode;
private boolean mHaveHashCode;
private SoftReference<String> mStringRef;
+ private Certificate[] mCertificateChain;
/**
* Create Signature from an existing raw byte array.
*/
public Signature(byte[] signature) {
mSignature = signature.clone();
+ mCertificateChain = null;
+ }
+
+ /**
+ * Create signature from a certificate chain. Used for backward
+ * compatibility.
+ *
+ * @throws CertificateEncodingException
+ * @hide
+ */
+ public Signature(Certificate[] certificateChain) throws CertificateEncodingException {
+ mSignature = certificateChain[0].getEncoded();
+ if (certificateChain.length > 1) {
+ mCertificateChain = Arrays.copyOfRange(certificateChain, 1, certificateChain.length);
+ }
}
private static final int parseHexDigit(int nibble) {
@@ -156,6 +173,29 @@
return cert.getPublicKey();
}
+ /**
+ * Used for compatibility code that needs to check the certificate chain
+ * during upgrades.
+ *
+ * @throws CertificateEncodingException
+ * @hide
+ */
+ public Signature[] getChainSignatures() throws CertificateEncodingException {
+ if (mCertificateChain == null) {
+ return new Signature[] { this };
+ }
+
+ Signature[] chain = new Signature[1 + mCertificateChain.length];
+ chain[0] = this;
+
+ int i = 1;
+ for (Certificate c : mCertificateChain) {
+ chain[i++] = new Signature(c.getEncoded());
+ }
+
+ return chain;
+ }
+
@Override
public boolean equals(Object obj) {
try {
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index f53aa4c..c0383a3 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -27,8 +27,8 @@
*/
public class UserInfo implements Parcelable {
- /** 6 bits for user type */
- public static final int FLAG_MASK_USER_TYPE = 0x0000003F;
+ /** 8 bits for user type */
+ public static final int FLAG_MASK_USER_TYPE = 0x000000FF;
/**
* *************************** NOTE ***************************
@@ -70,6 +70,11 @@
*/
public static final int FLAG_MANAGED_PROFILE = 0x00000020;
+ /**
+ * Indicates that this user is disabled.
+ */
+ public static final int FLAG_DISABLED = 0x00000040;
+
public static final int NO_PROFILE_GROUP_ID = -1;
@@ -117,6 +122,10 @@
return (flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE;
}
+ public boolean isEnabled() {
+ return (flags & FLAG_DISABLED) != FLAG_DISABLED;
+ }
+
/**
* @return true if this user can be switched to.
**/
diff --git a/core/java/android/ddm/DdmHandleHeap.java b/core/java/android/ddm/DdmHandleHeap.java
index cece556..e24aeb2 100644
--- a/core/java/android/ddm/DdmHandleHeap.java
+++ b/core/java/android/ddm/DdmHandleHeap.java
@@ -219,7 +219,7 @@
if (false)
Log.d("ddm-heap", "Heap GC request");
- System.gc();
+ Runtime.getRuntime().gc();
return null; // empty response
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 528e119..722d956 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -455,22 +455,25 @@
* <p>The maximum numbers of different types of output streams
* that can be configured and used simultaneously by a camera device.</p>
* <p>This is a 3 element tuple that contains the max number of output simultaneous
- * streams for raw sensor, processed (and uncompressed), and JPEG formats respectively.
- * For example, if max raw sensor format output stream number is 1, max YUV streams
+ * streams for raw sensor, processed (but not stalling), and processed (and stalling)
+ * formats respectively. For example, assuming that JPEG is typically a processed and
+ * stalling stream, if max raw sensor format output stream number is 1, max YUV streams
* number is 3, and max JPEG stream number is 2, then this tuple should be <code>(1, 3, 2)</code>.</p>
* <p>This lists the upper bound of the number of output streams supported by
* the camera device. Using more streams simultaneously may require more hardware and
* CPU resources that will consume more power. The image format for a output stream can
- * be any supported format provided by {@link CameraCharacteristics#SCALER_AVAILABLE_FORMATS android.scaler.availableFormats}. The formats
- * defined in {@link CameraCharacteristics#SCALER_AVAILABLE_FORMATS android.scaler.availableFormats} can be catergorized into the 3 stream types
- * as below:</p>
+ * be any supported format provided by {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS android.scaler.availableStreamConfigurations}.
+ * The formats defined in {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS android.scaler.availableStreamConfigurations} can be catergorized
+ * into the 3 stream types as below:</p>
* <ul>
- * <li>JPEG-compressed format: BLOB.</li>
- * <li>Raw formats: RAW_SENSOR and RAW_OPAQUE.</li>
- * <li>processed, uncompressed formats: YCbCr_420_888, YCrCb_420_SP, YV12.</li>
+ * <li>Processed (but stalling): any non-RAW format with a stallDurations > 0.
+ * Typically JPEG format (ImageFormat#JPEG).</li>
+ * <li>Raw formats: ImageFormat#RAW_SENSOR and ImageFormat#RAW_OPAQUE.</li>
+ * <li>Processed (but not-stalling): any non-RAW format without a stall duration.
+ * Typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12.</li>
* </ul>
*
- * @see CameraCharacteristics#SCALER_AVAILABLE_FORMATS
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS
*/
public static final Key<int[]> REQUEST_MAX_NUM_OUTPUT_STREAMS =
new Key<int[]>("android.request.maxNumOutputStreams", int[].class);
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index ee2adac..40a7905 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -157,7 +157,14 @@
mCameraId = cameraId;
mDeviceListener = listener;
mDeviceHandler = handler;
- TAG = String.format("CameraDevice-%s-JV", mCameraId);
+
+ final int MAX_TAG_LEN = 23;
+ String tag = String.format("CameraDevice-JV-%s", mCameraId);
+ if (tag.length() > MAX_TAG_LEN) {
+ tag = tag.substring(0, MAX_TAG_LEN);
+ }
+ TAG = tag;
+
DEBUG = Log.isLoggable(TAG, Log.DEBUG);
}
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
index 21de9f5..4c074e9 100644
--- a/core/java/android/hardware/location/GeofenceHardware.java
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -79,7 +79,7 @@
*/
public static final int MONITOR_UNSUPPORTED = 2;
- // The following constants need to match geofence flags in gps.h
+ // The following constants need to match geofence flags in gps.h and fused_location.h
/**
* The constant to indicate that the user has entered the geofence.
*/
@@ -92,7 +92,7 @@
/**
* The constant to indicate that the user is uncertain with respect to a
- * geofence. nn
+ * geofence.
*/
public static final int GEOFENCE_UNCERTAIN = 1<<2L;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index c51d1a7..505ef9c 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -255,7 +255,9 @@
public static final int CURSOR_ANCHOR_MONITOR_MODE_NONE = 0x0;
/**
- * The IME expects that {@link #onUpdateCursor(Rect)} is called back.
+ * Passing this flag into a call to {@link #setCursorAnchorMonitorMode(int)} will result in
+ * the cursor rectangle being provided in screen coordinates to subsequent
+ * {@link #onUpdateCursor(Rect)} callbacks.
*/
public static final int CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT = 0x1;
@@ -1703,9 +1705,11 @@
}
/**
- * Called when the application has reported a new location of its text
- * cursor. This is only called if explicitly requested by the input method.
- * The default implementation does nothing.
+ * Called when the application has reported a new location of its text cursor. This is only
+ * called if explicitly requested by the input method. The default implementation does nothing.
+ * @param newCursor The new cursor position, in screen coordinates if the input method calls
+ * {@link #setCursorAnchorMonitorMode} with {@link #CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT}.
+ * Otherwise, this is in local coordinates.
*/
public void onUpdateCursor(Rect newCursor) {
// Intentionally empty
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 22543e3..a725bec 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -24,13 +24,13 @@
import java.net.InterfaceAddress;
import java.net.UnknownHostException;
-import static libcore.io.OsConstants.IFA_F_DADFAILED;
-import static libcore.io.OsConstants.IFA_F_DEPRECATED;
-import static libcore.io.OsConstants.IFA_F_TENTATIVE;
-import static libcore.io.OsConstants.RT_SCOPE_HOST;
-import static libcore.io.OsConstants.RT_SCOPE_LINK;
-import static libcore.io.OsConstants.RT_SCOPE_SITE;
-import static libcore.io.OsConstants.RT_SCOPE_UNIVERSE;
+import static android.system.OsConstants.IFA_F_DADFAILED;
+import static android.system.OsConstants.IFA_F_DEPRECATED;
+import static android.system.OsConstants.IFA_F_TENTATIVE;
+import static android.system.OsConstants.RT_SCOPE_HOST;
+import static android.system.OsConstants.RT_SCOPE_LINK;
+import static android.system.OsConstants.RT_SCOPE_SITE;
+import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
/**
* Identifies an IP address on a network link.
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index 119e533..643e8c2 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -22,9 +22,9 @@
import java.io.FileDescriptor;
import java.net.SocketOptions;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-import libcore.io.OsConstants;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
/**
* Socket implementation used for android.net.LocalSocket and
@@ -248,7 +248,7 @@
throw new IllegalStateException("unknown sockType");
}
try {
- fd = Libcore.os.socket(OsConstants.AF_UNIX, osType, 0);
+ fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
mFdCreatedInternally = true;
} catch (ErrnoException e) {
e.rethrowAsIOException();
@@ -268,7 +268,7 @@
return;
}
try {
- Libcore.os.close(fd);
+ Os.close(fd);
} catch (ErrnoException e) {
e.rethrowAsIOException();
}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index a470e88..30b61c5 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -54,7 +54,7 @@
public class MobileDataStateTracker extends BaseNetworkStateTracker {
private static final String TAG = "MobileDataStateTracker";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
private PhoneConstants.DataState mMobileDataState;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f05ddde..9e9820f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -173,6 +173,8 @@
private static final String BLUETOOTH_STATE_COUNT_DATA = "bsc";
private static final String POWER_USE_SUMMARY_DATA = "pws";
private static final String POWER_USE_ITEM_DATA = "pwi";
+ private static final String DISCHARGE_STEP_DATA = "dsd";
+ private static final String CHARGE_STEP_DATA = "csd";
private final StringBuilder mFormatBuilder = new StringBuilder(32);
private final Formatter mFormatter = new Formatter(mFormatBuilder);
@@ -1340,6 +1342,18 @@
public abstract long computeBatteryTimeRemaining(long curTime);
/**
+ * Return the historical number of discharge steps we currently have.
+ */
+ public abstract int getNumDischargeStepDurations();
+
+ /**
+ * Return the array of discharge step durations; the number of valid
+ * items in it is returned by {@link #getNumDischargeStepDurations()}.
+ * These values are in milliseconds.
+ */
+ public abstract long[] getDischargeStepDurationsArray();
+
+ /**
* Compute an approximation for how much time (in microseconds) remains until the battery
* is fully charged. Returns -1 if no time can be computed: either there is not
* enough current data to make a decision, or the battery is currently
@@ -1349,6 +1363,18 @@
*/
public abstract long computeChargeTimeRemaining(long curTime);
+ /**
+ * Return the historical number of charge steps we currently have.
+ */
+ public abstract int getNumChargeStepDurations();
+
+ /**
+ * Return the array of charge step durations; the number of valid
+ * items in it is returned by {@link #getNumChargeStepDurations()}.
+ * These values are in milliseconds.
+ */
+ public abstract long[] getChargeStepDurationsArray();
+
public abstract Map<String, ? extends LongCounter> getWakeupReasonStats();
public abstract Map<String, ? extends Timer> getKernelWakelockStats();
@@ -3120,6 +3146,28 @@
pw.print(suffix);
}
+ private static boolean dumpDurationSteps(PrintWriter pw, String header, long[] steps,
+ int count, boolean checkin) {
+ if (count <= 0) {
+ return false;
+ }
+ if (!checkin) {
+ pw.println(header);
+ }
+ String[] lineArgs = new String[1];
+ for (int i=0; i<count; i++) {
+ if (checkin) {
+ lineArgs[0] = Long.toString(steps[i]);
+ dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs);
+ } else {
+ pw.print(" #"); pw.print(i); pw.print(": ");
+ TimeUtils.formatDuration(steps[i], pw);
+ pw.println();
+ }
+ }
+ return true;
+ }
+
public static final int DUMP_UNPLUGGED_ONLY = 1<<0;
public static final int DUMP_CHARGED_ONLY = 1<<1;
public static final int DUMP_HISTORY_ONLY = 1<<2;
@@ -3239,7 +3287,27 @@
}
}
if (didPid) {
- pw.println("");
+ pw.println();
+ }
+ if (dumpDurationSteps(pw, "Discharge step durations:", getDischargeStepDurationsArray(),
+ getNumDischargeStepDurations(), false)) {
+ long timeRemaining = computeBatteryTimeRemaining(SystemClock.elapsedRealtime());
+ if (timeRemaining >= 0) {
+ pw.print(" Estimated discharge time remaining: ");
+ TimeUtils.formatDuration(timeRemaining / 1000, pw);
+ pw.println();
+ }
+ pw.println();
+ }
+ if (dumpDurationSteps(pw, "Charge step durations:", getChargeStepDurationsArray(),
+ getNumChargeStepDurations(), false)) {
+ long timeRemaining = computeChargeTimeRemaining(SystemClock.elapsedRealtime());
+ if (timeRemaining >= 0) {
+ pw.print(" Estimated charge time remaining: ");
+ TimeUtils.formatDuration(timeRemaining / 1000, pw);
+ pw.println();
+ }
+ pw.println();
}
}
@@ -3248,7 +3316,7 @@
pw.println(" System starts: " + getStartCount()
+ ", currently on battery: " + getIsOnBattery());
dumpLocked(context, pw, "", STATS_SINCE_CHARGED, reqUid);
- pw.println("");
+ pw.println();
}
if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
pw.println("Statistics since last unplugged:");
@@ -3352,6 +3420,12 @@
}
}
}
+ if (!filtering) {
+ dumpDurationSteps(pw, DISCHARGE_STEP_DATA, getDischargeStepDurationsArray(),
+ getNumDischargeStepDurations(), true);
+ dumpDurationSteps(pw, CHARGE_STEP_DATA, getChargeStepDurationsArray(),
+ getNumChargeStepDurations(), true);
+ }
if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
dumpCheckinLocked(context, pw, STATS_SINCE_CHARGED, -1);
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 11e3795..1ca6b90 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -16,12 +16,17 @@
package android.os;
+import android.text.TextUtils;
+import android.util.Slog;
+
import com.android.internal.telephony.TelephonyProperties;
/**
* Information about the current build, extracted from system properties.
*/
public class Build {
+ private static final String TAG = "Build";
+
/** Value used for when a build property is unknown. */
public static final String UNKNOWN = "unknown";
@@ -76,12 +81,38 @@
public static final String SERIAL = getString("ro.serialno");
/**
- * A list of ABIs (in priority) order supported by this device.
+ * An ordered list of ABIs supported by this device. The most preferred ABI is the first
+ * element in the list.
+ *
+ * See {@link #SUPPORTED_32_BIT_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}.
*
* @hide
*/
public static final String[] SUPPORTED_ABIS = getString("ro.product.cpu.abilist").split(",");
+ /**
+ * An ordered list of <b>32 bit</b> ABIs supported by this device. The most preferred ABI
+ * is the first element in the list.
+ *
+ * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}.
+ *
+ * @hide
+ */
+ public static final String[] SUPPORTED_32_BIT_ABIS = getString("ro.product.cpu.abilist32")
+ .split(",");
+
+ /**
+ * An ordered list of <b>64 bit</b> ABIs supported by this device. The most preferred ABI
+ * is the first element in the list.
+ *
+ * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_32_BIT_ABIS}.
+ *
+ * @hide
+ */
+ public static final String[] SUPPORTED_64_BIT_ABIS = getString("ro.product.cpu.abilist64")
+ .split(",");
+
+
/** Various version strings. */
public static class VERSION {
/**
@@ -509,7 +540,43 @@
public static final String TAGS = getString("ro.build.tags");
/** A string that uniquely identifies this build. Do not attempt to parse this value. */
- public static final String FINGERPRINT = getString("ro.build.fingerprint");
+ public static final String FINGERPRINT = deriveFingerprint();
+
+ /**
+ * Some devices split the fingerprint components between multiple
+ * partitions, so we might derive the fingerprint at runtime.
+ */
+ private static String deriveFingerprint() {
+ String finger = SystemProperties.get("ro.build.fingerprint");
+ if (TextUtils.isEmpty(finger)) {
+ finger = getString("ro.product.brand") + '/' +
+ getString("ro.product.name") + '/' +
+ getString("ro.product.device") + ':' +
+ getString("ro.build.version.release") + '/' +
+ getString("ro.build.id") + '/' +
+ getString("ro.build.version.incremental") + ':' +
+ getString("ro.build.type") + '/' +
+ getString("ro.build.tags");
+ }
+ return finger;
+ }
+
+ /**
+ * Ensure that raw fingerprint system property is defined. If it was derived
+ * dynamically by {@link #deriveFingerprint()} this is where we push the
+ * derived value into the property service.
+ *
+ * @hide
+ */
+ public static void ensureFingerprintProperty() {
+ if (TextUtils.isEmpty(SystemProperties.get("ro.build.fingerprint"))) {
+ try {
+ SystemProperties.set("ro.build.fingerprint", FINGERPRINT);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Failed to set fingerprint property", e);
+ }
+ }
+ }
// The following properties only make sense for internal engineering builds.
public static final long TIME = getLong("ro.build.date.utc") * 1000;
diff --git a/core/java/android/os/CommonClock.java b/core/java/android/os/CommonClock.java
index 2ecf317..f83a90b 100644
--- a/core/java/android/os/CommonClock.java
+++ b/core/java/android/os/CommonClock.java
@@ -23,6 +23,7 @@
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
+import static android.system.OsConstants.*;
/**
* Used for accessing the android common time service's common clock and receiving notifications
diff --git a/core/java/android/os/CommonTimeUtils.java b/core/java/android/os/CommonTimeUtils.java
index 20755d9..ba060b8 100644
--- a/core/java/android/os/CommonTimeUtils.java
+++ b/core/java/android/os/CommonTimeUtils.java
@@ -20,7 +20,7 @@
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.util.Locale;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
class CommonTimeUtils {
/**
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 1089f27..d71c3e6 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,12 +16,12 @@
package android.os;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.Log;
import android.util.Slog;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -85,7 +85,7 @@
*/
public static int setPermissions(String path, int mode, int uid, int gid) {
try {
- Libcore.os.chmod(path, mode);
+ Os.chmod(path, mode);
} catch (ErrnoException e) {
Slog.w(TAG, "Failed to chmod(" + path + "): " + e);
return e.errno;
@@ -93,7 +93,7 @@
if (uid >= 0 || gid >= 0) {
try {
- Libcore.os.chown(path, uid, gid);
+ Os.chown(path, uid, gid);
} catch (ErrnoException e) {
Slog.w(TAG, "Failed to chown(" + path + "): " + e);
return e.errno;
@@ -113,7 +113,7 @@
*/
public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) {
try {
- Libcore.os.fchmod(fd, mode);
+ Os.fchmod(fd, mode);
} catch (ErrnoException e) {
Slog.w(TAG, "Failed to fchmod(): " + e);
return e.errno;
@@ -121,7 +121,7 @@
if (uid >= 0 || gid >= 0) {
try {
- Libcore.os.fchown(fd, uid, gid);
+ Os.fchown(fd, uid, gid);
} catch (ErrnoException e) {
Slog.w(TAG, "Failed to fchown(): " + e);
return e.errno;
@@ -136,7 +136,7 @@
*/
public static int getUid(String path) {
try {
- return Libcore.os.stat(path).st_uid;
+ return Os.stat(path).st_uid;
} catch (ErrnoException e) {
return -1;
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index c3f7370..899a958 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -29,6 +29,7 @@
interface IUserManager {
UserInfo createUser(in String name, int flags);
UserInfo createProfileForUser(in String name, int flags, int userHandle);
+ void setUserEnabled(int userHandle);
boolean removeUser(int userHandle);
void setUserName(int userHandle, String name);
void setUserIcon(int userHandle, in Bitmap icon);
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 24bf05e..c6b2151 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -16,24 +16,24 @@
package android.os;
-import static libcore.io.OsConstants.AF_UNIX;
-import static libcore.io.OsConstants.SEEK_SET;
-import static libcore.io.OsConstants.SOCK_STREAM;
-import static libcore.io.OsConstants.S_ISLNK;
-import static libcore.io.OsConstants.S_ISREG;
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.SEEK_SET;
+import static android.system.OsConstants.SOCK_STREAM;
+import static android.system.OsConstants.S_ISLNK;
+import static android.system.OsConstants.S_ISREG;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStat;
import android.util.Log;
import dalvik.system.CloseGuard;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
-import libcore.io.Libcore;
import libcore.io.Memory;
-import libcore.io.OsConstants;
-import libcore.io.StructStat;
import java.io.Closeable;
import java.io.File;
@@ -261,7 +261,7 @@
*/
public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
try {
- final FileDescriptor fd = Libcore.os.dup(orig);
+ final FileDescriptor fd = Os.dup(orig);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
@@ -297,7 +297,7 @@
original.setInt$(fd);
try {
- final FileDescriptor dup = Libcore.os.dup(original);
+ final FileDescriptor dup = Os.dup(original);
return new ParcelFileDescriptor(dup);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
@@ -359,7 +359,7 @@
*/
public static ParcelFileDescriptor[] createPipe() throws IOException {
try {
- final FileDescriptor[] fds = Libcore.os.pipe();
+ final FileDescriptor[] fds = Os.pipe();
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fds[0]),
new ParcelFileDescriptor(fds[1]) };
@@ -381,7 +381,7 @@
public static ParcelFileDescriptor[] createReliablePipe() throws IOException {
try {
final FileDescriptor[] comm = createCommSocketPair();
- final FileDescriptor[] fds = Libcore.os.pipe();
+ final FileDescriptor[] fds = Os.pipe();
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fds[0], comm[0]),
new ParcelFileDescriptor(fds[1], comm[1]) };
@@ -398,7 +398,7 @@
try {
final FileDescriptor fd0 = new FileDescriptor();
final FileDescriptor fd1 = new FileDescriptor();
- Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1);
+ Os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1);
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fd0),
new ParcelFileDescriptor(fd1) };
@@ -421,7 +421,7 @@
final FileDescriptor[] comm = createCommSocketPair();
final FileDescriptor fd0 = new FileDescriptor();
final FileDescriptor fd1 = new FileDescriptor();
- Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1);
+ Os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1);
return new ParcelFileDescriptor[] {
new ParcelFileDescriptor(fd0, comm[0]),
new ParcelFileDescriptor(fd1, comm[1]) };
@@ -434,7 +434,7 @@
try {
final FileDescriptor comm1 = new FileDescriptor();
final FileDescriptor comm2 = new FileDescriptor();
- Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2);
+ Os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2);
IoUtils.setBlocking(comm1, false);
IoUtils.setBlocking(comm2, false);
return new FileDescriptor[] { comm1, comm2 };
@@ -520,7 +520,7 @@
return mWrapped.getStatSize();
} else {
try {
- final StructStat st = Libcore.os.fstat(mFd);
+ final StructStat st = Os.fstat(mFd);
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
return st.st_size;
} else {
@@ -543,7 +543,7 @@
return mWrapped.seekTo(pos);
} else {
try {
- return Libcore.os.lseek(mFd, pos, SEEK_SET);
+ return Os.lseek(mFd, pos, SEEK_SET);
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
@@ -695,7 +695,7 @@
writePtr += len;
}
- Libcore.os.write(mCommFd, buf, 0, writePtr);
+ Os.write(mCommFd, buf, 0, writePtr);
} catch (ErrnoException e) {
// Reporting status is best-effort
Log.w(TAG, "Failed to report status: " + e);
@@ -712,7 +712,7 @@
private static Status readCommStatus(FileDescriptor comm, byte[] buf) {
try {
- final int n = Libcore.os.read(comm, buf, 0, buf.length);
+ final int n = Os.read(comm, buf, 0, buf.length);
if (n == 0) {
// EOF means they're dead
return new Status(Status.DEAD);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 995e396..1b3aa0a 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -18,6 +18,7 @@
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
+import android.system.Os;
import android.util.Log;
import com.android.internal.os.Zygote;
import java.io.BufferedWriter;
@@ -28,7 +29,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import libcore.io.Libcore;
/*package*/ class ZygoteStartFailedEx extends Exception {
/**
@@ -707,16 +707,6 @@
return primaryZygoteState;
}
- // TODO: Get rid of this. This is a temporary workaround until all the
- // compilation related pieces for the dual zygote stack are ready.
- // b/3647418.
- if (System.getenv("ANDROID_SOCKET_" + SECONDARY_ZYGOTE_SOCKET) == null) {
- Log.e(LOG_TAG, "Forcing app to primary zygote, secondary unavailable (ABI= " + abi + ")");
- // Should be :
- // throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
- return primaryZygoteState;
- }
-
// The primary zygote didn't match. Try the secondary.
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET,
@@ -741,7 +731,7 @@
* {@link #killProcess} and {@link #sendSignal}.
*/
public static final int myPid() {
- return Libcore.os.getpid();
+ return Os.getpid();
}
/**
@@ -749,7 +739,7 @@
* @hide
*/
public static final int myPpid() {
- return Libcore.os.getppid();
+ return Os.getppid();
}
/**
@@ -757,7 +747,7 @@
* {@link #setThreadPriority(int, int)}.
*/
public static final int myTid() {
- return Libcore.os.gettid();
+ return Os.gettid();
}
/**
@@ -767,7 +757,7 @@
* a uid identifies a specific app sandbox in a specific user.
*/
public static final int myUid() {
- return Libcore.os.getuid();
+ return Os.getuid();
}
/**
diff --git a/core/java/android/os/StatFs.java b/core/java/android/os/StatFs.java
index 9e9521a..13e9a15 100644
--- a/core/java/android/os/StatFs.java
+++ b/core/java/android/os/StatFs.java
@@ -16,9 +16,9 @@
package android.os;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-import libcore.io.StructStatVfs;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStatVfs;
/**
* Retrieve overall information about the space on a filesystem. This is a
@@ -41,7 +41,7 @@
private static StructStatVfs doStat(String path) {
try {
- return Libcore.os.statvfs(path);
+ return Os.statvfs(path);
} catch (ErrnoException e) {
throw new IllegalArgumentException("Invalid path: " + path, e);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 63de9a0..1b2b798 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -300,8 +300,7 @@
/**
* Sets all the user-wide restrictions for this user.
- * Requires the MANAGE_USERS permission or profile/device owner
- * privileges.
+ * Requires the MANAGE_USERS permission.
* @param restrictions the Bundle containing all the restrictions.
*/
public void setUserRestrictions(Bundle restrictions) {
@@ -310,8 +309,7 @@
/**
* Sets all the user-wide restrictions for the specified user.
- * Requires the MANAGE_USERS permission or profile/device owner
- * privileges.
+ * Requires the MANAGE_USERS permission.
* @param restrictions the Bundle containing all the restrictions.
* @param userHandle the UserHandle of the user for whom to set the restrictions.
*/
@@ -325,8 +323,7 @@
/**
* Sets the value of a specific restriction.
- * Requires the MANAGE_USERS permission or profile/device owner
- * privileges.
+ * Requires the MANAGE_USERS permission.
* @param key the key of the restriction
* @param value the value for the restriction
*/
@@ -339,8 +336,7 @@
/**
* @hide
* Sets the value of a specific restriction on a specific user.
- * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission or profile/device owner
- * privileges.
+ * Requires the MANAGE_USERS permission.
* @param key the key of the restriction
* @param value the value for the restriction
* @param userHandle the user whose restriction is to be changed.
@@ -441,6 +437,22 @@
}
/**
+ * Sets the user as enabled, if such an user exists.
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * Note that the default is true, it's only that managed profiles might not be enabled.
+ *
+ * @param userHandle the id of the profile to enable
+ * @hide
+ */
+ public void setUserEnabled(int userHandle) {
+ try {
+ mService.setUserEnabled(userHandle);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not enable the profile", e);
+ }
+ }
+
+ /**
* Return the number of users currently created on the device.
*/
public int getUserCount() {
@@ -560,7 +572,7 @@
/**
* Returns information for all users on this device. Requires
* {@link android.Manifest.permission#MANAGE_USERS} permission.
- *
+ *
* @param excludeDying specify if the list should exclude users being
* removed.
* @return the list of users that were created.
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 9a768e0..b907375 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -17,7 +17,7 @@
package android.provider;
import static android.net.TrafficStats.KB_IN_BYTES;
-import static libcore.io.OsConstants.SEEK_SET;
+import static android.system.OsConstants.SEEK_SET;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
@@ -38,11 +38,11 @@
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.system.Os;
import android.util.Log;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
-import libcore.io.Libcore;
import java.io.BufferedInputStream;
import java.io.File;
@@ -809,7 +809,7 @@
// optimal decode path; otherwise fall back to buffering.
BufferedInputStream is = null;
try {
- Libcore.os.lseek(fd, offset, SEEK_SET);
+ Os.lseek(fd, offset, SEEK_SET);
} catch (ErrnoException e) {
is = new BufferedInputStream(new FileInputStream(fd), THUMBNAIL_BUFFER_SIZE);
is.mark(THUMBNAIL_BUFFER_SIZE);
@@ -835,7 +835,7 @@
bitmap = BitmapFactory.decodeStream(is, null, opts);
} else {
try {
- Libcore.os.lseek(fd, offset, SEEK_SET);
+ Os.lseek(fd, offset, SEEK_SET);
} catch (ErrnoException e) {
e.rethrowAsIOException();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7f9f862..ab06230 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -723,6 +723,13 @@
= "android.settings.NOTIFICATION_LISTENER_SETTINGS";
/**
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CONDITION_PROVIDER_SETTINGS
+ = "android.settings.ACTION_CONDITION_PROVIDER_SETTINGS";
+
+ /**
* Activity Action: Show settings for video captioning.
* <p>
* In some cases, a matching Activity may not exist, so ensure you safeguard
@@ -757,6 +764,11 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_ZEN_MODE_SETTINGS = "android.settings.ZEN_MODE_SETTINGS";
+ /** {@hide} */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String
+ ACTION_SHOW_REGULATORY_INFO = "android.settings.SHOW_REGULATORY_INFO";
+
// End of Intent actions for Settings
/**
@@ -3311,6 +3323,12 @@
"input_method_selector_visibility";
/**
+ * The currently selected voice interaction service flattened ComponentName.
+ * @hide
+ */
+ public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
+
+ /**
* bluetooth HCI snoop log configuration
* @hide
*/
@@ -4510,6 +4528,11 @@
*/
public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
+ /**
+ * @hide
+ */
+ public static final String ENABLED_CONDITION_PROVIDERS = "enabled_condition_providers";
+
/** @hide */
public static final String BAR_SERVICE_COMPONENT = "bar_service_component";
diff --git a/core/java/android/provider/TvContract.java b/core/java/android/provider/TvContract.java
index 233e0ca..62252be 100644
--- a/core/java/android/provider/TvContract.java
+++ b/core/java/android/provider/TvContract.java
@@ -16,9 +16,13 @@
package android.provider;
+import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.net.Uri;
+import java.util.List;
+
/**
* <p>
* The contract between the TV provider and applications. Contains definitions for the supported
@@ -42,6 +46,35 @@
/** The authority for the TV provider. */
public static final String AUTHORITY = "com.android.tv";
+ private static final String PATH_CHANNEL = "channel";
+ private static final String PATH_PROGRAM = "program";
+ private static final String PATH_INPUT = "input";
+
+ /**
+ * An optional query, update or delete URI parameter that allows the caller to specify start
+ * time (in milliseconds since the epoch) to filter programs.
+ *
+ * @hide
+ */
+ public static final String PARAM_START_TIME = "start_time";
+
+ /**
+ * An optional query, update or delete URI parameter that allows the caller to specify end time
+ * (in milliseconds since the epoch) to filter programs.
+ *
+ * @hide
+ */
+ public static final String PARAM_END_TIME = "end_time";
+
+ /**
+ * A query, update or delete URI parameter that allows the caller to operate on all or
+ * browsable-only channels. If set to "true", the rows that contain non-browsable channels are
+ * not affected.
+ *
+ * @hide
+ */
+ public static final String PARAM_BROWSABLE_ONLY = "browable_only";
+
/**
* Builds a URI that points to a specific channel.
*
@@ -52,6 +85,32 @@
}
/**
+ * Builds a URI that points to all browsable channels from a given TV input.
+ *
+ * @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
+ * the given TV input.
+ */
+ public static final Uri buildChannelsUriForInput(ComponentName name) {
+ return buildChannelsUriForInput(name, true);
+ }
+
+ /**
+ * Builds a URI that points to all or browsable-only channels from a given TV input.
+ *
+ * @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
+ * the given TV input.
+ * @param browsableOnly If set to {@code true} the URI points to only browsable channels. If set
+ * to {@code false} the URI points to all channels regardless of whether they are
+ * browsable or not.
+ */
+ public static final Uri buildChannelsUriForInput(ComponentName name, boolean browsableOnly) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+ .appendPath(PATH_INPUT).appendPath(name.getPackageName())
+ .appendPath(name.getClassName()).appendPath(PATH_CHANNEL)
+ .appendQueryParameter(PARAM_BROWSABLE_ONLY, String.valueOf(browsableOnly)).build();
+ }
+
+ /**
* Builds a URI that points to a specific program.
*
* @param programId The ID of the program to point to.
@@ -61,6 +120,37 @@
}
/**
+ * Builds a URI that points to all programs on a given channel.
+ *
+ * @param channelUri The URI of the channel to return programs for.
+ */
+ public static final Uri buildProgramsUriForChannel(Uri channelUri) {
+ if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) {
+ throw new IllegalArgumentException("Not a channel: " + channelUri);
+ }
+ String channelId = String.valueOf(ContentUris.parseId(channelUri));
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+ .appendPath(PATH_CHANNEL).appendPath(channelId).appendPath(PATH_PROGRAM).build();
+ }
+
+ /**
+ * Builds a URI that points to programs on a specific channel whose schedules overlap with the
+ * given time frame.
+ *
+ * @param channelUri The URI of the channel to return programs for.
+ * @param startTime The start time used to filter programs. The returned programs should have
+ * {@link Programs#END_TIME_UTC_MILLIS} that is greater than this time.
+ * @param endTime The end time used to filter programs. The returned programs should have
+ * {@link Programs#START_TIME_UTC_MILLIS} that is less than this time.
+ */
+ public static final Uri buildProgramsUriForChannel(Uri channelUri, long startTime,
+ long endTime) {
+ Uri uri = buildProgramsUriForChannel(channelUri);
+ return uri.buildUpon().appendQueryParameter(PARAM_START_TIME, String.valueOf(startTime))
+ .appendQueryParameter(PARAM_END_TIME, String.valueOf(endTime)).build();
+ }
+
+ /**
* Builds a URI that points to a specific program the user watched.
*
* @param watchedProgramId The ID of the watched program to point to.
@@ -70,6 +160,61 @@
return ContentUris.withAppendedId(WatchedPrograms.CONTENT_URI, watchedProgramId);
}
+ /**
+ * Extracts the {@link Channels#PACKAGE_NAME} from a given URI.
+ *
+ * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
+ * {@link #buildChannelsUriForInput(ComponentName, boolean)}.
+ * @hide
+ */
+ public static final String getPackageName(Uri channelsUri) {
+ final List<String> paths = channelsUri.getPathSegments();
+ if (paths.size() < 4) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ return paths.get(1);
+ }
+
+ /**
+ * Extracts the {@link Channels#SERVICE_NAME} from a given URI.
+ *
+ * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
+ * {@link #buildChannelsUriForInput(ComponentName, boolean)}.
+ * @hide
+ */
+ public static final String getServiceName(Uri channelsUri) {
+ final List<String> paths = channelsUri.getPathSegments();
+ if (paths.size() < 4) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ return paths.get(2);
+ }
+
+ /**
+ * Extracts the {@link Channels#_ID} from a given URI.
+ *
+ * @param programsUri A URI constructed by {@link #buildProgramsUriForChannel(Uri)} or
+ * {@link #buildProgramsUriForChannel(Uri, long, long)}.
+ * @hide
+ */
+ public static final String getChannelId(Uri programsUri) {
+ final List<String> paths = programsUri.getPathSegments();
+ if (paths.size() < 3) {
+ throw new IllegalArgumentException("Not programs: " + programsUri);
+ }
+ if (!PATH_CHANNEL.equals(paths.get(0)) || !PATH_PROGRAM.equals(paths.get(2))) {
+ throw new IllegalArgumentException("Not programs: " + programsUri);
+ }
+ return paths.get(1);
+ }
+
+
private TvContract() {}
/**
@@ -93,7 +238,8 @@
public static final class Channels implements BaseTvColumns {
/** The content:// style URI for this table. */
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/channel");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ + PATH_CHANNEL);
/** The MIME type of a directory of TV channels. */
public static final String CONTENT_TYPE =
@@ -276,7 +422,8 @@
public static final class Programs implements BaseTvColumns {
/** The content:// style URI for this table. */
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/program");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ + PATH_PROGRAM);
/** The MIME type of a directory of TV programs. */
public static final String CONTENT_TYPE =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java b/core/java/android/service/notification/Condition.aidl
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
copy to core/java/android/service/notification/Condition.aidl
index 0377123..432852c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
+++ b/core/java/android/service/notification/Condition.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (c) 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -14,10 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package android.service.notification;
-import android.view.View;
+parcelable Condition;
-public interface OnSizeChangedListener {
- void onSizeChanged(View view, int w, int h, int oldw, int oldh);
-}
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
new file mode 100644
index 0000000..71e3166
--- /dev/null
+++ b/core/java/android/service/notification/Condition.java
@@ -0,0 +1,147 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Condition information from condition providers.
+ *
+ * @hide
+ */
+public class Condition implements Parcelable {
+
+ public static final String SCHEME = "condition";
+
+ public static final int STATE_FALSE = 0;
+ public static final int STATE_TRUE = 1;
+ public static final int STATE_UNKNOWN = 2;
+ public static final int STATE_ERROR = 3;
+
+ public static final int FLAG_RELEVANT_NOW = 1 << 0;
+ public static final int FLAG_RELEVANT_ALWAYS = 1 << 1;
+
+ public final Uri id;
+ public String caption;
+ public int state;
+ public int flags;
+
+ public Condition(Uri id, String caption, int state, int flags) {
+ if (id == null) throw new IllegalArgumentException("id is required");
+ if (caption == null) throw new IllegalArgumentException("caption is required");
+ if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state);
+ this.id = id;
+ this.caption = caption;
+ this.state = state;
+ this.flags = flags;
+ }
+
+ private Condition(Parcel source) {
+ this((Uri)source.readParcelable(Condition.class.getClassLoader()),
+ source.readString(),
+ source.readInt(),
+ source.readInt());
+ }
+
+ private static boolean isValidState(int state) {
+ return state >= STATE_FALSE && state <= STATE_ERROR;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(id, 0);
+ dest.writeString(caption);
+ dest.writeInt(state);
+ dest.writeInt(flags);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(Condition.class.getSimpleName()).append('[')
+ .append("id=").append(id)
+ .append(",caption=").append(caption)
+ .append(",state=").append(stateToString(state))
+ .append(",flags=").append(flags)
+ .append(']').toString();
+ }
+
+ public static String stateToString(int state) {
+ if (state == STATE_FALSE) return "STATE_FALSE";
+ if (state == STATE_TRUE) return "STATE_TRUE";
+ if (state == STATE_UNKNOWN) return "STATE_UNKNOWN";
+ if (state == STATE_ERROR) return "STATE_ERROR";
+ throw new IllegalArgumentException("state is invalid: " + state);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Condition)) return false;
+ if (o == this) return true;
+ final Condition other = (Condition) o;
+ return Objects.equals(other.id, id)
+ && Objects.equals(other.caption, caption)
+ && other.state == state
+ && other.flags == flags;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, caption, state, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public Condition copy() {
+ final Parcel parcel = Parcel.obtain();
+ try {
+ writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return new Condition(parcel);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ public static Uri.Builder newId(Context context) {
+ return new Uri.Builder().scheme(SCHEME).authority(context.getPackageName());
+ }
+
+ public static boolean isValidId(Uri id, String pkg) {
+ return id != null && id.getScheme().equals(SCHEME) && id.getAuthority().equals(pkg);
+ }
+
+ public static final Parcelable.Creator<Condition> CREATOR
+ = new Parcelable.Creator<Condition>() {
+ @Override
+ public Condition createFromParcel(Parcel source) {
+ return new Condition(source);
+ }
+
+ @Override
+ public Condition[] newArray(int size) {
+ return new Condition[size];
+ }
+ };
+}
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
new file mode 100644
index 0000000..326412f
--- /dev/null
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import android.annotation.SdkConstant;
+import android.app.INotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * 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>
+ * <pre>
+ * <service android:name=".MyConditionProvider"
+ * android:label="@string/service_name"
+ * android:permission="android.permission.BIND_CONDITION_PROVIDER_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.service.notification.ConditionProviderService" />
+ * </intent-filter>
+ * </service></pre>
+ *
+ * @hide
+ */
+public abstract class ConditionProviderService extends Service {
+ private final String TAG = ConditionProviderService.class.getSimpleName()
+ + "[" + getClass().getSimpleName() + "]";
+
+ private final H mHandler = new H();
+
+ private Provider mProvider;
+ private INotificationManager mNoMan;
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE
+ = "android.service.notification.ConditionProviderService";
+
+ abstract public void onConnected();
+ abstract public void onRequestConditions(int relevance);
+ abstract public void onSubscribe(Uri conditionId);
+ abstract public void onUnsubscribe(Uri conditionId);
+
+ private final INotificationManager getNotificationInterface() {
+ if (mNoMan == null) {
+ mNoMan = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ }
+ return mNoMan;
+ }
+
+ public final void notifyCondition(Condition condition) {
+ if (condition == null) return;
+ notifyConditions(new Condition[]{ condition });
+ }
+
+ public final void notifyConditions(Condition... conditions) {
+ if (!isBound() || conditions == null) return;
+ try {
+ getNotificationInterface().notifyConditions(getPackageName(), mProvider, conditions);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (mProvider == null) {
+ mProvider = new Provider();
+ }
+ return mProvider;
+ }
+
+ private boolean isBound() {
+ if (mProvider == null) {
+ Log.w(TAG, "Condition provider service not yet bound.");
+ return false;
+ }
+ return true;
+ }
+
+ private final class Provider extends IConditionProvider.Stub {
+ @Override
+ public void onConnected() {
+ mHandler.obtainMessage(H.ON_CONNECTED).sendToTarget();
+ }
+
+ @Override
+ public void onRequestConditions(int relevance) {
+ mHandler.obtainMessage(H.ON_REQUEST_CONDITIONS, relevance, 0).sendToTarget();
+ }
+
+ @Override
+ public void onSubscribe(Uri conditionId) {
+ mHandler.obtainMessage(H.ON_SUBSCRIBE, conditionId).sendToTarget();
+ }
+
+ @Override
+ public void onUnsubscribe(Uri conditionId) {
+ mHandler.obtainMessage(H.ON_UNSUBSCRIBE, conditionId).sendToTarget();
+ }
+ }
+
+ private final class H extends Handler {
+ private static final int ON_CONNECTED = 1;
+ private static final int ON_REQUEST_CONDITIONS = 2;
+ private static final int ON_SUBSCRIBE = 3;
+ private static final int ON_UNSUBSCRIBE = 4;
+
+ @Override
+ public void handleMessage(Message msg) {
+ String name = null;
+ try {
+ switch(msg.what) {
+ case ON_CONNECTED:
+ name = "onConnected";
+ onConnected();
+ break;
+ case ON_REQUEST_CONDITIONS:
+ name = "onRequestConditions";
+ onRequestConditions(msg.arg1);
+ break;
+ case ON_SUBSCRIBE:
+ name = "onSubscribe";
+ onSubscribe((Uri)msg.obj);
+ break;
+ case ON_UNSUBSCRIBE:
+ name = "onUnsubscribe";
+ onUnsubscribe((Uri)msg.obj);
+ break;
+ }
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running " + name, t);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java b/core/java/android/service/notification/IConditionListener.aidl
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
copy to core/java/android/service/notification/IConditionListener.aidl
index 0377123..01f874f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
+++ b/core/java/android/service/notification/IConditionListener.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
+/**
+ * Copyright (c) 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package android.service.notification;
-import android.view.View;
+import android.net.Uri;
+import android.service.notification.Condition;
-public interface OnSizeChangedListener {
- void onSizeChanged(View view, int w, int h, int oldw, int oldh);
-}
+/** @hide */
+oneway interface IConditionListener {
+ void onConditionsReceived(in Condition[] conditions);
+}
\ No newline at end of file
diff --git a/core/java/android/service/notification/IConditionProvider.aidl b/core/java/android/service/notification/IConditionProvider.aidl
new file mode 100644
index 0000000..ada8939
--- /dev/null
+++ b/core/java/android/service/notification/IConditionProvider.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import android.net.Uri;
+import android.service.notification.Condition;
+
+/** @hide */
+oneway interface IConditionProvider {
+ void onConnected();
+ void onRequestConditions(int relevance);
+ void onSubscribe(in Uri conditionId);
+ void onUnsubscribe(in Uri conditionId);
+}
\ No newline at end of file
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 2c0b76d..72720d1 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -87,7 +87,7 @@
}
private String key() {
- return pkg + '|' + opPkg + '|' + id + '|' + tag + '|' + uid;
+ return pkg + '|' + id + '|' + tag + '|' + uid;
}
public void writeToParcel(Parcel out, int flags) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java b/core/java/android/service/voice/IVoiceInteractionService.aidl
similarity index 69%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
rename to core/java/android/service/voice/IVoiceInteractionService.aidl
index 0377123..e9e2f4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
+++ b/core/java/android/service/voice/IVoiceInteractionService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package android.service.voice;
-import android.view.View;
-
-public interface OnSizeChangedListener {
- void onSizeChanged(View view, int w, int h, int oldw, int oldh);
+/**
+ * @hide
+ */
+oneway interface IVoiceInteractionService {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java b/core/java/android/service/voice/IVoiceInteractionSession.aidl
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
copy to core/java/android/service/voice/IVoiceInteractionSession.aidl
index 0377123..7dbf66b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package android.service.voice;
-import android.view.View;
+import android.os.Bundle;
-public interface OnSizeChangedListener {
- void onSizeChanged(View view, int w, int h, int oldw, int oldh);
+import com.android.internal.app.IVoiceInteractorCallback;
+import com.android.internal.app.IVoiceInteractorRequest;
+
+/**
+ * @hide
+ */
+interface IVoiceInteractionSession {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java b/core/java/android/service/voice/IVoiceInteractionSessionService.aidl
similarity index 65%
copy from packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
copy to core/java/android/service/voice/IVoiceInteractionSessionService.aidl
index 0377123..2519442 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
+++ b/core/java/android/service/voice/IVoiceInteractionSessionService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package android.service.voice;
-import android.view.View;
+import android.os.Bundle;
-public interface OnSizeChangedListener {
- void onSizeChanged(View view, int w, int h, int oldw, int oldh);
+import android.service.voice.IVoiceInteractionSession;
+
+/**
+ * @hide
+ */
+oneway interface IVoiceInteractionSessionService {
+ void newSession(IBinder token, in Bundle args);
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
new file mode 100644
index 0000000..d005890
--- /dev/null
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.annotation.SdkConstant;
+import android.app.Instrumentation;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import com.android.internal.app.IVoiceInteractionManagerService;
+
+public class VoiceInteractionService extends Service {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ * To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_VOICE_INTERACTION} permission so
+ * that other applications can not abuse it.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.voice.VoiceInteractionService";
+
+ /**
+ * Name under which a VoiceInteractionService component publishes information about itself.
+ * This meta-data should reference an XML resource containing a
+ * <code><{@link
+ * android.R.styleable#VoiceInteractionService voice-interaction-service}></code> tag.
+ */
+ public static final String SERVICE_META_DATA = "android.voice_interaction";
+
+ IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
+ };
+
+ IVoiceInteractionManagerService mSystemService;
+
+ public void startVoiceActivity(Intent intent, Bundle sessionArgs) {
+ try {
+ mSystemService.startVoiceActivity(intent,
+ intent.resolveType(getContentResolver()),
+ mInterface, sessionArgs);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
+ ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
new file mode 100644
index 0000000..a909ead
--- /dev/null
+++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.speech.RecognitionService;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class VoiceInteractionServiceInfo {
+ static final String TAG = "VoiceInteractionServiceInfo";
+
+ private String mParseError;
+
+ private ServiceInfo mServiceInfo;
+ private String mSessionService;
+ private String mSettingsActivity;
+
+ public VoiceInteractionServiceInfo(PackageManager pm, ComponentName comp)
+ throws PackageManager.NameNotFoundException {
+ this(pm, pm.getServiceInfo(comp, PackageManager.GET_META_DATA));
+ }
+
+ public VoiceInteractionServiceInfo(PackageManager pm, ServiceInfo si) {
+ if (!Manifest.permission.BIND_VOICE_INTERACTION.equals(si.permission)) {
+ mParseError = "Service does not require permission "
+ + Manifest.permission.BIND_VOICE_INTERACTION;
+ return;
+ }
+
+ XmlResourceParser parser = null;
+ try {
+ parser = si.loadXmlMetaData(pm, VoiceInteractionService.SERVICE_META_DATA);
+ if (parser == null) {
+ mParseError = "No " + VoiceInteractionService.SERVICE_META_DATA
+ + " meta-data for " + si.packageName;
+ return;
+ }
+
+ Resources res = pm.getResourcesForApplication(si.applicationInfo);
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+
+ String nodeName = parser.getName();
+ if (!"voice-interaction-service".equals(nodeName)) {
+ mParseError = "Meta-data does not start with voice-interaction-service tag";
+ return;
+ }
+
+ TypedArray array = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.VoiceInteractionService);
+ mSessionService = array.getString(
+ com.android.internal.R.styleable.VoiceInteractionService_sessionService);
+ mSettingsActivity = array.getString(
+ com.android.internal.R.styleable.VoiceInteractionService_settingsActivity);
+ array.recycle();
+ if (mSessionService == null) {
+ mParseError = "No sessionService specified";
+ return;
+ }
+ } catch (XmlPullParserException e) {
+ mParseError = "Error parsing voice interation service meta-data: " + e;
+ Log.w(TAG, "error parsing voice interaction service meta-data", e);
+ return;
+ } catch (IOException e) {
+ mParseError = "Error parsing voice interation service meta-data: " + e;
+ Log.w(TAG, "error parsing voice interaction service meta-data", e);
+ return;
+ } catch (PackageManager.NameNotFoundException e) {
+ mParseError = "Error parsing voice interation service meta-data: " + e;
+ Log.w(TAG, "error parsing voice interaction service meta-data", e);
+ return;
+ } finally {
+ if (parser != null) parser.close();
+ }
+ mServiceInfo = si;
+ }
+
+ public String getParseError() {
+ return mParseError;
+ }
+
+ public ServiceInfo getServiceInfo() {
+ return mServiceInfo;
+ }
+
+ public String getSessionService() {
+ return mSessionService;
+ }
+
+ public String getSettingsActivity() {
+ return mSettingsActivity;
+ }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
new file mode 100644
index 0000000..963b6b4
--- /dev/null
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -0,0 +1,195 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.IVoiceInteractorCallback;
+import com.android.internal.app.IVoiceInteractorRequest;
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
+
+public abstract class VoiceInteractionSession {
+ static final String TAG = "VoiceInteractionSession";
+ static final boolean DEBUG = true;
+
+ final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
+ @Override
+ public IVoiceInteractorRequest startConfirmation(String callingPackage,
+ IVoiceInteractorCallback callback, String prompt, Bundle extras) {
+ Request request = findRequest(callback, true);
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_CONFIRMATION,
+ new Caller(callingPackage, Binder.getCallingUid()), request,
+ prompt, extras));
+ return request.mInterface;
+ }
+
+ @Override
+ public IVoiceInteractorRequest startCommand(String callingPackage,
+ IVoiceInteractorCallback callback, String command, Bundle extras) {
+ Request request = findRequest(callback, true);
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMMAND,
+ new Caller(callingPackage, Binder.getCallingUid()), request,
+ command, extras));
+ return request.mInterface;
+ }
+
+ @Override
+ public boolean[] supportsCommands(String callingPackage, String[] commands) {
+ Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS,
+ 0, new Caller(callingPackage, Binder.getCallingUid()), commands);
+ SomeArgs args = mHandlerCaller.sendMessageAndWait(msg);
+ if (args != null) {
+ boolean[] res = (boolean[])args.arg1;
+ args.recycle();
+ return res;
+ }
+ return new boolean[commands.length];
+ }
+ };
+
+ final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() {
+ };
+
+ public static class Request {
+ final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
+ @Override
+ public void cancel() throws RemoteException {
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
+ }
+ };
+ final IVoiceInteractorCallback mCallback;
+ final HandlerCaller mHandlerCaller;
+ Request(IVoiceInteractorCallback callback, HandlerCaller handlerCaller) {
+ mCallback = callback;
+ mHandlerCaller = handlerCaller;
+ }
+
+ public void sendConfirmResult(boolean confirmed, Bundle result) {
+ try {
+ if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
+ + " confirmed=" + confirmed + " result=" + result);
+ mCallback.deliverConfirmationResult(mInterface, confirmed, result);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void sendCommandResult(boolean complete, Bundle result) {
+ try {
+ if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
+ + " result=" + result);
+ mCallback.deliverCommandResult(mInterface, complete, result);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void sendCancelResult() {
+ try {
+ if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
+ mCallback.deliverCancel(mInterface);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public static class Caller {
+ final String packageName;
+ final int uid;
+
+ Caller(String _packageName, int _uid) {
+ packageName = _packageName;
+ uid = _uid;
+ }
+ }
+
+ static final int MSG_START_CONFIRMATION = 1;
+ static final int MSG_START_COMMAND = 2;
+ static final int MSG_SUPPORTS_COMMANDS = 3;
+ static final int MSG_CANCEL = 4;
+
+ final Context mContext;
+ final HandlerCaller mHandlerCaller;
+ final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() {
+ @Override
+ public void executeMessage(Message msg) {
+ SomeArgs args = (SomeArgs)msg.obj;
+ switch (msg.what) {
+ case MSG_START_CONFIRMATION:
+ if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface
+ + " prompt=" + args.arg3 + " extras=" + args.arg4);
+ onConfirm((Caller)args.arg1, (Request)args.arg2, (String)args.arg3,
+ (Bundle)args.arg4);
+ break;
+ case MSG_START_COMMAND:
+ if (DEBUG) Log.d(TAG, "onCommand: req=" + ((Request) args.arg2).mInterface
+ + " command=" + args.arg3 + " extras=" + args.arg4);
+ onCommand((Caller) args.arg1, (Request) args.arg2, (String) args.arg3,
+ (Bundle) args.arg4);
+ break;
+ case MSG_SUPPORTS_COMMANDS:
+ if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg2);
+ args.arg1 = onGetSupportedCommands((Caller) args.arg1, (String[]) args.arg2);
+ break;
+ case MSG_CANCEL:
+ if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request) args.arg1).mInterface);
+ onCancel((Request)args.arg1);
+ break;
+ }
+ }
+ };
+
+ final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
+
+ public VoiceInteractionSession(Context context) {
+ this(context, new Handler());
+ }
+
+ public VoiceInteractionSession(Context context, Handler handler) {
+ mContext = context;
+ mHandlerCaller = new HandlerCaller(context, handler.getLooper(),
+ mHandlerCallerCallback, true);
+ }
+
+ Request findRequest(IVoiceInteractorCallback callback, boolean newRequest) {
+ synchronized (this) {
+ Request req = mActiveRequests.get(callback.asBinder());
+ if (req != null) {
+ if (newRequest) {
+ throw new IllegalArgumentException("Given request callback " + callback
+ + " is already active");
+ }
+ return req;
+ }
+ req = new Request(callback, mHandlerCaller);
+ mActiveRequests.put(callback.asBinder(), req);
+ return req;
+ }
+ }
+
+ public abstract boolean[] onGetSupportedCommands(Caller caller, String[] commands);
+ public abstract void onConfirm(Caller caller, Request request, String prompt, Bundle extras);
+ public abstract void onCommand(Caller caller, Request request, String command, Bundle extras);
+ public abstract void onCancel(Request request);
+}
diff --git a/core/java/android/service/voice/VoiceInteractionSessionService.java b/core/java/android/service/voice/VoiceInteractionSessionService.java
new file mode 100644
index 0000000..40e5bba
--- /dev/null
+++ b/core/java/android/service/voice/VoiceInteractionSessionService.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
+
+public abstract class VoiceInteractionSessionService extends Service {
+
+ static final int MSG_NEW_SESSION = 1;
+
+ IVoiceInteractionManagerService mSystemService;
+
+ IVoiceInteractionSessionService mInterface = new IVoiceInteractionSessionService.Stub() {
+ public void newSession(IBinder token, Bundle args) {
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOO(MSG_NEW_SESSION,
+ token, args));
+
+ }
+ };
+
+ HandlerCaller mHandlerCaller;
+ final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() {
+ @Override
+ public void executeMessage(Message msg) {
+ SomeArgs args = (SomeArgs)msg.obj;
+ switch (msg.what) {
+ case MSG_NEW_SESSION:
+ doNewSession((IBinder)args.arg1, (Bundle)args.arg2);
+ break;
+ }
+ }
+ };
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
+ ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+ mHandlerCaller = new HandlerCaller(this, Looper.myLooper(),
+ mHandlerCallerCallback, true);
+ }
+
+ public abstract VoiceInteractionSession onNewSession(Bundle args);
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mInterface.asBinder();
+ }
+
+ void doNewSession(IBinder token, Bundle args) {
+ VoiceInteractionSession session = onNewSession(args);
+ try {
+ mSystemService.deliverNewSession(token, session.mSession, session.mInteractor);
+ } catch (RemoteException e) {
+ }
+ }
+}
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 77cd71e..6f00707 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -188,10 +188,6 @@
spacing = metrics.descent - metrics.ascent;
}
- if (spacingmult != 1 || spacingadd != 0) {
- spacing = (int)(spacing * spacingmult + spacingadd + 0.5f);
- }
-
mBottom = spacing;
if (includepad) {
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index f7ac75a..0db00f0 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -632,7 +632,11 @@
bottom = fm.bottom;
}
- if (j == 0) {
+ boolean firstLine = (j == 0);
+ boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
+ boolean lastLine = currentLineIsTheLastVisibleOne || (end == bufEnd);
+
+ if (firstLine) {
if (trackPad) {
mTopPadding = top - above;
}
@@ -641,7 +645,10 @@
above = top;
}
}
- if (end == bufEnd) {
+
+ int extra;
+
+ if (lastLine) {
if (trackPad) {
mBottomPadding = bottom - below;
}
@@ -651,9 +658,8 @@
}
}
- int extra;
- if (needMultiply && end != bufEnd) {
+ if (needMultiply && !lastLine) {
double ex = (below - above) * (spacingmult - 1) + spacingadd;
if (ex >= 0) {
extra = (int)(ex + EXTRA_ROUNDING);
@@ -690,8 +696,6 @@
if (ellipsize != null) {
// If there is only one line, then do any type of ellipsis except when it is MARQUEE
// if there are multiple lines, just allow END ellipsis on the last line
- boolean firstLine = (j == 0);
- boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
boolean doEllipsis =
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 9c98b98..b0cbcd2 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -106,4 +106,69 @@
public static String formatIpAddress(int ipv4Address) {
return NetworkUtils.intToInetAddress(ipv4Address).getHostAddress();
}
+
+ private static final int SECONDS_PER_MINUTE = 60;
+ private static final int SECONDS_PER_HOUR = 60 * 60;
+ private static final int SECONDS_PER_DAY = 24 * 60 * 60;
+
+ /**
+ * Returns elapsed time for the given millis, in the following format:
+ * 1 day 5 hrs; will include at most two units, can go down to seconds precision.
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @return the formatted elapsed time
+ * @hide
+ */
+ public static String formatShortElapsedTime(Context context, long millis) {
+ long secondsLong = millis / 1000;
+
+ int days = 0, hours = 0, minutes = 0;
+ if (secondsLong >= SECONDS_PER_DAY) {
+ days = (int)(secondsLong / SECONDS_PER_DAY);
+ secondsLong -= days * SECONDS_PER_DAY;
+ }
+ if (secondsLong >= SECONDS_PER_HOUR) {
+ hours = (int)(secondsLong / SECONDS_PER_HOUR);
+ secondsLong -= hours * SECONDS_PER_HOUR;
+ }
+ if (secondsLong >= SECONDS_PER_MINUTE) {
+ minutes = (int)(secondsLong / SECONDS_PER_MINUTE);
+ secondsLong -= minutes * SECONDS_PER_MINUTE;
+ }
+ int seconds = (int)secondsLong;
+
+ if (days >= 2) {
+ days += (hours+12)/24;
+ return context.getString(com.android.internal.R.string.durationDays, days);
+ } else if (days > 0) {
+ if (hours == 1) {
+ return context.getString(com.android.internal.R.string.durationDayHour, days, hours);
+ }
+ return context.getString(com.android.internal.R.string.durationDayHours, days, hours);
+ } else if (hours >= 2) {
+ hours += (minutes+30)/60;
+ return context.getString(com.android.internal.R.string.durationHours, hours);
+ } else if (hours > 0) {
+ if (minutes == 1) {
+ return context.getString(com.android.internal.R.string.durationHourMinute, hours,
+ minutes);
+ }
+ return context.getString(com.android.internal.R.string.durationHourMinutes, hours,
+ minutes);
+ } else if (minutes >= 2) {
+ minutes += (seconds+30)/60;
+ return context.getString(com.android.internal.R.string.durationMinutes, minutes);
+ } else if (minutes > 0) {
+ if (seconds == 1) {
+ return context.getString(com.android.internal.R.string.durationMinuteSecond, minutes,
+ seconds);
+ }
+ return context.getString(com.android.internal.R.string.durationMinuteSeconds, minutes,
+ seconds);
+ } else if (seconds == 1) {
+ return context.getString(com.android.internal.R.string.durationSecond, seconds);
+ } else {
+ return context.getString(com.android.internal.R.string.durationSeconds, seconds);
+ }
+ }
}
diff --git a/core/java/android/transition/ChangeClipBounds.java b/core/java/android/transition/ChangeClipBounds.java
new file mode 100644
index 0000000..a61b29d
--- /dev/null
+++ b/core/java/android/transition/ChangeClipBounds.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.RectEvaluator;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * ChangeClipBounds captures the {@link android.view.View#getClipBounds()} before and after the
+ * scene change and animates those changes during the transition.
+ */
+public class ChangeClipBounds extends Transition {
+
+ private static final String TAG = "ChangeTransform";
+
+ private static final String PROPNAME_CLIP = "android:clipBounds:clip";
+ private static final String PROPNAME_BOUNDS = "android:clipBounds:bounds";
+
+ private static final String[] sTransitionProperties = {
+ PROPNAME_CLIP,
+ };
+
+ @Override
+ public String[] getTransitionProperties() {
+ return sTransitionProperties;
+ }
+
+ private void captureValues(TransitionValues values) {
+ View view = values.view;
+ if (view.getVisibility() == View.GONE) {
+ return;
+ }
+
+ Rect clip = view.getClipBounds();
+ values.values.put(PROPNAME_CLIP, clip);
+ if (clip == null) {
+ Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+ values.values.put(PROPNAME_BOUNDS, bounds);
+ }
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null
+ || !startValues.values.containsKey(PROPNAME_CLIP)
+ || !endValues.values.containsKey(PROPNAME_CLIP)) {
+ return null;
+ }
+ Rect start = (Rect) startValues.values.get(PROPNAME_CLIP);
+ Rect end = (Rect) endValues.values.get(PROPNAME_CLIP);
+ if (start == null && end == null) {
+ return null; // No animation required since there is no clip.
+ }
+
+ if (start == null) {
+ start = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+ } else if (end == null) {
+ end = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ }
+ if (start.equals(end)) {
+ return null;
+ }
+
+ endValues.view.setClipBounds(start);
+ RectEvaluator evaluator = new RectEvaluator(new Rect());
+ return ObjectAnimator.ofObject(endValues.view, "clipBounds", evaluator, start, end);
+ }
+}
diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java
new file mode 100644
index 0000000..85cb2c7
--- /dev/null
+++ b/core/java/android/transition/ChangeTransform.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.transition;
+
+import android.animation.Animator;
+import android.animation.FloatArrayEvaluator;
+import android.animation.ObjectAnimator;
+import android.util.FloatProperty;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This Transition captures scale and rotation for Views before and after the
+ * scene change and animates those changes during the transition.
+ *
+ * <p>ChangeTransform does not work when the pivot changes between scenes, so either the
+ * pivot must be set to prevent automatic pivot adjustment or the View's size must be unchanged.</p>
+ */
+public class ChangeTransform extends Transition {
+
+ private static final String TAG = "ChangeTransform";
+
+ private static final String PROPNAME_SCALE_X = "android:changeTransform:scaleX";
+ private static final String PROPNAME_SCALE_Y = "android:changeTransform:scaleY";
+ private static final String PROPNAME_ROTATION_X = "android:changeTransform:rotationX";
+ private static final String PROPNAME_ROTATION_Y = "android:changeTransform:rotationY";
+ private static final String PROPNAME_ROTATION_Z = "android:changeTransform:rotationZ";
+ private static final String PROPNAME_PIVOT_X = "android:changeTransform:pivotX";
+ private static final String PROPNAME_PIVOT_Y = "android:changeTransform:pivotY";
+
+ private static final String[] sTransitionProperties = {
+ PROPNAME_SCALE_X,
+ PROPNAME_SCALE_Y,
+ PROPNAME_ROTATION_X,
+ PROPNAME_ROTATION_Y,
+ PROPNAME_ROTATION_Z,
+ };
+
+ private static final FloatProperty<View>[] sChangedProperties = new FloatProperty[] {
+ (FloatProperty) View.SCALE_X,
+ (FloatProperty) View.SCALE_Y,
+ (FloatProperty) View.ROTATION_X,
+ (FloatProperty) View.ROTATION_Y,
+ (FloatProperty) View.ROTATION,
+ };
+
+ private static Property<View, float[]> TRANSFORMS = new Property<View, float[]>(float[].class,
+ "transforms") {
+ @Override
+ public float[] get(View object) {
+ return null;
+ }
+
+ @Override
+ public void set(View view, float[] values) {
+ for (int i = 0; i < values.length; i++) {
+ float value = values[i];
+ if (value != Float.NaN) {
+ sChangedProperties[i].setValue(view, value);
+ }
+ }
+ }
+ };
+
+ @Override
+ public String[] getTransitionProperties() {
+ return sTransitionProperties;
+ }
+
+ private void captureValues(TransitionValues values) {
+ View view = values.view;
+ if (view.getVisibility() == View.GONE) {
+ return;
+ }
+
+ values.values.put(PROPNAME_SCALE_X, view.getScaleX());
+ values.values.put(PROPNAME_SCALE_Y, view.getScaleY());
+ values.values.put(PROPNAME_PIVOT_X, view.getPivotX());
+ values.values.put(PROPNAME_PIVOT_Y, view.getPivotY());
+ values.values.put(PROPNAME_ROTATION_X, view.getRotationX());
+ values.values.put(PROPNAME_ROTATION_Y, view.getRotationY());
+ values.values.put(PROPNAME_ROTATION_Z, view.getRotation());
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null
+ || !startValues.values.containsKey(PROPNAME_SCALE_X)
+ || !endValues.values.containsKey(PROPNAME_SCALE_X)
+ || !isPivotSame(startValues, endValues)
+ || !isChanged(startValues, endValues)) {
+ return null;
+ }
+
+ float[] start = createValues(startValues);
+ float[] end = createValues(endValues);
+ for (int i = 0; i < start.length; i++) {
+ if (start[i] == end[i]) {
+ start[i] = Float.NaN;
+ end[i] = Float.NaN;
+ } else {
+ sChangedProperties[i].setValue(endValues.view, start[i]);
+ }
+ }
+ FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[start.length]);
+ return ObjectAnimator.ofObject(endValues.view, TRANSFORMS, evaluator, start, end);
+ }
+
+ private static float[] createValues(TransitionValues transitionValues) {
+ float[] values = new float[sChangedProperties.length];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = (Float) transitionValues.values.get(sTransitionProperties[i]);
+ }
+ return values;
+ }
+
+ private static boolean isPivotSame(TransitionValues startValues, TransitionValues endValues) {
+ float startPivotX = (Float) startValues.values.get(PROPNAME_PIVOT_X);
+ float startPivotY = (Float) startValues.values.get(PROPNAME_PIVOT_Y);
+ float endPivotX = (Float) endValues.values.get(PROPNAME_PIVOT_X);
+ float endPivotY = (Float) endValues.values.get(PROPNAME_PIVOT_Y);
+
+ // We don't support pivot changes, because they could be automatically set
+ // and we can't end the state in an automatic state.
+ return startPivotX == endPivotX && startPivotY == endPivotY;
+ }
+
+ private static boolean isChanged(TransitionValues startValues, TransitionValues endValues) {
+ for (int i = 0; i < sChangedProperties.length; i++) {
+ Object start = startValues.values.get(sTransitionProperties[i]);
+ Object end = endValues.values.get(sTransitionProperties[i]);
+ if (!start.equals(end)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/transition/CircularPropagation.java b/core/java/android/transition/CircularPropagation.java
index 18a3d22..51beb51 100644
--- a/core/java/android/transition/CircularPropagation.java
+++ b/core/java/android/transition/CircularPropagation.java
@@ -34,7 +34,7 @@
public class CircularPropagation extends VisibilityPropagation {
private static final String TAG = "CircularPropagation";
- private float mPropagationSpeed = 4.0f;
+ private float mPropagationSpeed = 3.0f;
/**
* Sets the speed at which transition propagation happens, relative to the duration of the
@@ -91,8 +91,12 @@
float maxDistance = distance(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight());
float distanceFraction = distance/maxDistance;
- return Math.round(transition.getDuration() * directionMultiplier / mPropagationSpeed
- * distanceFraction);
+ long duration = transition.getDuration();
+ if (duration < 0) {
+ duration = 300;
+ }
+
+ return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction);
}
private static float distance(float x1, float y1, float x2, float y2) {
diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java
index 08e27d3..e70dc0c 100644
--- a/core/java/android/transition/Fade.java
+++ b/core/java/android/transition/Fade.java
@@ -105,7 +105,7 @@
if (DBG) {
Log.d(LOG_TAG, "Created animator " + anim);
}
- FadeAnimatorListener listener = new FadeAnimatorListener(view, endAlpha);
+ FadeAnimatorListener listener = new FadeAnimatorListener(view);
anim.addListener(listener);
anim.addPauseListener(listener);
return anim;
@@ -138,13 +138,11 @@
private static class FadeAnimatorListener extends AnimatorListenerAdapter {
private final View mView;
- private final float mEndAlpha;
private boolean mCanceled = false;
- private float mPausedAlpha;
+ private float mPausedAlpha = -1;
- public FadeAnimatorListener(View view, float endAlpha) {
+ public FadeAnimatorListener(View view) {
mView = view;
- mEndAlpha = endAlpha;
}
@Override
@@ -158,14 +156,14 @@
@Override
public void onAnimationEnd(Animator animator) {
if (!mCanceled) {
- mView.setTransitionAlpha(mEndAlpha);
+ mView.setTransitionAlpha(1);
}
}
@Override
public void onAnimationPause(Animator animator) {
mPausedAlpha = mView.getTransitionAlpha();
- mView.setTransitionAlpha(mEndAlpha);
+ mView.setTransitionAlpha(1);
}
@Override
diff --git a/core/java/android/transition/MoveImage.java b/core/java/android/transition/MoveImage.java
index d68e971..e4c3939 100644
--- a/core/java/android/transition/MoveImage.java
+++ b/core/java/android/transition/MoveImage.java
@@ -170,13 +170,20 @@
drawable = drawable.getConstantState().newDrawable();
final MatrixClippedDrawable matrixClippedDrawable = new MatrixClippedDrawable(drawable);
+ final ImageView overlayImage = new ImageView(imageView.getContext());
+ final ViewGroupOverlay overlay = sceneRoot.getOverlay();
+ overlay.add(overlayImage);
+ overlayImage.setLeft(0);
+ overlayImage.setTop(0);
+ overlayImage.setRight(sceneRoot.getWidth());
+ overlayImage.setBottom(sceneRoot.getBottom());
+ overlayImage.setScaleType(ImageView.ScaleType.MATRIX);
+ overlayImage.setImageDrawable(matrixClippedDrawable);
matrixClippedDrawable.setMatrix(startMatrix);
matrixClippedDrawable.setBounds(startBounds);
matrixClippedDrawable.setClipRect(startClip);
imageView.setVisibility(View.INVISIBLE);
- final ViewGroupOverlay overlay = sceneRoot.getOverlay();
- overlay.add(matrixClippedDrawable);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(matrixClippedDrawable,
changes.toArray(new PropertyValuesHolder[changes.size()]));
@@ -184,19 +191,24 @@
@Override
public void onAnimationEnd(Animator animation) {
imageView.setVisibility(View.VISIBLE);
- overlay.remove(matrixClippedDrawable);
+ overlay.remove(overlayImage);
}
@Override
public void onAnimationPause(Animator animation) {
imageView.setVisibility(View.VISIBLE);
- overlay.remove(matrixClippedDrawable);
+ overlayImage.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationResume(Animator animation) {
imageView.setVisibility(View.INVISIBLE);
- overlay.add(matrixClippedDrawable);
+ overlayImage.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ onAnimationEnd(animation);
}
};
diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java
index c331945..5d38ac8 100644
--- a/core/java/android/transition/SidePropagation.java
+++ b/core/java/android/transition/SidePropagation.java
@@ -52,7 +52,7 @@
*/
public static final int BOTTOM = Slide.BOTTOM;
- private float mPropagationSpeed = 4.0f;
+ private float mPropagationSpeed = 3.0f;
private int mSide = BOTTOM;
/**
@@ -129,8 +129,12 @@
float maxDistance = getMaxDistance(sceneRoot);
float distanceFraction = distance/maxDistance;
- return Math.round(transition.getDuration() * directionMultiplier / mPropagationSpeed
- * distanceFraction);
+ long duration = transition.getDuration();
+ if (duration < 0) {
+ duration = 300;
+ }
+
+ return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction);
}
private int distance(int viewX, int viewY, int epicenterX, int epicenterY,
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index c67d6fa..2549fde 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -66,13 +66,12 @@
*
* {@sample development/samples/ApiDemos/res/transition/changebounds.xml ChangeBounds}
*
- * <p>{@link android.transition.Explode} transition:</p>
+ * <p>This TransitionSet contains {@link android.transition.Explode} for visibility,
+ * {@link android.transition.ChangeBounds}, {@link android.transition.ChangeTransform},
+ * and {@link android.transition.ChangeClipBounds} for non-<code>ImageView</code>s and
+ * {@link android.transition.MoveImage} for <code>ImageView</code>s:</p>
*
- * {@sample development/samples/ApiDemos/res/transition/explode.xml Explode}
- *
- * <p>{@link android.transition.MoveImage} transition:</p>
- *
- * {@sample development/samples/ApiDemos/res/transition/move_image.xml MoveImage}
+ * {@sample development/samples/ApiDemos/res/transition/explode_move_together.xml MultipleTransform}
*
* <p>Note that attributes for the transition are not required, just as they are
* optional when declared in code; Transitions created from XML resources will use
@@ -89,7 +88,8 @@
* transition uses a fadingMode of {@link Fade#OUT} instead of the default
* out-in behavior. Finally, note the use of the <code>targets</code> sub-tag, which
* takes a set of {@link android.R.styleable#TransitionTarget target} tags, each
- * of which lists a specific <code>targetId</code> which this transition acts upon.
+ * of which lists a specific <code>targetId</code>, <code>targetClass</code>,
+ * <code>excludeId</code>, or <code>excludeClass</code>, which this transition acts upon.
* Use of targets is optional, but can be used to either limit the time spent checking
* attributes on unchanging views, or limiting the types of animations run on specific views.
* In this case, we know that only the <code>grayscaleContainer</code> will be
@@ -116,6 +116,7 @@
ArrayList<Integer> mTargetIdExcludes = null;
ArrayList<View> mTargetExcludes = null;
ArrayList<Class> mTargetTypeExcludes = null;
+ ArrayList<Class> mTargetTypes = null;
ArrayList<Integer> mTargetIdChildExcludes = null;
ArrayList<View> mTargetChildExcludes = null;
ArrayList<Class> mTargetTypeChildExcludes = null;
@@ -569,19 +570,15 @@
}
}
}
- if (mTargetIds.size() == 0 && mTargets.size() == 0) {
+ if (mTargetIds.size() == 0 && mTargets.size() == 0 && mTargetTypes == null) {
return true;
}
- if (mTargetIds.size() > 0) {
- for (int i = 0; i < mTargetIds.size(); ++i) {
- if (mTargetIds.get(i) == targetId) {
- return true;
- }
- }
+ if (mTargetIds.contains((int) targetId) || mTargets.contains(target)) {
+ return true;
}
- if (target != null && mTargets.size() > 0) {
- for (int i = 0; i < mTargets.size(); ++i) {
- if (mTargets.get(i) == target) {
+ if (mTargetTypes != null) {
+ for (int i = 0; i < mTargetTypes.size(); ++i) {
+ if (mTargetTypes.get(i).isInstance(target)) {
return true;
}
}
@@ -727,6 +724,36 @@
}
/**
+ * Adds the Class of a target view that this Transition is interested in
+ * animating. By default, there are no targetTypes, and a Transition will
+ * listen for changes on every view in the hierarchy below the sceneRoot
+ * of the Scene being transitioned into. Setting targetTypes constrains
+ * the Transition to only listen for, and act on, views with these classes.
+ * Views with different classes will be ignored.
+ *
+ * <p>Note that any View that can be cast to targetType will be included, so
+ * if targetType is <code>View.class</code>, all Views will be included.</p>
+ *
+ * @see #addTarget(int)
+ * @see #addTarget(android.view.View)
+ * @see #excludeTarget(Class, boolean)
+ * @see #excludeChildren(Class, boolean)
+ *
+ * @param targetType The type to include when running this transition.
+ * @return The Transition to which the target class was added.
+ * Returning the same object makes it easier to chain calls during
+ * construction, such as
+ * <code>transitionSet.addTransitions(new Fade()).addTarget(ImageView.class);</code>
+ */
+ public Transition addTarget(Class targetType) {
+ if (mTargetTypes == null) {
+ mTargetTypes = new ArrayList<Class>();
+ }
+ mTargetTypes.add(targetType);
+ return this;
+ }
+
+ /**
* Removes the given targetId from the list of ids that this Transition
* is interested in animating.
*
@@ -1116,9 +1143,6 @@
if (view == null) {
return;
}
- if (!isValidTarget(view, view.getId())) {
- return;
- }
boolean isListViewItem = false;
if (view.getParent() instanceof ListView) {
isListViewItem = true;
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index 14ecc15..a5e960a 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -153,6 +153,12 @@
} else if ("moveImage".equals(name)) {
transition = new MoveImage();
newTransition = true;
+ } else if ("changeTransform".equals(name)) {
+ transition = new ChangeTransform();
+ newTransition = true;
+ } else if ("changeClipBounds".equals(name)) {
+ transition = new ChangeClipBounds();
+ newTransition = true;
} else if ("autoTransition".equals(name)) {
transition = new AutoTransition();
newTransition = true;
@@ -210,7 +216,6 @@
int type;
int depth = parser.getDepth();
- ArrayList<Integer> targetIds = new ArrayList<Integer>();
while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
@@ -225,18 +230,31 @@
int id = a.getResourceId(
com.android.internal.R.styleable.TransitionTarget_targetId, -1);
if (id >= 0) {
- targetIds.add(id);
+ transition.addTarget(id);
+ } else if ((id = a.getResourceId(
+ com.android.internal.R.styleable.TransitionTarget_excludeId, -1)) >= 0) {
+ transition.excludeTarget(id, true);
+ } else {
+ String className = a.getString(
+ com.android.internal.R.styleable.TransitionTarget_excludeClass);
+ try {
+ if (className != null) {
+ Class clazz = Class.forName(className);
+ transition.excludeTarget(clazz, true);
+ } else if ((className = a.getString(
+ com.android.internal.R.styleable.TransitionTarget_targetClass))
+ != null) {
+ Class clazz = Class.forName(className);
+ transition.addTarget(clazz);
+ }
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Could not create " + className, e);
+ }
}
} else {
throw new RuntimeException("Unknown scene name: " + parser.getName());
}
}
- int numTargets = targetIds.size();
- if (numTargets > 0) {
- for (int i = 0; i < numTargets; ++i) {
- transition.addTarget(targetIds.get(i));
- }
- }
}
private Transition loadTransition(Transition transition, AttributeSet attrs)
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 526803a..6e6496c 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -347,10 +347,11 @@
}
if (viewToKeep != null) {
+ int originalVisibility = viewToKeep.getVisibility();
viewToKeep.setVisibility(View.VISIBLE);
Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
if (animator == null) {
- viewToKeep.setVisibility(finalVisibility);
+ viewToKeep.setVisibility(originalVisibility);
} else {
final View finalViewToKeep = viewToKeep;
animator.addListener(new AnimatorListenerAdapter() {
diff --git a/core/java/android/tv/ITvInputClient.aidl b/core/java/android/tv/ITvInputClient.aidl
index 43be6f0..538f8a1 100644
--- a/core/java/android/tv/ITvInputClient.aidl
+++ b/core/java/android/tv/ITvInputClient.aidl
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.tv.ITvInputSession;
+import android.view.InputChannel;
/**
* Interface a client of the ITvInputManager implements, to identify itself and receive information
@@ -25,6 +26,6 @@
* @hide
*/
oneway interface ITvInputClient {
- void onSessionCreated(in ComponentName name, IBinder token, int seq);
+ void onSessionCreated(in ComponentName name, IBinder token, in InputChannel channel, int seq);
void onAvailabilityChanged(in ComponentName name, boolean isAvailable);
}
diff --git a/core/java/android/tv/ITvInputService.aidl b/core/java/android/tv/ITvInputService.aidl
index 672784f..4f1bc2b 100644
--- a/core/java/android/tv/ITvInputService.aidl
+++ b/core/java/android/tv/ITvInputService.aidl
@@ -18,6 +18,7 @@
import android.tv.ITvInputServiceCallback;
import android.tv.ITvInputSessionCallback;
+import android.view.InputChannel;
/**
* Top-level interface to a TV input component (implemented in a Service).
@@ -26,5 +27,5 @@
oneway interface ITvInputService {
void registerCallback(ITvInputServiceCallback callback);
void unregisterCallback(in ITvInputServiceCallback callback);
- void createSession(ITvInputSessionCallback callback);
+ void createSession(in InputChannel channel, ITvInputSessionCallback callback);
}
diff --git a/core/java/android/tv/ITvInputSessionWrapper.java b/core/java/android/tv/ITvInputSessionWrapper.java
index a6e0877..3ccccf3 100644
--- a/core/java/android/tv/ITvInputSessionWrapper.java
+++ b/core/java/android/tv/ITvInputSessionWrapper.java
@@ -20,9 +20,16 @@
import android.graphics.Rect;
import android.net.Uri;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
+import android.tv.TvInputManager.Session;
import android.tv.TvInputService.TvInputSessionImpl;
import android.util.Log;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.Surface;
import com.android.internal.os.HandlerCaller;
@@ -45,50 +52,66 @@
private static final int DO_RELAYOUT_OVERLAY_VIEW = 6;
private static final int DO_REMOVE_OVERLAY_VIEW = 7;
- private TvInputSessionImpl mTvInputSession;
private final HandlerCaller mCaller;
- public ITvInputSessionWrapper(Context context, TvInputSessionImpl session) {
+ private TvInputSessionImpl mTvInputSessionImpl;
+ private InputChannel mChannel;
+ private TvInputEventReceiver mReceiver;
+
+ public ITvInputSessionWrapper(Context context, TvInputSessionImpl sessionImpl,
+ InputChannel channel) {
mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */);
- mTvInputSession = session;
+ mTvInputSessionImpl = sessionImpl;
+ mChannel = channel;
+ if (channel != null) {
+ mReceiver = new TvInputEventReceiver(channel, context.getMainLooper());
+ }
}
@Override
public void executeMessage(Message msg) {
- if (mTvInputSession == null) {
+ if (mTvInputSessionImpl == null) {
return;
}
switch (msg.what) {
case DO_RELEASE: {
- mTvInputSession.release();
- mTvInputSession = null;
+ mTvInputSessionImpl.release();
+ mTvInputSessionImpl = null;
+ if (mReceiver != null) {
+ mReceiver.dispose();
+ mReceiver = null;
+ }
+ if (mChannel != null) {
+ mChannel.dispose();
+ mChannel = null;
+ }
return;
}
case DO_SET_SURFACE: {
- mTvInputSession.setSurface((Surface) msg.obj);
+ mTvInputSessionImpl.setSurface((Surface) msg.obj);
return;
}
case DO_SET_VOLUME: {
- mTvInputSession.setVolume((Float) msg.obj);
+ mTvInputSessionImpl.setVolume((Float) msg.obj);
return;
}
case DO_TUNE: {
- mTvInputSession.tune((Uri) msg.obj);
+ mTvInputSessionImpl.tune((Uri) msg.obj);
return;
}
case DO_CREATE_OVERLAY_VIEW: {
SomeArgs args = (SomeArgs) msg.obj;
- mTvInputSession.createOverlayView((IBinder) args.arg1, (Rect) args.arg2);
+ mTvInputSessionImpl.createOverlayView((IBinder) args.arg1, (Rect) args.arg2);
args.recycle();
return;
}
case DO_RELAYOUT_OVERLAY_VIEW: {
- mTvInputSession.relayoutOverlayView((Rect) msg.obj);
+ mTvInputSessionImpl.relayoutOverlayView((Rect) msg.obj);
return;
}
case DO_REMOVE_OVERLAY_VIEW: {
- mTvInputSession.removeOverlayView(true);
+ mTvInputSessionImpl.removeOverlayView(true);
return;
}
default: {
@@ -133,4 +156,24 @@
public void removeOverlayView() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_OVERLAY_VIEW));
}
+
+ private final class TvInputEventReceiver extends InputEventReceiver {
+ public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ if (mTvInputSessionImpl == null) {
+ // The session has been finished.
+ finishInputEvent(event, false);
+ return;
+ }
+
+ int handled = mTvInputSessionImpl.dispatchInputEvent(event, this);
+ if (handled != Session.DISPATCH_IN_PROGRESS) {
+ finishInputEvent(event, handled == Session.DISPATCH_HANDLED);
+ }
+ }
+ }
}
diff --git a/core/java/android/tv/TvInputManager.java b/core/java/android/tv/TvInputManager.java
index 05f0b9c..7b9b1fb 100644
--- a/core/java/android/tv/TvInputManager.java
+++ b/core/java/android/tv/TvInputManager.java
@@ -21,9 +21,16 @@
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pools.Pool;
+import android.util.Pools.SimplePool;
import android.util.SparseArray;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventSender;
import android.view.Surface;
import android.view.View;
@@ -138,7 +145,8 @@
mUserId = userId;
mClient = new ITvInputClient.Stub() {
@Override
- public void onSessionCreated(ComponentName name, IBinder token, int seq) {
+ public void onSessionCreated(ComponentName name, IBinder token, InputChannel channel,
+ int seq) {
synchronized (mSessionCreateCallbackRecordMap) {
SessionCreateCallbackRecord record = mSessionCreateCallbackRecordMap.get(seq);
mSessionCreateCallbackRecordMap.delete(seq);
@@ -148,7 +156,7 @@
}
Session session = null;
if (token != null) {
- session = new Session(name, token, mService, mUserId);
+ session = new Session(token, channel, mService, mUserId);
}
record.postSessionCreated(session);
}
@@ -321,13 +329,30 @@
/** The Session provides the per-session functionality of TV inputs. */
public static final class Session {
+ static final int DISPATCH_IN_PROGRESS = -1;
+ static final int DISPATCH_NOT_HANDLED = 0;
+ static final int DISPATCH_HANDLED = 1;
+
+ private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500;
+
private final ITvInputManager mService;
private final int mUserId;
+
+ // For scheduling input event handling on the main thread. This also serves as a lock to
+ // protect pending input events and the input channel.
+ private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper());
+
+ private final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
+ private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
+
private IBinder mToken;
+ private TvInputEventSender mSender;
+ private InputChannel mChannel;
/** @hide */
- private Session(ComponentName name, IBinder token, ITvInputManager service, int userId) {
+ private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId) {
mToken = token;
+ mChannel = channel;
mService = service;
mUserId = userId;
}
@@ -347,6 +372,18 @@
} catch (RemoteException e) {
throw new RuntimeException(e);
}
+
+ synchronized (mHandler) {
+ if (mChannel != null) {
+ if (mSender != null) {
+ flushPendingEventsLocked();
+ mSender.dispose();
+ mSender = null;
+ }
+ mChannel.dispose();
+ mChannel = null;
+ }
+ }
}
/**
@@ -478,5 +515,228 @@
throw new RuntimeException(e);
}
}
+
+ /**
+ * Dispatches an input event to this session.
+ *
+ * @param event {@link InputEvent} to dispatch.
+ * @param token A token used to identify the input event later in the callback.
+ * @param callback A callback used to receive the dispatch result.
+ * @param handler {@link Handler} that the dispatch result will be delivered to.
+ * @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
+ * {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
+ * {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
+ * be invoked later.
+ * @throws IllegalArgumentException if any of the necessary arguments is {@code null}.
+ * @hide
+ */
+ public int dispatchInputEvent(InputEvent event, Object token,
+ FinishedInputEventCallback callback, Handler handler) {
+ if (event == null) {
+ throw new IllegalArgumentException("event cannot be null");
+ }
+ if (callback != null && handler == null) {
+ throw new IllegalArgumentException("handler cannot be null");
+ }
+ synchronized (mHandler) {
+ if (mChannel == null) {
+ return DISPATCH_NOT_HANDLED;
+ }
+ PendingEvent p = obtainPendingEventLocked(event, token, callback, handler);
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ // Already running on the main thread so we can send the event immediately.
+ return sendInputEventOnMainLooperLocked(p);
+ }
+
+ // Post the event to the main thread.
+ Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ return DISPATCH_IN_PROGRESS;
+ }
+ }
+
+ /**
+ * Callback that is invoked when an input event that was dispatched to this session has been
+ * finished.
+ *
+ * @hide
+ */
+ public interface FinishedInputEventCallback {
+ /**
+ * Called when the dispatched input event is finished.
+ *
+ * @param token a token passed to {@link #dispatchInputEvent}.
+ * @param handled {@code true} if the dispatched input event was handled properly.
+ * {@code false} otherwise.
+ */
+ public void onFinishedInputEvent(Object token, boolean handled);
+ }
+
+ // Must be called on the main looper
+ private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
+ synchronized (mHandler) {
+ int result = sendInputEventOnMainLooperLocked(p);
+ if (result == DISPATCH_IN_PROGRESS) {
+ return;
+ }
+ }
+
+ invokeFinishedInputEventCallback(p, false);
+ }
+
+ private int sendInputEventOnMainLooperLocked(PendingEvent p) {
+ if (mChannel != null) {
+ if (mSender == null) {
+ mSender = new TvInputEventSender(mChannel, mHandler.getLooper());
+ }
+
+ final InputEvent event = p.mEvent;
+ final int seq = event.getSequenceNumber();
+ if (mSender.sendInputEvent(seq, event)) {
+ mPendingEvents.put(seq, p);
+ Message msg = mHandler.obtainMessage(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT);
+ return DISPATCH_IN_PROGRESS;
+ }
+
+ Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:"
+ + event);
+ }
+ return DISPATCH_NOT_HANDLED;
+ }
+
+ void finishedInputEvent(int seq, boolean handled, boolean timeout) {
+ final PendingEvent p;
+ synchronized (mHandler) {
+ int index = mPendingEvents.indexOfKey(seq);
+ if (index < 0) {
+ return; // spurious, event already finished or timed out
+ }
+
+ p = mPendingEvents.valueAt(index);
+ mPendingEvents.removeAt(index);
+
+ if (timeout) {
+ Log.w(TAG, "Timeout waiting for seesion to handle input event after "
+ + INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken);
+ } else {
+ mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
+ }
+ }
+
+ invokeFinishedInputEventCallback(p, handled);
+ }
+
+ // Assumes the event has already been removed from the queue.
+ void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
+ p.mHandled = handled;
+ if (p.mHandler.getLooper().isCurrentThread()) {
+ // Already running on the callback handler thread so we can send the callback
+ // immediately.
+ p.run();
+ } else {
+ // Post the event to the callback handler thread.
+ // In this case, the callback will be responsible for recycling the event.
+ Message msg = Message.obtain(p.mHandler, p);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
+ }
+ }
+
+ private void flushPendingEventsLocked() {
+ mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
+
+ final int count = mPendingEvents.size();
+ for (int i = 0; i < count; i++) {
+ int seq = mPendingEvents.keyAt(i);
+ Message msg = mHandler.obtainMessage(InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
+ }
+ }
+
+ private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
+ FinishedInputEventCallback callback, Handler handler) {
+ PendingEvent p = mPendingEventPool.acquire();
+ if (p == null) {
+ p = new PendingEvent();
+ }
+ p.mEvent = event;
+ p.mToken = token;
+ p.mCallback = callback;
+ p.mHandler = handler;
+ return p;
+ }
+
+ private void recyclePendingEventLocked(PendingEvent p) {
+ p.recycle();
+ mPendingEventPool.release(p);
+ }
+
+ private final class InputEventHandler extends Handler {
+ public static final int MSG_SEND_INPUT_EVENT = 1;
+ public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
+ public static final int MSG_FLUSH_INPUT_EVENT = 3;
+
+ InputEventHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SEND_INPUT_EVENT: {
+ sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj);
+ return;
+ }
+ case MSG_TIMEOUT_INPUT_EVENT: {
+ finishedInputEvent(msg.arg1, false, true);
+ return;
+ }
+ case MSG_FLUSH_INPUT_EVENT: {
+ finishedInputEvent(msg.arg1, false, false);
+ return;
+ }
+ }
+ }
+ }
+
+ private final class TvInputEventSender extends InputEventSender {
+ public TvInputEventSender(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEventFinished(int seq, boolean handled) {
+ finishedInputEvent(seq, handled, false);
+ }
+ }
+
+ private final class PendingEvent implements Runnable {
+ public InputEvent mEvent;
+ public Object mToken;
+ public FinishedInputEventCallback mCallback;
+ public Handler mHandler;
+ public boolean mHandled;
+
+ public void recycle() {
+ mEvent = null;
+ mToken = null;
+ mCallback = null;
+ mHandler = null;
+ mHandled = false;
+ }
+
+ @Override
+ public void run() {
+ mCallback.onFinishedInputEvent(mToken, mHandled);
+
+ synchronized (mHandler) {
+ recyclePendingEventLocked(this);
+ }
+ }
+ }
}
}
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index 80eb407..70e7f95 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -28,13 +28,21 @@
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.tv.TvInputManager.Session;
import android.util.Log;
import android.view.Gravity;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
/**
* A base class for implementing television input service.
@@ -89,10 +97,17 @@
}
@Override
- public void createSession(ITvInputSessionCallback cb) {
- if (cb != null) {
- mHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, cb).sendToTarget();
+ public void createSession(InputChannel channel, ITvInputSessionCallback cb) {
+ if (channel == null) {
+ Log.w(TAG, "Creating session without input channel");
}
+ if (cb == null) {
+ return;
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = channel;
+ args.arg2 = cb;
+ mHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget();
}
};
}
@@ -131,9 +146,11 @@
/**
* Base class for derived classes to implement to provide {@link TvInputManager.Session}.
*/
- public abstract class TvInputSessionImpl {
+ public abstract class TvInputSessionImpl implements KeyEvent.Callback {
+ private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
private final WindowManager mWindowManager;
private WindowManager.LayoutParams mWindowParams;
+ private Surface mSurface;
private View mOverlayView;
private boolean mOverlayViewEnabled;
private IBinder mWindowToken;
@@ -143,6 +160,13 @@
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
}
+ /**
+ * Enables or disables the overlay view. By default, the overlay view is disabled. Must be
+ * called explicitly after the session is created to enable the overlay view.
+ *
+ * @param enable {@code true} if you want to enable the overlay view. {@code false}
+ * otherwise.
+ */
public void setOverlayViewEnabled(final boolean enable) {
mHandler.post(new Runnable() {
@Override
@@ -203,31 +227,154 @@
}
/**
+ * Default implementation of {@link android.view.KeyEvent.Callback#onKeyDown(int, KeyEvent)
+ * KeyEvent.Callback.onKeyDown()}: always returns false (doesn't handle the event).
+ * <p>
+ * Override this to intercept key down events before they are processed by the application.
+ * If you return true, the application will not process the event itself. If you return
+ * false, the normal application processing will occur as if the TV input had not seen the
+ * event at all.
+ *
+ * @param keyCode The value in event.getKeyCode().
+ * @param event Description of the key event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ */
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Default implementation of
+ * {@link android.view.KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
+ * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle the event).
+ * <p>
+ * Override this to intercept key long press events before they are processed by the
+ * application. If you return true, the application will not process the event itself. If
+ * you return false, the normal application processing will occur as if the TV input had not
+ * seen the event at all.
+ *
+ * @param keyCode The value in event.getKeyCode().
+ * @param event Description of the key event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ */
+ @Override
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Default implementation of
+ * {@link android.view.KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
+ * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle the event).
+ * <p>
+ * Override this to intercept special key multiple events before they are processed by the
+ * application. If you return true, the application will not itself process the event. If
+ * you return false, the normal application processing will occur as if the TV input had not
+ * seen the event at all.
+ *
+ * @param keyCode The value in event.getKeyCode().
+ * @param count The number of times the action was made.
+ * @param event Description of the key event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ */
+ @Override
+ public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Default implementation of {@link android.view.KeyEvent.Callback#onKeyUp(int, KeyEvent)
+ * KeyEvent.Callback.onKeyUp()}: always returns false (doesn't handle the event).
+ * <p>
+ * Override this to intercept key up events before they are processed by the application. If
+ * you return true, the application will not itself process the event. If you return false,
+ * the normal application processing will occur as if the TV input had not seen the event at
+ * all.
+ *
+ * @param keyCode The value in event.getKeyCode().
+ * @param event Description of the key event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ */
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Implement this method to handle touch screen motion events on the current input session.
+ *
+ * @param event The motion event being received.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ * @see View#onTouchEvent
+ */
+ public boolean onTouchEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * Implement this method to handle trackball events on the current input session.
+ *
+ * @param event The motion event being received.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ * @see View#onTrackballEvent
+ */
+ public boolean onTrackballEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * Implement this method to handle generic motion events on the current input session.
+ *
+ * @param event The motion event being received.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ * @see View#onGenericMotionEvent
+ */
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
* This method is called when the application would like to stop using the current input
* session.
*/
void release() {
onRelease();
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
+ }
removeOverlayView(true);
}
/**
- * Calls {@link onSetSurface}.
+ * Calls {@link #onSetSurface}.
*/
void setSurface(Surface surface) {
onSetSurface(surface);
+ if (mSurface != null) {
+ mSurface.release();
+ }
+ mSurface = surface;
// TODO: Handle failure.
}
/**
- * Calls {@link onSetVolume}.
+ * Calls {@link #onSetVolume}.
*/
void setVolume(float volume) {
onSetVolume(volume);
}
/**
- * Calls {@link onTune}.
+ * Calls {@link #onTune}.
*/
void tune(Uri channelUri) {
onTune(channelUri);
@@ -235,8 +382,8 @@
}
/**
- * Creates an overlay view. This calls {@link onCreateOverlayView} to get
- * a view to attach to the overlay window.
+ * Creates an overlay view. This calls {@link #onCreateOverlayView} to get a view to attach
+ * to the overlay window.
*
* @param windowToken A window token of an application.
* @param frame A position of the overlay view.
@@ -314,6 +461,42 @@
mWindowParams = null;
}
}
+
+ /**
+ * Takes care of dispatching incoming input events and tells whether the event was handled.
+ */
+ int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
+ if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")");
+ if (event instanceof KeyEvent) {
+ if (((KeyEvent) event).dispatch(this, mDispatcherState, this)) {
+ return Session.DISPATCH_HANDLED;
+ }
+ } else if (event instanceof MotionEvent) {
+ MotionEvent motionEvent = (MotionEvent) event;
+ final int source = motionEvent.getSource();
+ if (motionEvent.isTouchEvent()) {
+ if (onTouchEvent(motionEvent)) {
+ return Session.DISPATCH_HANDLED;
+ }
+ } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ if (onTrackballEvent(motionEvent)) {
+ return Session.DISPATCH_HANDLED;
+ }
+ } else {
+ if (onGenericMotionEvent(motionEvent)) {
+ return Session.DISPATCH_HANDLED;
+ }
+ }
+ }
+ if (mOverlayView == null) {
+ return Session.DISPATCH_NOT_HANDLED;
+ }
+ if (!mOverlayView.hasWindowFocus()) {
+ mOverlayView.getViewRootImpl().windowFocusChanged(true, true);
+ }
+ mOverlayView.getViewRootImpl().dispatchInputEvent(event, receiver);
+ return Session.DISPATCH_IN_PROGRESS;
+ }
}
private final class ServiceHandler extends Handler {
@@ -324,20 +507,23 @@
public final void handleMessage(Message msg) {
switch (msg.what) {
case DO_CREATE_SESSION: {
- ITvInputSessionCallback cb = (ITvInputSessionCallback) msg.obj;
+ SomeArgs args = (SomeArgs) msg.obj;
+ InputChannel channel = (InputChannel) args.arg1;
+ ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
try {
TvInputSessionImpl sessionImpl = onCreateSession();
if (sessionImpl == null) {
// Failed to create a session.
cb.onSessionCreated(null);
- return;
+ } else {
+ ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
+ sessionImpl, channel);
+ cb.onSessionCreated(stub);
}
- ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
- sessionImpl);
- cb.onSessionCreated(stub);
} catch (RemoteException e) {
Log.e(TAG, "error in onSessionCreated");
}
+ args.recycle();
return;
}
case DO_BROADCAST_AVAILABILITY_CHANGE: {
diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java
index 325950d..289823b 100644
--- a/core/java/android/tv/TvView.java
+++ b/core/java/android/tv/TvView.java
@@ -20,20 +20,24 @@
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
-import android.tv.TvInputManager;
import android.tv.TvInputManager.Session;
+import android.tv.TvInputManager.Session.FinishedInputEventCallback;
import android.tv.TvInputManager.SessionCreateCallback;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-import android.view.ViewTreeObserver;
/**
* View playing TV
*/
public class TvView extends SurfaceView {
+ // STOPSHIP: Turn debugging off.
+ private static final boolean DEBUG = true;
private static final String TAG = "TvView";
private final Handler mHandler = new Handler();
@@ -41,11 +45,11 @@
private Surface mSurface;
private boolean mOverlayViewCreated;
private Rect mOverlayViewFrame;
- private boolean mGlobalListenersAdded;
- private TvInputManager mTvInputManager;
+ private final TvInputManager mTvInputManager;
private SessionCreateCallback mSessionCreateCallback;
+ private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
- private SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+ private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format + ", width=" + width
@@ -70,6 +74,25 @@
}
};
+ private final FinishedInputEventCallback mFinishedInputEventCallback =
+ new FinishedInputEventCallback() {
+ @Override
+ public void onFinishedInputEvent(Object token, boolean handled) {
+ if (DEBUG) {
+ Log.d(TAG, "onFinishedInputEvent(token=" + token + ", handled=" + handled + ")");
+ }
+ if (handled) {
+ return;
+ }
+ // TODO: Re-order unhandled events.
+ InputEvent event = (InputEvent) token;
+ if (dispatchUnhandledInputEvent(event)) {
+ return;
+ }
+ getViewRootImpl().dispatchUnhandledInputEvent(event);
+ }
+ };
+
public TvView(Context context) {
this(context, null, 0);
}
@@ -124,6 +147,98 @@
}
}
+ /**
+ * Dispatches an unhandled input event to the next receiver.
+ * <p>
+ * Except system keys, TvView always consumes input events in the normal flow. This is called
+ * asynchronously from where the event is dispatched. It gives the host application a chance to
+ * dispatch the unhandled input events.
+ *
+ * @param event The input event.
+ * @return {@code true} if the event was handled by the view, {@code false} otherwise.
+ */
+ public boolean dispatchUnhandledInputEvent(InputEvent event) {
+ if (mOnUnhandledInputEventListener != null) {
+ if (mOnUnhandledInputEventListener.onUnhandledInputEvent(event)) {
+ return true;
+ }
+ }
+ return onUnhandledInputEvent(event);
+ }
+
+ /**
+ * Called when an unhandled input event was also not handled by the user provided callback. This
+ * is the last chance to handle the unhandled input event in the TvView.
+ *
+ * @param event The input event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to be
+ * handled by the next receiver, return {@code false}.
+ */
+ public boolean onUnhandledInputEvent(InputEvent event) {
+ return false;
+ }
+
+ /**
+ * Registers a callback to be invoked when an input event was not handled by the bound TV input.
+ *
+ * @param listener The callback to invoke when the unhandled input event was received.
+ */
+ public void setOnUnhandledInputEventListener(OnUnhandledInputEventListener listener) {
+ mOnUnhandledInputEventListener = listener;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (super.dispatchKeyEvent(event)) {
+ return true;
+ }
+ if (DEBUG) Log.d(TAG, "dispatchKeyEvent(" + event + ")");
+ if (mSession == null) {
+ return false;
+ }
+ int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+ return ret != Session.DISPATCH_NOT_HANDLED;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if (super.dispatchTouchEvent(event)) {
+ return true;
+ }
+ if (DEBUG) Log.d(TAG, "dispatchTouchEvent(" + event + ")");
+ if (mSession == null) {
+ return false;
+ }
+ int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+ return ret != Session.DISPATCH_NOT_HANDLED;
+ }
+
+ @Override
+ public boolean dispatchTrackballEvent(MotionEvent event) {
+ if (super.dispatchTrackballEvent(event)) {
+ return true;
+ }
+ if (DEBUG) Log.d(TAG, "dispatchTrackballEvent(" + event + ")");
+ if (mSession == null) {
+ return false;
+ }
+ int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+ return ret != Session.DISPATCH_NOT_HANDLED;
+ }
+
+ @Override
+ public boolean dispatchGenericMotionEvent(MotionEvent event) {
+ if (super.dispatchGenericMotionEvent(event)) {
+ return true;
+ }
+ if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent(" + event + ")");
+ if (mSession == null) {
+ return false;
+ }
+ int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+ return ret != Session.DISPATCH_NOT_HANDLED;
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -196,6 +311,23 @@
location[0] + getWidth(), location[1] + getHeight());
}
+ /**
+ * Interface definition for a callback to be invoked when the unhandled input event is received.
+ */
+ public interface OnUnhandledInputEventListener {
+ /**
+ * Called when an input event was not handled by the bound TV input.
+ * <p>
+ * This is called asynchronously from where the event is dispatched. It gives the host
+ * application a chance to handle the unhandled input events.
+ *
+ * @param event The input event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ */
+ boolean onUnhandledInputEvent(InputEvent event);
+ }
+
private class MySessionCreateCallback implements SessionCreateCallback {
final SessionCreateCallback mExternalCallback;
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 29c0ba2..aefced8 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -56,16 +56,18 @@
public static final class Event {
private final ByteBuffer mBuffer;
- // Layout of event log entry received from kernel.
+ // Layout of event log entry received from Android logger.
+ // see system/core/include/log/logger.h
private static final int LENGTH_OFFSET = 0;
+ private static final int HEADER_SIZE_OFFSET = 2;
private static final int PROCESS_OFFSET = 4;
private static final int THREAD_OFFSET = 8;
private static final int SECONDS_OFFSET = 12;
private static final int NANOSECONDS_OFFSET = 16;
- private static final int PAYLOAD_START = 20;
- private static final int TAG_OFFSET = 20;
- private static final int DATA_START = 24;
+ // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET
+ private static final int V1_PAYLOAD_START = 20;
+ private static final int DATA_OFFSET = 4;
// Value types
private static final byte INT_TYPE = 0;
@@ -97,14 +99,22 @@
/** @return the type tag code of the entry */
public int getTag() {
- return mBuffer.getInt(TAG_OFFSET);
+ int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
+ if (offset == 0) {
+ offset = V1_PAYLOAD_START;
+ }
+ return mBuffer.getInt(offset);
}
/** @return one of Integer, Long, String, null, or Object[] of same. */
public synchronized Object getData() {
try {
- mBuffer.limit(PAYLOAD_START + mBuffer.getShort(LENGTH_OFFSET));
- mBuffer.position(DATA_START); // Just after the tag.
+ int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
+ if (offset == 0) {
+ offset = V1_PAYLOAD_START;
+ }
+ mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET));
+ mBuffer.position(offset + DATA_OFFSET); // Just after the tag.
return decodeObject();
} catch (IllegalArgumentException e) {
Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index d8d11f7..97339cc 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -555,7 +555,7 @@
}
@Override
- public void invokeFunctor(long functor, boolean waitForCompletion) {
+ void invokeFunctor(long functor, boolean waitForCompletion) {
boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
boolean hasContext = !needsContext;
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 56d96e1..d31c79d 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -453,7 +453,7 @@
* has invoked. If false, the functor may be invoked
* asynchronously.
*/
- public abstract void invokeFunctor(long functor, boolean waitForCompletion);
+ abstract void invokeFunctor(long functor, boolean waitForCompletion);
/**
* Initializes the hardware renderer for the specified surface and setup the
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index c3f429c..05e202b 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -645,12 +645,11 @@
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
// frameworks/native/include/android/keycodes.h
- // frameworks/base/include/androidfw/KeycodeLabels.h
+ // frameworks/base/include/androidfw/InputEventAttributes.h
// external/webkit/WebKit/android/plugins/ANPKeyCodes.h
// frameworks/base/core/res/res/values/attrs.xml
// emulator?
// LAST_KEYCODE
- // KEYCODE_SYMBOLIC_NAMES
//
// Also Android currently does not reserve code ranges for vendor-
// specific key codes. If you have new key codes to have, you
@@ -658,237 +657,6 @@
// those new codes. This is intended to maintain a consistent
// set of key code definitions across all Android devices.
- // Symbolic names of all key codes.
- private static final SparseArray<String> KEYCODE_SYMBOLIC_NAMES = new SparseArray<String>();
- private static void populateKeycodeSymbolicNames() {
- SparseArray<String> names = KEYCODE_SYMBOLIC_NAMES;
- names.append(KEYCODE_UNKNOWN, "KEYCODE_UNKNOWN");
- names.append(KEYCODE_SOFT_LEFT, "KEYCODE_SOFT_LEFT");
- names.append(KEYCODE_SOFT_RIGHT, "KEYCODE_SOFT_RIGHT");
- names.append(KEYCODE_HOME, "KEYCODE_HOME");
- names.append(KEYCODE_BACK, "KEYCODE_BACK");
- names.append(KEYCODE_CALL, "KEYCODE_CALL");
- names.append(KEYCODE_ENDCALL, "KEYCODE_ENDCALL");
- names.append(KEYCODE_0, "KEYCODE_0");
- names.append(KEYCODE_1, "KEYCODE_1");
- names.append(KEYCODE_2, "KEYCODE_2");
- names.append(KEYCODE_3, "KEYCODE_3");
- names.append(KEYCODE_4, "KEYCODE_4");
- names.append(KEYCODE_5, "KEYCODE_5");
- names.append(KEYCODE_6, "KEYCODE_6");
- names.append(KEYCODE_7, "KEYCODE_7");
- names.append(KEYCODE_8, "KEYCODE_8");
- names.append(KEYCODE_9, "KEYCODE_9");
- names.append(KEYCODE_STAR, "KEYCODE_STAR");
- names.append(KEYCODE_POUND, "KEYCODE_POUND");
- names.append(KEYCODE_DPAD_UP, "KEYCODE_DPAD_UP");
- names.append(KEYCODE_DPAD_DOWN, "KEYCODE_DPAD_DOWN");
- names.append(KEYCODE_DPAD_LEFT, "KEYCODE_DPAD_LEFT");
- names.append(KEYCODE_DPAD_RIGHT, "KEYCODE_DPAD_RIGHT");
- names.append(KEYCODE_DPAD_CENTER, "KEYCODE_DPAD_CENTER");
- names.append(KEYCODE_VOLUME_UP, "KEYCODE_VOLUME_UP");
- names.append(KEYCODE_VOLUME_DOWN, "KEYCODE_VOLUME_DOWN");
- names.append(KEYCODE_POWER, "KEYCODE_POWER");
- names.append(KEYCODE_CAMERA, "KEYCODE_CAMERA");
- names.append(KEYCODE_CLEAR, "KEYCODE_CLEAR");
- names.append(KEYCODE_A, "KEYCODE_A");
- names.append(KEYCODE_B, "KEYCODE_B");
- names.append(KEYCODE_C, "KEYCODE_C");
- names.append(KEYCODE_D, "KEYCODE_D");
- names.append(KEYCODE_E, "KEYCODE_E");
- names.append(KEYCODE_F, "KEYCODE_F");
- names.append(KEYCODE_G, "KEYCODE_G");
- names.append(KEYCODE_H, "KEYCODE_H");
- names.append(KEYCODE_I, "KEYCODE_I");
- names.append(KEYCODE_J, "KEYCODE_J");
- names.append(KEYCODE_K, "KEYCODE_K");
- names.append(KEYCODE_L, "KEYCODE_L");
- names.append(KEYCODE_M, "KEYCODE_M");
- names.append(KEYCODE_N, "KEYCODE_N");
- names.append(KEYCODE_O, "KEYCODE_O");
- names.append(KEYCODE_P, "KEYCODE_P");
- names.append(KEYCODE_Q, "KEYCODE_Q");
- names.append(KEYCODE_R, "KEYCODE_R");
- names.append(KEYCODE_S, "KEYCODE_S");
- names.append(KEYCODE_T, "KEYCODE_T");
- names.append(KEYCODE_U, "KEYCODE_U");
- names.append(KEYCODE_V, "KEYCODE_V");
- names.append(KEYCODE_W, "KEYCODE_W");
- names.append(KEYCODE_X, "KEYCODE_X");
- names.append(KEYCODE_Y, "KEYCODE_Y");
- names.append(KEYCODE_Z, "KEYCODE_Z");
- names.append(KEYCODE_COMMA, "KEYCODE_COMMA");
- names.append(KEYCODE_PERIOD, "KEYCODE_PERIOD");
- names.append(KEYCODE_ALT_LEFT, "KEYCODE_ALT_LEFT");
- names.append(KEYCODE_ALT_RIGHT, "KEYCODE_ALT_RIGHT");
- names.append(KEYCODE_SHIFT_LEFT, "KEYCODE_SHIFT_LEFT");
- names.append(KEYCODE_SHIFT_RIGHT, "KEYCODE_SHIFT_RIGHT");
- names.append(KEYCODE_TAB, "KEYCODE_TAB");
- names.append(KEYCODE_SPACE, "KEYCODE_SPACE");
- names.append(KEYCODE_SYM, "KEYCODE_SYM");
- names.append(KEYCODE_EXPLORER, "KEYCODE_EXPLORER");
- names.append(KEYCODE_ENVELOPE, "KEYCODE_ENVELOPE");
- names.append(KEYCODE_ENTER, "KEYCODE_ENTER");
- names.append(KEYCODE_DEL, "KEYCODE_DEL");
- names.append(KEYCODE_GRAVE, "KEYCODE_GRAVE");
- names.append(KEYCODE_MINUS, "KEYCODE_MINUS");
- names.append(KEYCODE_EQUALS, "KEYCODE_EQUALS");
- names.append(KEYCODE_LEFT_BRACKET, "KEYCODE_LEFT_BRACKET");
- names.append(KEYCODE_RIGHT_BRACKET, "KEYCODE_RIGHT_BRACKET");
- names.append(KEYCODE_BACKSLASH, "KEYCODE_BACKSLASH");
- names.append(KEYCODE_SEMICOLON, "KEYCODE_SEMICOLON");
- names.append(KEYCODE_APOSTROPHE, "KEYCODE_APOSTROPHE");
- names.append(KEYCODE_SLASH, "KEYCODE_SLASH");
- names.append(KEYCODE_AT, "KEYCODE_AT");
- names.append(KEYCODE_NUM, "KEYCODE_NUM");
- names.append(KEYCODE_HEADSETHOOK, "KEYCODE_HEADSETHOOK");
- names.append(KEYCODE_FOCUS, "KEYCODE_FOCUS");
- names.append(KEYCODE_PLUS, "KEYCODE_PLUS");
- names.append(KEYCODE_MENU, "KEYCODE_MENU");
- names.append(KEYCODE_NOTIFICATION, "KEYCODE_NOTIFICATION");
- names.append(KEYCODE_SEARCH, "KEYCODE_SEARCH");
- names.append(KEYCODE_MEDIA_PLAY_PAUSE, "KEYCODE_MEDIA_PLAY_PAUSE");
- names.append(KEYCODE_MEDIA_STOP, "KEYCODE_MEDIA_STOP");
- names.append(KEYCODE_MEDIA_NEXT, "KEYCODE_MEDIA_NEXT");
- names.append(KEYCODE_MEDIA_PREVIOUS, "KEYCODE_MEDIA_PREVIOUS");
- names.append(KEYCODE_MEDIA_REWIND, "KEYCODE_MEDIA_REWIND");
- names.append(KEYCODE_MEDIA_FAST_FORWARD, "KEYCODE_MEDIA_FAST_FORWARD");
- names.append(KEYCODE_MUTE, "KEYCODE_MUTE");
- names.append(KEYCODE_PAGE_UP, "KEYCODE_PAGE_UP");
- names.append(KEYCODE_PAGE_DOWN, "KEYCODE_PAGE_DOWN");
- names.append(KEYCODE_PICTSYMBOLS, "KEYCODE_PICTSYMBOLS");
- names.append(KEYCODE_SWITCH_CHARSET, "KEYCODE_SWITCH_CHARSET");
- names.append(KEYCODE_BUTTON_A, "KEYCODE_BUTTON_A");
- names.append(KEYCODE_BUTTON_B, "KEYCODE_BUTTON_B");
- names.append(KEYCODE_BUTTON_C, "KEYCODE_BUTTON_C");
- names.append(KEYCODE_BUTTON_X, "KEYCODE_BUTTON_X");
- names.append(KEYCODE_BUTTON_Y, "KEYCODE_BUTTON_Y");
- names.append(KEYCODE_BUTTON_Z, "KEYCODE_BUTTON_Z");
- names.append(KEYCODE_BUTTON_L1, "KEYCODE_BUTTON_L1");
- names.append(KEYCODE_BUTTON_R1, "KEYCODE_BUTTON_R1");
- names.append(KEYCODE_BUTTON_L2, "KEYCODE_BUTTON_L2");
- names.append(KEYCODE_BUTTON_R2, "KEYCODE_BUTTON_R2");
- names.append(KEYCODE_BUTTON_THUMBL, "KEYCODE_BUTTON_THUMBL");
- names.append(KEYCODE_BUTTON_THUMBR, "KEYCODE_BUTTON_THUMBR");
- names.append(KEYCODE_BUTTON_START, "KEYCODE_BUTTON_START");
- names.append(KEYCODE_BUTTON_SELECT, "KEYCODE_BUTTON_SELECT");
- names.append(KEYCODE_BUTTON_MODE, "KEYCODE_BUTTON_MODE");
- names.append(KEYCODE_ESCAPE, "KEYCODE_ESCAPE");
- names.append(KEYCODE_FORWARD_DEL, "KEYCODE_FORWARD_DEL");
- names.append(KEYCODE_CTRL_LEFT, "KEYCODE_CTRL_LEFT");
- names.append(KEYCODE_CTRL_RIGHT, "KEYCODE_CTRL_RIGHT");
- names.append(KEYCODE_CAPS_LOCK, "KEYCODE_CAPS_LOCK");
- names.append(KEYCODE_SCROLL_LOCK, "KEYCODE_SCROLL_LOCK");
- names.append(KEYCODE_META_LEFT, "KEYCODE_META_LEFT");
- names.append(KEYCODE_META_RIGHT, "KEYCODE_META_RIGHT");
- names.append(KEYCODE_FUNCTION, "KEYCODE_FUNCTION");
- names.append(KEYCODE_SYSRQ, "KEYCODE_SYSRQ");
- names.append(KEYCODE_BREAK, "KEYCODE_BREAK");
- names.append(KEYCODE_MOVE_HOME, "KEYCODE_MOVE_HOME");
- names.append(KEYCODE_MOVE_END, "KEYCODE_MOVE_END");
- names.append(KEYCODE_INSERT, "KEYCODE_INSERT");
- names.append(KEYCODE_FORWARD, "KEYCODE_FORWARD");
- names.append(KEYCODE_MEDIA_PLAY, "KEYCODE_MEDIA_PLAY");
- names.append(KEYCODE_MEDIA_PAUSE, "KEYCODE_MEDIA_PAUSE");
- names.append(KEYCODE_MEDIA_CLOSE, "KEYCODE_MEDIA_CLOSE");
- names.append(KEYCODE_MEDIA_EJECT, "KEYCODE_MEDIA_EJECT");
- names.append(KEYCODE_MEDIA_RECORD, "KEYCODE_MEDIA_RECORD");
- names.append(KEYCODE_F1, "KEYCODE_F1");
- names.append(KEYCODE_F2, "KEYCODE_F2");
- names.append(KEYCODE_F3, "KEYCODE_F3");
- names.append(KEYCODE_F4, "KEYCODE_F4");
- names.append(KEYCODE_F5, "KEYCODE_F5");
- names.append(KEYCODE_F6, "KEYCODE_F6");
- names.append(KEYCODE_F7, "KEYCODE_F7");
- names.append(KEYCODE_F8, "KEYCODE_F8");
- names.append(KEYCODE_F9, "KEYCODE_F9");
- names.append(KEYCODE_F10, "KEYCODE_F10");
- names.append(KEYCODE_F11, "KEYCODE_F11");
- names.append(KEYCODE_F12, "KEYCODE_F12");
- names.append(KEYCODE_NUM_LOCK, "KEYCODE_NUM_LOCK");
- names.append(KEYCODE_NUMPAD_0, "KEYCODE_NUMPAD_0");
- names.append(KEYCODE_NUMPAD_1, "KEYCODE_NUMPAD_1");
- names.append(KEYCODE_NUMPAD_2, "KEYCODE_NUMPAD_2");
- names.append(KEYCODE_NUMPAD_3, "KEYCODE_NUMPAD_3");
- names.append(KEYCODE_NUMPAD_4, "KEYCODE_NUMPAD_4");
- names.append(KEYCODE_NUMPAD_5, "KEYCODE_NUMPAD_5");
- names.append(KEYCODE_NUMPAD_6, "KEYCODE_NUMPAD_6");
- names.append(KEYCODE_NUMPAD_7, "KEYCODE_NUMPAD_7");
- names.append(KEYCODE_NUMPAD_8, "KEYCODE_NUMPAD_8");
- names.append(KEYCODE_NUMPAD_9, "KEYCODE_NUMPAD_9");
- names.append(KEYCODE_NUMPAD_DIVIDE, "KEYCODE_NUMPAD_DIVIDE");
- names.append(KEYCODE_NUMPAD_MULTIPLY, "KEYCODE_NUMPAD_MULTIPLY");
- names.append(KEYCODE_NUMPAD_SUBTRACT, "KEYCODE_NUMPAD_SUBTRACT");
- names.append(KEYCODE_NUMPAD_ADD, "KEYCODE_NUMPAD_ADD");
- names.append(KEYCODE_NUMPAD_DOT, "KEYCODE_NUMPAD_DOT");
- names.append(KEYCODE_NUMPAD_COMMA, "KEYCODE_NUMPAD_COMMA");
- names.append(KEYCODE_NUMPAD_ENTER, "KEYCODE_NUMPAD_ENTER");
- names.append(KEYCODE_NUMPAD_EQUALS, "KEYCODE_NUMPAD_EQUALS");
- names.append(KEYCODE_NUMPAD_LEFT_PAREN, "KEYCODE_NUMPAD_LEFT_PAREN");
- names.append(KEYCODE_NUMPAD_RIGHT_PAREN, "KEYCODE_NUMPAD_RIGHT_PAREN");
- names.append(KEYCODE_VOLUME_MUTE, "KEYCODE_VOLUME_MUTE");
- names.append(KEYCODE_INFO, "KEYCODE_INFO");
- names.append(KEYCODE_CHANNEL_UP, "KEYCODE_CHANNEL_UP");
- names.append(KEYCODE_CHANNEL_DOWN, "KEYCODE_CHANNEL_DOWN");
- names.append(KEYCODE_ZOOM_IN, "KEYCODE_ZOOM_IN");
- names.append(KEYCODE_ZOOM_OUT, "KEYCODE_ZOOM_OUT");
- names.append(KEYCODE_TV, "KEYCODE_TV");
- names.append(KEYCODE_WINDOW, "KEYCODE_WINDOW");
- names.append(KEYCODE_GUIDE, "KEYCODE_GUIDE");
- names.append(KEYCODE_DVR, "KEYCODE_DVR");
- names.append(KEYCODE_BOOKMARK, "KEYCODE_BOOKMARK");
- names.append(KEYCODE_CAPTIONS, "KEYCODE_CAPTIONS");
- names.append(KEYCODE_SETTINGS, "KEYCODE_SETTINGS");
- names.append(KEYCODE_TV_POWER, "KEYCODE_TV_POWER");
- names.append(KEYCODE_TV_INPUT, "KEYCODE_TV_INPUT");
- names.append(KEYCODE_STB_INPUT, "KEYCODE_STB_INPUT");
- names.append(KEYCODE_STB_POWER, "KEYCODE_STB_POWER");
- names.append(KEYCODE_AVR_POWER, "KEYCODE_AVR_POWER");
- names.append(KEYCODE_AVR_INPUT, "KEYCODE_AVR_INPUT");
- names.append(KEYCODE_PROG_RED, "KEYCODE_PROG_RED");
- names.append(KEYCODE_PROG_GREEN, "KEYCODE_PROG_GREEN");
- names.append(KEYCODE_PROG_YELLOW, "KEYCODE_PROG_YELLOW");
- names.append(KEYCODE_PROG_BLUE, "KEYCODE_PROG_BLUE");
- names.append(KEYCODE_APP_SWITCH, "KEYCODE_APP_SWITCH");
- names.append(KEYCODE_BUTTON_1, "KEYCODE_BUTTON_1");
- names.append(KEYCODE_BUTTON_2, "KEYCODE_BUTTON_2");
- names.append(KEYCODE_BUTTON_3, "KEYCODE_BUTTON_3");
- names.append(KEYCODE_BUTTON_4, "KEYCODE_BUTTON_4");
- names.append(KEYCODE_BUTTON_5, "KEYCODE_BUTTON_5");
- names.append(KEYCODE_BUTTON_6, "KEYCODE_BUTTON_6");
- names.append(KEYCODE_BUTTON_7, "KEYCODE_BUTTON_7");
- names.append(KEYCODE_BUTTON_8, "KEYCODE_BUTTON_8");
- names.append(KEYCODE_BUTTON_9, "KEYCODE_BUTTON_9");
- names.append(KEYCODE_BUTTON_10, "KEYCODE_BUTTON_10");
- names.append(KEYCODE_BUTTON_11, "KEYCODE_BUTTON_11");
- names.append(KEYCODE_BUTTON_12, "KEYCODE_BUTTON_12");
- names.append(KEYCODE_BUTTON_13, "KEYCODE_BUTTON_13");
- names.append(KEYCODE_BUTTON_14, "KEYCODE_BUTTON_14");
- names.append(KEYCODE_BUTTON_15, "KEYCODE_BUTTON_15");
- names.append(KEYCODE_BUTTON_16, "KEYCODE_BUTTON_16");
- names.append(KEYCODE_LANGUAGE_SWITCH, "KEYCODE_LANGUAGE_SWITCH");
- names.append(KEYCODE_MANNER_MODE, "KEYCODE_MANNER_MODE");
- names.append(KEYCODE_3D_MODE, "KEYCODE_3D_MODE");
- names.append(KEYCODE_CONTACTS, "KEYCODE_CONTACTS");
- names.append(KEYCODE_CALENDAR, "KEYCODE_CALENDAR");
- names.append(KEYCODE_MUSIC, "KEYCODE_MUSIC");
- names.append(KEYCODE_CALCULATOR, "KEYCODE_CALCULATOR");
- names.append(KEYCODE_ZENKAKU_HANKAKU, "KEYCODE_ZENKAKU_HANKAKU");
- names.append(KEYCODE_EISU, "KEYCODE_EISU");
- names.append(KEYCODE_MUHENKAN, "KEYCODE_MUHENKAN");
- names.append(KEYCODE_HENKAN, "KEYCODE_HENKAN");
- names.append(KEYCODE_KATAKANA_HIRAGANA, "KEYCODE_KATAKANA_HIRAGANA");
- names.append(KEYCODE_YEN, "KEYCODE_YEN");
- names.append(KEYCODE_RO, "KEYCODE_RO");
- names.append(KEYCODE_KANA, "KEYCODE_KANA");
- names.append(KEYCODE_ASSIST, "KEYCODE_ASSIST");
- names.append(KEYCODE_BRIGHTNESS_DOWN, "KEYCODE_BRIGHTNESS_DOWN");
- names.append(KEYCODE_BRIGHTNESS_UP, "KEYCODE_BRIGHTNESS_UP");
- names.append(KEYCODE_MEDIA_AUDIO_TRACK, "KEYCODE_MEDIA_AUDIO_TRACK");
- names.append(KEYCODE_SLEEP, "KEYCODE_SLEEP");
- names.append(KEYCODE_WAKEUP, "KEYCODE_WAKEUP");
- };
-
// Symbolic names of all metakeys in bit order from least significant to most significant.
// Accordingly there are exactly 32 values in this table.
private static final String[] META_SYMBOLIC_NAMES = new String[] {
@@ -926,6 +694,8 @@
"0x80000000",
};
+ private static final String LABEL_PREFIX = "KEYCODE_";
+
/**
* @deprecated There are now more than MAX_KEYCODE keycodes.
* Use {@link #getMaxKeyCode()} instead.
@@ -1367,9 +1137,8 @@
boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
}
- static {
- populateKeycodeSymbolicNames();
- }
+ private static native String nativeKeyCodeToString(int keyCode);
+ private static native int nativeKeyCodeFromString(String keyCode);
private KeyEvent() {
}
@@ -1792,19 +1561,15 @@
return mAction == ACTION_DOWN;
}
- /**
- * Is this a system key? System keys can not be used for menu shortcuts.
- *
- * TODO: this information should come from a table somewhere.
- * TODO: should the dpad keys be here? arguably, because they also shouldn't be menu shortcuts
+ /** Is this a system key? System keys can not be used for menu shortcuts.
*/
public final boolean isSystem() {
- return native_isSystemKey(mKeyCode);
+ return isSystemKey(mKeyCode);
}
/** @hide */
- public final boolean hasDefaultAction() {
- return native_hasDefaultAction(mKeyCode);
+ public final boolean isWakeKey() {
+ return isWakeKey(mKeyCode);
}
/**
@@ -1887,6 +1652,62 @@
return false;
}
+
+ /** Is this a system key? System keys can not be used for menu shortcuts.
+ * @hide
+ */
+ public static final boolean isSystemKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MENU:
+ case KeyEvent.KEYCODE_SOFT_RIGHT:
+ case KeyEvent.KEYCODE_HOME:
+ case KeyEvent.KEYCODE_BACK:
+ case KeyEvent.KEYCODE_CALL:
+ case KeyEvent.KEYCODE_ENDCALL:
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_VOLUME_MUTE:
+ case KeyEvent.KEYCODE_MUTE:
+ case KeyEvent.KEYCODE_POWER:
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ case KeyEvent.KEYCODE_MEDIA_RECORD:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ case KeyEvent.KEYCODE_CAMERA:
+ case KeyEvent.KEYCODE_FOCUS:
+ case KeyEvent.KEYCODE_SEARCH:
+ case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
+ case KeyEvent.KEYCODE_BRIGHTNESS_UP:
+ case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ return true;
+ }
+
+ return false;
+ }
+
+ /** @hide */
+ public static final boolean isWakeKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK:
+ case KeyEvent.KEYCODE_POWER:
+ case KeyEvent.KEYCODE_MENU:
+ case KeyEvent.KEYCODE_SLEEP:
+ case KeyEvent.KEYCODE_WAKEUP:
+ return true;
+ }
+ return false;
+ }
+
/** {@inheritDoc} */
@Override
public final int getDeviceId() {
@@ -2866,8 +2687,8 @@
* @see KeyCharacterMap#getDisplayLabel
*/
public static String keyCodeToString(int keyCode) {
- String symbolicName = KEYCODE_SYMBOLIC_NAMES.get(keyCode);
- return symbolicName != null ? symbolicName : Integer.toString(keyCode);
+ String symbolicName = nativeKeyCodeToString(keyCode);
+ return symbolicName != null ? LABEL_PREFIX + symbolicName : Integer.toString(keyCode);
}
/**
@@ -2879,17 +2700,13 @@
* @see #keycodeToString(int)
*/
public static int keyCodeFromString(String symbolicName) {
- if (symbolicName == null) {
- throw new IllegalArgumentException("symbolicName must not be null");
+ if (symbolicName.startsWith(LABEL_PREFIX)) {
+ symbolicName = symbolicName.substring(LABEL_PREFIX.length());
}
-
- final int count = KEYCODE_SYMBOLIC_NAMES.size();
- for (int i = 0; i < count; i++) {
- if (symbolicName.equals(KEYCODE_SYMBOLIC_NAMES.valueAt(i))) {
- return i;
- }
+ int keyCode = nativeKeyCodeFromString(symbolicName);
+ if (keyCode > 0) {
+ return keyCode;
}
-
try {
return Integer.parseInt(symbolicName, 10);
} catch (NumberFormatException ex) {
@@ -2977,7 +2794,4 @@
out.writeLong(mDownTime);
out.writeLong(mEventTime);
}
-
- private native boolean native_isSystemKey(int keyCode);
- private native boolean native_hasDefaultAction(int keyCode);
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 6378ffd..0626ab96 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -167,6 +167,7 @@
*/
public final class MotionEvent extends InputEvent implements Parcelable {
private static final long NS_PER_MS = 1000000;
+ private static final String LABEL_PREFIX = "AXIS_";
/**
* An invalid pointer id.
@@ -1369,6 +1370,9 @@
private static native long nativeReadFromParcel(long nativePtr, Parcel parcel);
private static native void nativeWriteToParcel(long nativePtr, Parcel parcel);
+ private static native String nativeAxisToString(int axis);
+ private static native int nativeAxisFromString(String label);
+
private MotionEvent() {
}
@@ -3051,8 +3055,8 @@
* @return The symbolic name of the specified axis.
*/
public static String axisToString(int axis) {
- String symbolicName = AXIS_SYMBOLIC_NAMES.get(axis);
- return symbolicName != null ? symbolicName : Integer.toString(axis);
+ String symbolicName = nativeAxisToString(axis);
+ return symbolicName != null ? LABEL_PREFIX + symbolicName : Integer.toString(axis);
}
/**
@@ -3064,17 +3068,13 @@
* @see KeyEvent#keyCodeToString(int)
*/
public static int axisFromString(String symbolicName) {
- if (symbolicName == null) {
- throw new IllegalArgumentException("symbolicName must not be null");
+ if (symbolicName.startsWith(LABEL_PREFIX)) {
+ symbolicName = symbolicName.substring(LABEL_PREFIX.length());
}
-
- final int count = AXIS_SYMBOLIC_NAMES.size();
- for (int i = 0; i < count; i++) {
- if (symbolicName.equals(AXIS_SYMBOLIC_NAMES.valueAt(i))) {
- return i;
- }
+ int axis = nativeAxisFromString(symbolicName);
+ if (axis >= 0) {
+ return axis;
}
-
try {
return Integer.parseInt(symbolicName, 10);
} catch (NumberFormatException ex) {
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 30e4281..8b80c3e0 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -20,6 +20,9 @@
import android.graphics.Matrix;
import android.graphics.Outline;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* <p>A display list records a series of graphics related operations and can replay
* them later. Display lists are usually built by recording operations on a
@@ -176,11 +179,24 @@
private boolean mValid;
private final long mNativeRenderNode;
+ // We need to keep a strong reference to all running animators to ensure that
+ // they can call removeAnimator when they have finished, as the native-side
+ // object can only hold a WeakReference<> to avoid leaking memory due to
+ // cyclic references.
+ private List<RenderNodeAnimator> mActiveAnimators;
+
private RenderNode(String name) {
mNativeRenderNode = nCreate(name);
}
/**
+ * @see RenderNode#adopt(long)
+ */
+ private RenderNode(long nativePtr) {
+ mNativeRenderNode = nativePtr;
+ }
+
+ /**
* Creates a new display list that can be used to record batches of
* drawing operations.
*
@@ -195,6 +211,17 @@
}
/**
+ * Adopts an existing native render node.
+ *
+ * Note: This will *NOT* incRef() on the native object, however it will
+ * decRef() when it is destroyed. The caller should have already incRef'd it
+ */
+ public static RenderNode adopt(long nativePtr) {
+ return new RenderNode(nativePtr);
+ }
+
+
+ /**
* Starts recording a display list for the render node. All
* operations performed on the returned canvas are recorded and
* stored in this display list.
@@ -443,6 +470,14 @@
return nHasOverlappingRendering(mNativeRenderNode);
}
+ public void setElevation(float lift) {
+ nSetElevation(mNativeRenderNode, lift);
+ }
+
+ public float getElevation() {
+ return nGetElevation(mNativeRenderNode);
+ }
+
/**
* Sets the translation value for the display list on the X axis.
*
@@ -814,6 +849,23 @@
}
///////////////////////////////////////////////////////////////////////////
+ // Animations
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void addAnimator(RenderNodeAnimator animator) {
+ if (mActiveAnimators == null) {
+ mActiveAnimators = new ArrayList<RenderNodeAnimator>();
+ }
+ mActiveAnimators.add(animator);
+ nAddAnimator(mNativeRenderNode, animator.getNativeAnimator());
+ }
+
+ public void removeAnimator(RenderNodeAnimator animator) {
+ nRemoveAnimator(mNativeRenderNode, animator.getNativeAnimator());
+ mActiveAnimators.remove(animator);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
// Native methods
///////////////////////////////////////////////////////////////////////////
@@ -854,6 +906,7 @@
private static native void nSetAlpha(long renderNode, float alpha);
private static native void nSetHasOverlappingRendering(long renderNode,
boolean hasOverlappingRendering);
+ private static native void nSetElevation(long renderNode, float lift);
private static native void nSetTranslationX(long renderNode, float translationX);
private static native void nSetTranslationY(long renderNode, float translationY);
private static native void nSetTranslationZ(long renderNode, float translationZ);
@@ -874,6 +927,7 @@
private static native float nGetCameraDistance(long renderNode);
private static native float nGetScaleX(long renderNode);
private static native float nGetScaleY(long renderNode);
+ private static native float nGetElevation(long renderNode);
private static native float nGetTranslationX(long renderNode);
private static native float nGetTranslationY(long renderNode);
private static native float nGetTranslationZ(long renderNode);
@@ -886,6 +940,13 @@
private static native void nOutput(long renderNode);
///////////////////////////////////////////////////////////////////////////
+ // Animations
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static native void nAddAnimator(long renderNode, long animatorPtr);
+ private static native void nRemoveAnimator(long renderNode, long animatorPtr);
+
+ ///////////////////////////////////////////////////////////////////////////
// Finalization
///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
new file mode 100644
index 0000000..b70ae3d
--- /dev/null
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.util.SparseIntArray;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * @hide
+ */
+public final class RenderNodeAnimator {
+
+ // Keep in sync with enum RenderProperty in Animator.h
+ private static final int TRANSLATION_X = 0;
+ private static final int TRANSLATION_Y = 1;
+ private static final int TRANSLATION_Z = 2;
+ private static final int SCALE_X = 3;
+ private static final int SCALE_Y = 4;
+ private static final int ROTATION = 5;
+ private static final int ROTATION_X = 6;
+ private static final int ROTATION_Y = 7;
+ private static final int X = 8;
+ private static final int Y = 9;
+ private static final int Z = 10;
+ private static final int ALPHA = 11;
+
+ // ViewPropertyAnimator uses a mask for its values, we need to remap them
+ // to the enum values here. RenderPropertyAnimator can't use the mask values
+ // directly as internally it uses a lookup table so it needs the values to
+ // be sequential starting from 0
+ private static final SparseIntArray sViewPropertyAnimatorMap = new SparseIntArray(15) {{
+ put(ViewPropertyAnimator.TRANSLATION_X, TRANSLATION_X);
+ put(ViewPropertyAnimator.TRANSLATION_Y, TRANSLATION_Y);
+ put(ViewPropertyAnimator.TRANSLATION_Z, TRANSLATION_Z);
+ put(ViewPropertyAnimator.SCALE_X, SCALE_X);
+ put(ViewPropertyAnimator.SCALE_Y, SCALE_Y);
+ put(ViewPropertyAnimator.ROTATION, ROTATION);
+ put(ViewPropertyAnimator.ROTATION_X, ROTATION_X);
+ put(ViewPropertyAnimator.ROTATION_Y, ROTATION_Y);
+ put(ViewPropertyAnimator.X, X);
+ put(ViewPropertyAnimator.Y, Y);
+ put(ViewPropertyAnimator.Z, Z);
+ put(ViewPropertyAnimator.ALPHA, ALPHA);
+ }};
+
+ // Keep in sync DeltaValueType in Animator.h
+ private static final int DELTA_TYPE_ABSOLUTE = 0;
+ private static final int DELTA_TYPE_DELTA = 1;
+
+ private RenderNode mTarget;
+ private long mNativePtr;
+
+ public int mapViewPropertyToRenderProperty(int viewProperty) {
+ return sViewPropertyAnimatorMap.get(viewProperty);
+ }
+
+ public RenderNodeAnimator(int property, int deltaType, float deltaValue) {
+ mNativePtr = nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
+ property, deltaType, deltaValue);
+ }
+
+ public void start(View target) {
+ mTarget = target.mRenderNode;
+ mTarget.addAnimator(this);
+ // Kick off a frame to start the process
+ target.invalidateViewProperty(true, false);
+ }
+
+ public void cancel() {
+ mTarget.removeAnimator(this);
+ }
+
+ public void setDuration(int duration) {
+ nSetDuration(mNativePtr, duration);
+ }
+
+ long getNativeAnimator() {
+ return mNativePtr;
+ }
+
+ private void onFinished() {
+ mTarget.removeAnimator(this);
+ }
+
+ // Called by native
+ private static void callOnFinished(WeakReference<RenderNodeAnimator> weakThis) {
+ RenderNodeAnimator animator = weakThis.get();
+ if (animator != null) {
+ animator.onFinished();
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ nUnref(mNativePtr);
+ mNativePtr = 0;
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis,
+ int property, int deltaValueType, float deltaValue);
+ private static native void nSetDuration(long nativePtr, int duration);
+ private static native void nUnref(long nativePtr);
+}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 924c331..eaec8ab 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -50,7 +50,7 @@
public class ThreadedRenderer extends HardwareRenderer {
private static final String LOGTAG = "ThreadedRenderer";
- private static final Rect NULL_RECT = new Rect(-1, -1, -1, -1);
+ private static final Rect NULL_RECT = new Rect();
private int mWidth, mHeight;
private long mNativeProxy;
@@ -58,9 +58,10 @@
private RenderNode mRootNode;
ThreadedRenderer(boolean translucent) {
- mNativeProxy = nCreateProxy(translucent);
- mRootNode = RenderNode.create("RootNode");
+ long rootNodePtr = nCreateRootRenderNode();
+ mRootNode = RenderNode.adopt(rootNodePtr);
mRootNode.setClipToBounds(false);
+ mNativeProxy = nCreateProxy(translucent, rootNodePtr);
}
@Override
@@ -202,8 +203,7 @@
if (dirty == null) {
dirty = NULL_RECT;
}
- nDrawDisplayList(mNativeProxy, mRootNode.getNativeDisplayList(),
- dirty.left, dirty.top, dirty.right, dirty.bottom);
+ nSyncAndDrawFrame(mNativeProxy, dirty.left, dirty.top, dirty.right, dirty.bottom);
}
@Override
@@ -217,7 +217,7 @@
}
@Override
- public void invokeFunctor(long functor, boolean waitForCompletion) {
+ void invokeFunctor(long functor, boolean waitForCompletion) {
nInvokeFunctor(mNativeProxy, functor, waitForCompletion);
}
@@ -293,7 +293,8 @@
/** @hide */
public static native void postToRenderThread(Runnable runnable);
- private static native long nCreateProxy(boolean translucent);
+ private static native long nCreateRootRenderNode();
+ private static native long nCreateProxy(boolean translucent, long rootRenderNode);
private static native void nDeleteProxy(long nativeProxy);
private static native boolean nInitialize(long nativeProxy, Surface window);
@@ -302,7 +303,7 @@
private static native void nSetup(long nativeProxy, int width, int height);
private static native void nSetDisplayListData(long nativeProxy, long displayList,
long newData);
- private static native void nDrawDisplayList(long nativeProxy, long displayList,
+ private static native void nSyncAndDrawFrame(long nativeProxy,
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
private static native void nRunWithGlContext(long nativeProxy, Runnable runnable);
private static native void nDestroyCanvasAndSurface(long nativeProxy);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 84d1328..85e3b3d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2375,24 +2375,19 @@
static final int PFLAG3_CALLED_SUPER = 0x10;
/**
- * Flag indicating that an view will be clipped to its outline.
- */
- static final int PFLAG3_CLIP_TO_OUTLINE = 0x20;
-
- /**
* Flag indicating that a view's outline has been specifically defined.
*/
- static final int PFLAG3_OUTLINE_DEFINED = 0x40;
+ static final int PFLAG3_OUTLINE_DEFINED = 0x20;
/**
* Flag indicating that we're in the process of applying window insets.
*/
- static final int PFLAG3_APPLYING_INSETS = 0x80;
+ static final int PFLAG3_APPLYING_INSETS = 0x40;
/**
* Flag indicating that we're in the process of fitting system windows using the old method.
*/
- static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x100;
+ static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x80;
/**
* Flag indicating that nested scrolling is enabled for this view.
@@ -3258,9 +3253,7 @@
/**
* Stores the outline of the view, passed down to the DisplayList level for
- * defining shadow shape and clipping.
- *
- * TODO: once RenderNode is long-lived, remove this and rely on native copy.
+ * defining shadow shape.
*/
private Outline mOutline;
@@ -3656,6 +3649,7 @@
float tx = 0;
float ty = 0;
float tz = 0;
+ float elevation = 0;
float rotation = 0;
float rotationX = 0;
float rotationY = 0;
@@ -3739,6 +3733,10 @@
tz = a.getDimensionPixelOffset(attr, 0);
transformSet = true;
break;
+ case com.android.internal.R.styleable.View_elevation:
+ elevation = a.getDimensionPixelOffset(attr, 0);
+ transformSet = true;
+ break;
case com.android.internal.R.styleable.View_rotation:
rotation = a.getFloat(attr, 0);
transformSet = true;
@@ -4087,6 +4085,7 @@
setTranslationX(tx);
setTranslationY(ty);
setTranslationZ(tz);
+ setElevation(elevation);
setRotation(rotation);
setRotationX(rotationX);
setRotationY(rotationY);
@@ -10442,6 +10441,48 @@
setTranslationY(y - mTop);
}
+ /**
+ * The visual z position of this view, in pixels. This is equivalent to the
+ * {@link #setTranslationZ(float) translationZ} property plus the current
+ * {@link #getElevation() elevation} property.
+ *
+ * @return The visual z position of this view, in pixels.
+ */
+ @ViewDebug.ExportedProperty(category = "drawing")
+ public float getZ() {
+ return getElevation() + getTranslationZ();
+ }
+
+ /**
+ * Sets the visual z position of this view, in pixels. This is equivalent to setting the
+ * {@link #setTranslationZ(float) translationZ} property to be the difference between
+ * the x value passed in and the current {@link #getElevation() elevation} property.
+ *
+ * @param z The visual z position of this view, in pixels.
+ */
+ public void setZ(float z) {
+ setTranslationZ(z - getElevation());
+ }
+
+ @ViewDebug.ExportedProperty(category = "drawing")
+ public float getElevation() {
+ return mRenderNode.getElevation();
+ }
+
+ /**
+ * Sets the base depth location of this view.
+ *
+ * @attr ref android.R.styleable#View_elevation
+ */
+ public void setElevation(float elevation) {
+ if (elevation != getElevation()) {
+ invalidateViewProperty(true, false);
+ mRenderNode.setElevation(elevation);
+ invalidateViewProperty(false, true);
+
+ invalidateParentIfNeededAndWasQuickRejected();
+ }
+ }
/**
* The horizontal location of this view relative to its {@link #getLeft() left} position.
@@ -10509,9 +10550,9 @@
}
/**
- * The depth location of this view relative to its parent.
+ * The depth location of this view relative to its {@link #getElevation() elevation}.
*
- * @return The depth of this view relative to its parent.
+ * @return The depth of this view relative to its elevation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
public float getTranslationZ() {
@@ -10519,7 +10560,7 @@
}
/**
- * Sets the depth location of this view relative to its parent.
+ * Sets the depth location of this view relative to its {@link #getElevation() elevation}.
*
* @attr ref android.R.styleable#View_translationZ
*/
@@ -10572,16 +10613,13 @@
/**
* Sets the outline of the view, which defines the shape of the shadow it
- * casts, and can used for clipping.
+ * casts.
* <p>
* If the outline is not set or is null, shadows will be cast from the
- * bounds of the View, and clipToOutline will be ignored.
+ * bounds of the View.
*
* @param outline The new outline of the view.
* Must be {@link android.graphics.Outline#isValid() valid.}
- *
- * @see #getClipToOutline()
- * @see #setClipToOutline(boolean)
*/
public void setOutline(@Nullable Outline outline) {
if (outline != null && !outline.isValid()) {
@@ -10602,39 +10640,27 @@
mRenderNode.setOutline(mOutline);
}
- /**
- * Returns whether the outline of the View will be used for clipping.
- *
- * @see #setOutline(Outline)
- */
- public final boolean getClipToOutline() {
- return ((mPrivateFlags3 & PFLAG3_CLIP_TO_OUTLINE) != 0);
- }
+ // TODO: remove
+ public final boolean getClipToOutline() { return false; }
+ public void setClipToOutline(boolean clipToOutline) {}
- /**
- * Sets whether the outline of the View will be used for clipping.
- * <p>
- * The current implementation of outline clipping uses
- * {@link Canvas#clipPath(Path) path clipping},
- * and thus does not support anti-aliasing, and is expensive in terms of
- * graphics performance. Therefore, it is strongly recommended that this
- * property only be set temporarily, as in an animation. For the same
- * reasons, there is no parallel XML attribute for this property.
- * <p>
- * If the outline of the view is not set or is empty, no clipping will be
- * performed.
- *
- * @see #setOutline(Outline)
- */
- public void setClipToOutline(boolean clipToOutline) {
- // TODO : Add a fast invalidation here.
- if (getClipToOutline() != clipToOutline) {
- if (clipToOutline) {
- mPrivateFlags3 |= PFLAG3_CLIP_TO_OUTLINE;
+ private void queryOutlineFromBackgroundIfUndefined() {
+ if ((mPrivateFlags3 & PFLAG3_OUTLINE_DEFINED) == 0) {
+ // Outline not currently defined, query from background
+ if (mOutline == null) {
+ mOutline = new Outline();
} else {
- mPrivateFlags3 &= ~PFLAG3_CLIP_TO_OUTLINE;
+ //invalidate outline, to ensure background calculates it
+ mOutline.set(null);
}
- mRenderNode.setClipToOutline(clipToOutline);
+ if (mBackground.getOutline(mOutline)) {
+ if (!mOutline.isValid()) {
+ throw new IllegalStateException("Background drawable failed to build outline");
+ }
+ mRenderNode.setOutline(mOutline);
+ } else {
+ mRenderNode.setOutline(null);
+ }
}
}
@@ -11207,7 +11233,7 @@
}
// Damage the entire IsolatedZVolume recieving this view's shadow.
- if (isHardwareAccelerated() && getTranslationZ() != 0) {
+ if (isHardwareAccelerated() && getZ() != 0) {
damageShadowReceiver();
}
}
@@ -11283,7 +11309,7 @@
} else {
damageInParent();
}
- if (isHardwareAccelerated() && invalidateParent && getTranslationZ() != 0) {
+ if (isHardwareAccelerated() && invalidateParent && getZ() != 0) {
damageShadowReceiver();
}
}
@@ -12784,6 +12810,10 @@
mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
+ if (mBackground != null) {
+ mBackground.clearHotspots();
+ }
+
removeUnsetPressCallback();
removeLongPressCallback();
removePerformClickCallback();
@@ -14893,11 +14923,7 @@
if (mBackgroundSizeChanged) {
background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
- if ((mPrivateFlags3 & PFLAG3_OUTLINE_DEFINED) == 0) {
- // Outline not currently define, query from background
- mOutline = background.getOutline();
- mRenderNode.setOutline(mOutline);
- }
+ queryOutlineFromBackgroundIfUndefined();
}
// Attempt to use a display list if requested.
@@ -15299,7 +15325,7 @@
* @param drawable the drawable to invalidate
*/
@Override
- public void invalidateDrawable(Drawable drawable) {
+ public void invalidateDrawable(@NonNull Drawable drawable) {
if (verifyDrawable(drawable)) {
final Rect dirty = drawable.getDirtyBounds();
final int scrollX = mScrollX;
@@ -15307,6 +15333,10 @@
invalidate(dirty.left + scrollX, dirty.top + scrollY,
dirty.right + scrollX, dirty.bottom + scrollY);
+
+ if (drawable == mBackground) {
+ queryOutlineFromBackgroundIfUndefined();
+ }
}
}
@@ -18004,7 +18034,7 @@
*
* <p>If <code>startNestedScroll</code> returns true, a cooperative parent was found.
* If it returns false the caller may ignore the rest of this contract until the next scroll.
- * </p>
+ * Calling startNestedScroll while a nested scroll is already in progress will return true.</p>
*
* <p>At each incremental step of the scroll the caller should invoke
* {@link #dispatchNestedPreScroll(int, int, int[], int[]) dispatchNestedPreScroll}
@@ -18028,6 +18058,10 @@
* @see #dispatchNestedScroll(int, int, int, int, int[])
*/
public boolean startNestedScroll(int axes) {
+ if (hasNestedScrollingParent()) {
+ // Already in progress
+ return true;
+ }
if (isNestedScrollingEnabled()) {
ViewParent p = getParent();
View child = this;
@@ -18830,6 +18864,22 @@
};
/**
+ * A Property wrapper around the <code>z</code> functionality handled by the
+ * {@link View#setZ(float)} and {@link View#getZ()} methods.
+ */
+ public static final Property<View, Float> Z = new FloatProperty<View>("z") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setZ(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getZ();
+ }
+ };
+
+ /**
* A Property wrapper around the <code>rotation</code> functionality handled by the
* {@link View#setRotation(float)} and {@link View#getRotation()} methods.
*/
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ad76145..8865ab4 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2342,6 +2342,7 @@
if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
+ stopNestedScroll();
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 6b21451..11d2622 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -133,21 +133,22 @@
* Constants used to associate a property being requested and the mechanism used to set
* the property (this class calls directly into View to set the properties in question).
*/
- private static final int NONE = 0x0000;
- private static final int TRANSLATION_X = 0x0001;
- private static final int TRANSLATION_Y = 0x0002;
- private static final int TRANSLATION_Z = 0x0004;
- private static final int SCALE_X = 0x0008;
- private static final int SCALE_Y = 0x0010;
- private static final int ROTATION = 0x0020;
- private static final int ROTATION_X = 0x0040;
- private static final int ROTATION_Y = 0x0080;
- private static final int X = 0x0100;
- private static final int Y = 0x0200;
- private static final int ALPHA = 0x0400;
+ static final int NONE = 0x0000;
+ static final int TRANSLATION_X = 0x0001;
+ static final int TRANSLATION_Y = 0x0002;
+ static final int TRANSLATION_Z = 0x0004;
+ static final int SCALE_X = 0x0008;
+ static final int SCALE_Y = 0x0010;
+ static final int ROTATION = 0x0020;
+ static final int ROTATION_X = 0x0040;
+ static final int ROTATION_Y = 0x0080;
+ static final int X = 0x0100;
+ static final int Y = 0x0200;
+ static final int Z = 0x0400;
+ static final int ALPHA = 0x0800;
private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | TRANSLATION_Z |
- SCALE_X | SCALE_Y | ROTATION | ROTATION_X | ROTATION_Y | X | Y;
+ SCALE_X | SCALE_Y | ROTATION | ROTATION_X | ROTATION_Y | X | Y | Z;
/**
* The mechanism by which the user can request several properties that are then animated
@@ -470,6 +471,32 @@
}
/**
+ * This method will cause the View's <code>z</code> property to be animated to the
+ * specified value. Animations already running on the property will be canceled.
+ *
+ * @param value The value to be animated to.
+ * @see View#setZ(float)
+ * @return This object, allowing calls to methods in this class to be chained.
+ */
+ public ViewPropertyAnimator z(float value) {
+ animateProperty(Z, value);
+ return this;
+ }
+
+ /**
+ * This method will cause the View's <code>z</code> property to be animated by the
+ * specified value. Animations already running on the property will be canceled.
+ *
+ * @param value The amount to be animated by, as an offset from the current value.
+ * @see View#setZ(float)
+ * @return This object, allowing calls to methods in this class to be chained.
+ */
+ public ViewPropertyAnimator zBy(float value) {
+ animatePropertyBy(Z, value);
+ return this;
+ }
+
+ /**
* This method will cause the View's <code>rotation</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
@@ -957,6 +984,9 @@
case Y:
renderNode.setTranslationY(value - mView.mTop);
break;
+ case Z:
+ renderNode.setTranslationZ(value - renderNode.getElevation());
+ break;
case ALPHA:
info.mAlpha = value;
renderNode.setAlpha(value);
@@ -993,6 +1023,8 @@
return mView.mLeft + node.getTranslationX();
case Y:
return mView.mTop + node.getTranslationY();
+ case Z:
+ return node.getElevation() + node.getTranslationZ();
case ALPHA:
return mView.mTransformationInfo.mAlpha;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 246905d..14e422c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -678,6 +678,14 @@
}
}
+ public boolean invokeFunctor(long functor, boolean waitForCompletion) {
+ if (mAttachInfo.mHardwareRenderer == null || !mAttachInfo.mHardwareRenderer.isEnabled()) {
+ return false;
+ }
+ mAttachInfo.mHardwareRenderer.invokeFunctor(functor, waitForCompletion);
+ return true;
+ }
+
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
mAttachInfo.mHardwareAccelerationRequested = false;
@@ -3204,8 +3212,11 @@
doDie();
break;
case MSG_DISPATCH_INPUT_EVENT: {
- InputEvent event = (InputEvent)msg.obj;
- enqueueInputEvent(event, null, 0, true);
+ SomeArgs args = (SomeArgs)msg.obj;
+ InputEvent event = (InputEvent)args.arg1;
+ InputEventReceiver receiver = (InputEventReceiver)args.arg2;
+ enqueueInputEvent(event, receiver, 0, true);
+ args.recycle();
} break;
case MSG_DISPATCH_KEY_FROM_IME: {
if (LOCAL_LOGV) Log.v(
@@ -5787,7 +5798,14 @@
}
public void dispatchInputEvent(InputEvent event) {
- Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, event);
+ dispatchInputEvent(event, null);
+ }
+
+ public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = event;
+ args.arg2 = receiver;
+ Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index c9be54d..c524611 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -76,14 +76,7 @@
public interface WindowManagerPolicy {
// Policy flags. These flags are also defined in frameworks/base/include/ui/Input.h.
public final static int FLAG_WAKE = 0x00000001;
- public final static int FLAG_WAKE_DROPPED = 0x00000002;
- public final static int FLAG_SHIFT = 0x00000004;
- public final static int FLAG_CAPS_LOCK = 0x00000008;
- public final static int FLAG_ALT = 0x00000010;
- public final static int FLAG_ALT_GR = 0x00000020;
- public final static int FLAG_MENU = 0x00000040;
- public final static int FLAG_LAUNCHER = 0x00000080;
- public final static int FLAG_VIRTUAL = 0x00000100;
+ public final static int FLAG_VIRTUAL = 0x00000002;
public final static int FLAG_INJECTED = 0x01000000;
public final static int FLAG_TRUSTED = 0x02000000;
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index f8160c8..bc2d7ec 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -259,7 +259,7 @@
public InputMethodInfo(String packageName, String className,
CharSequence label, String settingsActivity) {
this(buildDummyResolveInfo(packageName, className, label), false, settingsActivity, null,
- 0, false);
+ 0, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */);
}
/**
@@ -269,6 +269,17 @@
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
boolean forceDefault) {
+ this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId,
+ forceDefault, true /* supportsSwitchingToNextInputMethod */);
+ }
+
+ /**
+ * Temporary API for creating a built-in input method for test.
+ * @hide
+ */
+ public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
+ String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
+ boolean forceDefault, boolean supportsSwitchingToNextInputMethod) {
final ServiceInfo si = ri.serviceInfo;
mService = ri;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -277,7 +288,7 @@
mIsAuxIme = isAuxIme;
mSubtypes = new InputMethodSubtypeArray(subtypes);
mForceDefault = forceDefault;
- mSupportsSwitchingToNextInputMethod = true;
+ mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
}
private static ResolveInfo buildDummyResolveInfo(String packageName, String className,
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e3bd9fd..0227873 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -317,6 +317,10 @@
int mCursorSelEnd;
int mCursorCandStart;
int mCursorCandEnd;
+ /**
+ * The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}.
+ */
+ private final int[] mViewTopLeft = new int[2];
// -----------------------------------------------------------
@@ -1487,12 +1491,26 @@
return false;
}
synchronized (mH) {
- return mCursorAnchorMonitorMode ==
- InputMethodService.CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT;
+ return (mCursorAnchorMonitorMode &
+ InputMethodService.CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT) != 0;
}
}
/**
+ * Returns true if the current input method wants to receive the cursor rectangle in
+ * screen coordinates rather than local coordinates in the attached view.
+ *
+ * @hide
+ */
+ public boolean usesScreenCoordinatesForCursorLocked() {
+ // {@link InputMethodService#CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT} also means
+ // that {@link InputMethodService#onUpdateCursor} should provide the cursor rectangle
+ // in screen coordinates rather than local coordinates.
+ return (mCursorAnchorMonitorMode &
+ InputMethodService.CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT) != 0;
+ }
+
+ /**
* Set cursor/anchor monitor mode via {@link com.android.server.InputMethodManagerService}.
* This is an internal method for {@link android.inputmethodservice.InputMethodService} and
* should never be used from IMEs and applications.
@@ -1518,15 +1536,18 @@
|| mCurrentTextBoxAttribute == null || mCurMethod == null) {
return;
}
-
mTmpCursorRect.set(left, top, right, bottom);
if (!mCursorRect.equals(mTmpCursorRect)) {
if (DEBUG) Log.d(TAG, "updateCursor");
try {
if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
- mCurMethod.updateCursor(mTmpCursorRect);
mCursorRect.set(mTmpCursorRect);
+ if (usesScreenCoordinatesForCursorLocked()) {
+ view.getLocationOnScreen(mViewTopLeft);
+ mTmpCursorRect.offset(mViewTopLeft[0], mViewTopLeft[1]);
+ }
+ mCurMethod.updateCursor(mTmpCursorRect);
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index cf9539e..efb246a 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -701,7 +701,7 @@
*/
@Deprecated
public static void enablePlatformNotifications() {
- getFactory().getStatics().setPlatformNotificationsEnabled(true);
+ // noop
}
/**
@@ -713,7 +713,7 @@
*/
@Deprecated
public static void disablePlatformNotifications() {
- getFactory().getStatics().setPlatformNotificationsEnabled(false);
+ // noop
}
/**
@@ -1482,6 +1482,7 @@
* {@link KeyChain.ACTION_STORAGE_CHANGED}
*
* @param resultCallback A callback to be invoked when client certs are cleared.
+ * The embedder can pass null if not interested in the callback.
*
* TODO(sgurun) unhide
* @hide
@@ -1609,6 +1610,8 @@
* @return the address, or if no address is found, null
*/
public static String findAddress(String addr) {
+ // TODO: Rewrite this in Java so it is not needed to start up chromium
+ // Could also be deprecated
return getFactory().getStatics().findAddress(addr);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 301317e..becda67 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -543,7 +543,7 @@
/**
* The last CheckForTap runnable we posted, if any
*/
- private Runnable mPendingCheckForTap;
+ private CheckForTap mPendingCheckForTap;
/**
* The last CheckForKeyLongPress runnable we posted, if any
@@ -2126,7 +2126,9 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
+
mInLayout = true;
+
final int childCount = getChildCount();
if (changed) {
for (int i = 0; i < childCount; i++) {
@@ -3185,7 +3187,10 @@
return INVALID_ROW_ID;
}
- final class CheckForTap implements Runnable {
+ private final class CheckForTap implements Runnable {
+ float x;
+ float y;
+
@Override
public void run() {
if (mTouchMode == TOUCH_MODE_DOWN) {
@@ -3205,7 +3210,7 @@
final boolean longClickable = isLongClickable();
if (mSelector != null) {
- Drawable d = mSelector.getCurrent();
+ final Drawable d = mSelector.getCurrent();
if (d != null && d instanceof TransitionDrawable) {
if (longClickable) {
((TransitionDrawable) d).startTransition(longPressTimeout);
@@ -3213,6 +3218,9 @@
((TransitionDrawable) d).resetTransition();
}
}
+ if (d.supportsHotspots()) {
+ d.setHotspot(R.attr.state_pressed, x, y);
+ }
}
if (longClickable) {
@@ -3596,6 +3604,8 @@
mPendingCheckForTap = new CheckForTap();
}
+ mPendingCheckForTap.x = ev.getX();
+ mPendingCheckForTap.y = ev.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
}
}
@@ -3705,6 +3715,9 @@
if (d != null && d instanceof TransitionDrawable) {
((TransitionDrawable) d).resetTransition();
}
+ if (mSelector.supportsHotspots()) {
+ mSelector.setHotspot(R.attr.state_pressed, x, ev.getY());
+ }
}
if (mTouchModeReset != null) {
removeCallbacks(mTouchModeReset);
@@ -3716,6 +3729,9 @@
mTouchMode = TOUCH_MODE_REST;
child.setPressed(false);
setPressed(false);
+ if (mSelector != null && mSelector.supportsHotspots()) {
+ mSelector.removeHotspot(R.attr.state_pressed);
+ }
if (!mDataChanged && !mIsDetaching && isAttachedToWindow()) {
performClick.run();
}
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index 74b41c9..0c31496 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -256,6 +256,16 @@
}
}
+ /**
+ * Assigns the drawable that is to be drawn on top of the assigned contact photo.
+ *
+ * @param overlay Drawable to be drawn over the assigned contact photo. Must have a non-zero
+ * instrinsic width and height.
+ */
+ public void setOverlay(Drawable overlay) {
+ mOverlay = overlay;
+ }
+
private void onContactUriChanged() {
setEnabled(isAssigned());
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 34a6a40..7e8f6b4 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -553,6 +553,7 @@
if (mIsBeingDragged && mScrollStrictSpan == null) {
mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
}
+ startNestedScroll(SCROLL_AXIS_VERTICAL);
break;
}
@@ -565,6 +566,7 @@
if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
postInvalidateOnAnimation();
}
+ stopNestedScroll();
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 7640749..4726da7 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -102,7 +102,7 @@
private ScrollView mScrollView;
- private int mIconId = -1;
+ private int mIconId = 0;
private Drawable mIcon;
@@ -337,25 +337,39 @@
}
/**
- * Set resId to 0 if you don't want an icon.
- * @param resId the resourceId of the drawable to use as the icon or 0
- * if you don't want an icon.
+ * Specifies the icon to display next to the alert title.
+ *
+ * @param resId the resource identifier of the drawable to use as the icon,
+ * or 0 for no icon
*/
public void setIcon(int resId) {
+ mIcon = null;
mIconId = resId;
+
if (mIconView != null) {
- if (resId > 0) {
+ if (resId != 0) {
mIconView.setImageResource(mIconId);
- } else if (resId == 0) {
+ } else {
mIconView.setVisibility(View.GONE);
}
}
}
-
+
+ /**
+ * Specifies the icon to display next to the alert title.
+ *
+ * @param icon the drawable to use as the icon or null for no icon
+ */
public void setIcon(Drawable icon) {
mIcon = icon;
- if ((mIconView != null) && (mIcon != null)) {
- mIconView.setImageDrawable(icon);
+ mIconId = 0;
+
+ if (mIconView != null) {
+ if (icon != null) {
+ mIconView.setImageDrawable(icon);
+ } else {
+ mIconView.setVisibility(View.GONE);
+ }
}
}
@@ -485,28 +499,24 @@
View titleTemplate = mWindow.findViewById(R.id.title_template);
titleTemplate.setVisibility(View.GONE);
} else {
- final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
-
mIconView = (ImageView) mWindow.findViewById(R.id.icon);
- if (hasTextTitle) {
- /* Display the title if a title is supplied, else hide it */
- mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
+ final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
+ if (hasTextTitle) {
+ // Display the title if a title is supplied, else hide it.
+ mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
mTitleView.setText(mTitle);
-
- /* Do this last so that if the user has supplied any
- * icons we use them instead of the default ones. If the
- * user has specified 0 then make it disappear.
- */
- if (mIconId > 0) {
+
+ // Do this last so that if the user has supplied any icons we
+ // use them instead of the default ones. If the user has
+ // specified 0 then make it disappear.
+ if (mIconId != 0) {
mIconView.setImageResource(mIconId);
} else if (mIcon != null) {
mIconView.setImageDrawable(mIcon);
- } else if (mIconId == 0) {
-
- /* Apply the padding from the icon to ensure the
- * title is aligned correctly.
- */
+ } else {
+ // Apply the padding from the icon to ensure the title is
+ // aligned correctly.
mTitleView.setPadding(mIconView.getPaddingLeft(),
mIconView.getPaddingTop(),
mIconView.getPaddingRight(),
@@ -514,9 +524,8 @@
mIconView.setVisibility(View.GONE);
}
} else {
-
// Hide the title template
- View titleTemplate = mWindow.findViewById(R.id.title_template);
+ final View titleTemplate = mWindow.findViewById(R.id.title_template);
titleTemplate.setVisibility(View.GONE);
mIconView.setVisibility(View.GONE);
topPanel.setVisibility(View.GONE);
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index fc89b31..1bb577b 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -31,6 +31,14 @@
// Remaining methods are only used in Java.
byte[] getStatistics();
+ // Return the computed amount of time remaining on battery, in milliseconds.
+ // Returns -1 if nothing could be computed.
+ long computeBatteryTimeRemaining();
+
+ // Return the computed amount of time remaining to fully charge, in milliseconds.
+ // Returns -1 if nothing could be computed.
+ long computeChargeTimeRemaining();
+
void addIsolatedUid(int isolatedUid, int appUid);
void removeIsolatedUid(int isolatedUid, int appUid);
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
new file mode 100644
index 0000000..3219ddd
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractor;
+import android.service.voice.IVoiceInteractionService;
+import android.service.voice.IVoiceInteractionSession;
+
+interface IVoiceInteractionManagerService {
+ void startVoiceActivity(in Intent intent, String resolvedType, IVoiceInteractionService service,
+ in Bundle sessionArgs);
+ int deliverNewSession(IBinder token, IVoiceInteractionSession session,
+ IVoiceInteractor interactor);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractor.aidl b/core/java/com/android/internal/app/IVoiceInteractor.aidl
new file mode 100644
index 0000000..737906a
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractor.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractorCallback;
+import com.android.internal.app.IVoiceInteractorRequest;
+
+/**
+ * IPC interface for an application to perform calls through a VoiceInteractor.
+ */
+interface IVoiceInteractor {
+ IVoiceInteractorRequest startConfirmation(String callingPackage,
+ IVoiceInteractorCallback callback, String prompt, in Bundle extras);
+ IVoiceInteractorRequest startCommand(String callingPackage,
+ IVoiceInteractorCallback callback, String command, in Bundle extras);
+ boolean[] supportsCommands(String callingPackage, in String[] commands);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
new file mode 100644
index 0000000..c6f93e1
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractorRequest;
+
+/**
+ * IPC interface for an application to receive callbacks from the voice system.
+ */
+oneway interface IVoiceInteractorCallback {
+ void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed,
+ in Bundle result);
+ void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, in Bundle result);
+ void deliverCancel(IVoiceInteractorRequest request);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java b/core/java/com/android/internal/app/IVoiceInteractorRequest.aidl
similarity index 69%
copy from packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
copy to core/java/com/android/internal/app/IVoiceInteractorRequest.aidl
index 0377123..ce2902d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnSizeChangedListener.java
+++ b/core/java/com/android/internal/app/IVoiceInteractorRequest.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.internal.app;
-import android.view.View;
-
-public interface OnSizeChangedListener {
- void onSizeChanged(View view, int w, int h, int oldw, int oldh);
+/**
+ * IPC interface identifying a request from an application calling through an IVoiceInteractor.
+ */
+interface IVoiceInteractorRequest {
+ void cancel();
}
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
new file mode 100644
index 0000000..2f74372
--- /dev/null
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.Activity;
+import android.app.AppGlobals;
+import android.os.Bundle;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.app.ActivityManagerNative;
+import android.os.RemoteException;
+import android.util.Slog;
+import java.util.List;
+import java.util.Set;
+
+
+
+
+/*
+ * This is used in conjunction with DevicePolicyManager.setForwardingIntents to enable intents to be
+ * passed in and out of a managed profile.
+ */
+
+public class IntentForwarderActivity extends Activity {
+
+ public static String TAG = "IntentForwarderActivity";
+
+ public static String FORWARD_INTENT_TO_USER_OWNER
+ = "com.android.internal.app.ForwardIntentToUserOwner";
+
+ public static String FORWARD_INTENT_TO_MANAGED_PROFILE
+ = "com.android.internal.app.ForwardIntentToManagedProfile";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intentReceived = getIntent();
+
+ String className = intentReceived.getComponent().getClassName();
+ final UserHandle userDest;
+
+ if (className.equals(FORWARD_INTENT_TO_USER_OWNER)) {
+ userDest = UserHandle.OWNER;
+ } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
+ userDest = getManagedProfile();
+ } else {
+ Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
+ userDest = null;
+ }
+ if (userDest == null) { // This covers the case where there is no managed profile.
+ finish();
+ return;
+ }
+ Intent newIntent = new Intent(intentReceived);
+ newIntent.setComponent(null);
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
+ |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+ int callingUserId = getUserId();
+ IPackageManager ipm = AppGlobals.getPackageManager();
+ String resolvedType = newIntent.resolveTypeIfNeeded(getContentResolver());
+ boolean canForward = false;
+ try {
+ canForward = ipm.canForwardTo(newIntent, resolvedType, callingUserId,
+ userDest.getIdentifier());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "PackageManagerService is dead?");
+ }
+ if (canForward) {
+ startActivityAsUser(newIntent, userDest);
+ } else {
+ Slog.wtf(TAG, "the intent: " + newIntent + "cannot be forwarded from user "
+ + callingUserId + " to user " + userDest.getIdentifier());
+ }
+ finish();
+ }
+
+ /**
+ * Returns the managed profile for this device or null if there is no managed
+ * profile.
+ *
+ * TODO: Remove the assumption that there is only one managed profile
+ * on the device.
+ */
+ private UserHandle getManagedProfile() {
+ UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ List<UserInfo> relatedUsers = userManager.getProfiles(UserHandle.USER_OWNER);
+ for (UserInfo userInfo : relatedUsers) {
+ if (userInfo.isManagedProfile()) return new UserHandle(userInfo.id);
+ }
+ Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE
+ + " has been called, but there is no managed profile");
+ return null;
+ }
+}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 1bfad05..446ef55 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -26,6 +26,9 @@
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.SELinux;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
import android.util.Log;
import com.android.org.bouncycastle.util.encoders.Base64;
@@ -37,10 +40,7 @@
import java.util.ArrayList;
import java.util.Collections;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-import libcore.io.StructStat;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
/**
* Backup transport for stashing stuff into a known location on disk, and
@@ -109,7 +109,7 @@
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
if (DEBUG) {
try {
- StructStat ss = Libcore.os.fstat(data.getFileDescriptor());
+ StructStat ss = Os.fstat(data.getFileDescriptor());
Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName
+ " size=" + ss.st_size);
} catch (ErrnoException e) {
@@ -152,7 +152,7 @@
changeSet.readEntityData(buf, 0, dataSize);
if (DEBUG) {
try {
- long cur = Libcore.os.lseek(data.getFileDescriptor(), 0, SEEK_CUR);
+ long cur = Os.lseek(data.getFileDescriptor(), 0, SEEK_CUR);
Log.v(TAG, " read entity data; new pos=" + cur);
}
catch (ErrnoException e) {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index dcc0a4c..cba09d1 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -16,6 +16,7 @@
package com.android.internal.inputmethod;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings;
import android.content.Context;
@@ -119,14 +120,14 @@
}
}
- private static class InputMethodAndSubtypeCircularList {
+ private static class InputMethodAndSubtypeList {
private final Context mContext;
// Used to load label
private final PackageManager mPm;
private final String mSystemLocaleStr;
private final InputMethodSettings mSettings;
- public InputMethodAndSubtypeCircularList(Context context, InputMethodSettings settings) {
+ public InputMethodAndSubtypeList(Context context, InputMethodSettings settings) {
mContext = context;
mSettings = settings;
mPm = context.getPackageManager();
@@ -152,38 +153,6 @@
}
});
- public ImeSubtypeListItem getNextInputMethod(
- boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
- if (imi == null) {
- return null;
- }
- final List<ImeSubtypeListItem> imList =
- getSortedInputMethodAndSubtypeList();
- if (imList.size() <= 1) {
- return null;
- }
- final int N = imList.size();
- final int currentSubtypeId =
- subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
- subtype.hashCode()) : NOT_A_SUBTYPE_ID;
- for (int i = 0; i < N; ++i) {
- final ImeSubtypeListItem isli = imList.get(i);
- if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) {
- if (!onlyCurrentIme) {
- return imList.get((i + 1) % N);
- }
- for (int j = 0; j < N - 1; ++j) {
- final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
- if (candidate.mImi.equals(imi)) {
- return candidate;
- }
- }
- return null;
- }
- }
- return null;
- }
-
public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() {
return getSortedInputMethodAndSubtypeList(true, false, false);
}
@@ -247,7 +216,38 @@
private final ArrayDeque<SubtypeParams> mTypedSubtypeHistory = new ArrayDeque<SubtypeParams>();
private final Object mLock = new Object();
private final InputMethodSettings mSettings;
- private InputMethodAndSubtypeCircularList mSubtypeList;
+ private InputMethodAndSubtypeList mSubtypeList;
+
+ @VisibleForTesting
+ public static ImeSubtypeListItem getNextInputMethodImpl(List<ImeSubtypeListItem> imList,
+ boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
+ if (imi == null) {
+ return null;
+ }
+ if (imList.size() <= 1) {
+ return null;
+ }
+ final int N = imList.size();
+ final int currentSubtypeId =
+ subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
+ subtype.hashCode()) : NOT_A_SUBTYPE_ID;
+ for (int i = 0; i < N; ++i) {
+ final ImeSubtypeListItem isli = imList.get(i);
+ if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) {
+ if (!onlyCurrentIme) {
+ return imList.get((i + 1) % N);
+ }
+ for (int j = 0; j < N - 1; ++j) {
+ final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
+ if (candidate.mImi.equals(imi)) {
+ return candidate;
+ }
+ }
+ return null;
+ }
+ }
+ return null;
+ }
public InputMethodSubtypeSwitchingController(InputMethodSettings settings) {
mSettings = settings;
@@ -278,14 +278,15 @@
public void resetCircularListLocked(Context context) {
synchronized(mLock) {
- mSubtypeList = new InputMethodAndSubtypeCircularList(context, mSettings);
+ mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
}
}
public ImeSubtypeListItem getNextInputMethod(
boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
synchronized(mLock) {
- return mSubtypeList.getNextInputMethod(onlyCurrentIme, imi, subtype);
+ return getNextInputMethodImpl(mSubtypeList.getSortedInputMethodAndSubtypeList(),
+ onlyCurrentIme, imi, subtype);
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 343c507..f63fa8a 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -88,7 +88,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 103 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 104 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -319,6 +319,18 @@
int mDischargeAmountScreenOff;
int mDischargeAmountScreenOffSinceCharge;
+ static final int MAX_LEVEL_STEPS = 100;
+
+ int mLastDischargeStepLevel;
+ long mLastDischargeStepTime;
+ int mNumDischargeStepDurations;
+ final long[] mDischargeStepDurations = new long[MAX_LEVEL_STEPS];
+
+ int mLastChargeStepLevel;
+ long mLastChargeStepTime;
+ int mNumChargeStepDurations;
+ final long[] mChargeStepDurations = new long[MAX_LEVEL_STEPS];
+
long mLastWriteTime = 0; // Milliseconds
private int mBluetoothPingCount;
@@ -5721,6 +5733,10 @@
mDischargeAmountScreenOnSinceCharge = 0;
mDischargeAmountScreenOff = 0;
mDischargeAmountScreenOffSinceCharge = 0;
+ mLastDischargeStepTime = -1;
+ mNumDischargeStepDurations = 0;
+ mLastChargeStepTime = -1;
+ mNumChargeStepDurations = 0;
}
public void resetAllStatsCmdLocked() {
@@ -5885,7 +5901,10 @@
resetAllStatsLocked();
mDischargeStartLevel = level;
reset = true;
+ mNumDischargeStepDurations = 0;
}
+ mLastDischargeStepLevel = level;
+ mLastDischargeStepTime = -1;
pullPendingStateUpdatesLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
@@ -5921,6 +5940,9 @@
}
updateDischargeScreenLevelsLocked(mScreenOn, mScreenOn);
updateTimeBasesLocked(false, !mScreenOn, uptime, realtime);
+ mNumChargeStepDurations = 0;
+ mLastChargeStepLevel = level;
+ mLastChargeStepTime = -1;
}
if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
if (mFile != null) {
@@ -5944,6 +5966,24 @@
// This should probably be exposed in the API, though it's not critical
private static final int BATTERY_PLUGGED_NONE = 0;
+ private static int addLevelSteps(long[] steps, int stepCount, long lastStepTime,
+ int numStepLevels, long elapsedRealtime) {
+ if (lastStepTime >= 0 && numStepLevels > 0) {
+ long duration = elapsedRealtime - lastStepTime;
+ for (int i=0; i<numStepLevels; i++) {
+ System.arraycopy(steps, 0, steps, 1, steps.length-1);
+ long thisDuration = duration / (numStepLevels-i);
+ duration -= thisDuration;
+ steps[0] = thisDuration;
+ }
+ stepCount += numStepLevels;
+ if (stepCount > steps.length) {
+ stepCount = steps.length;
+ }
+ }
+ return stepCount;
+ }
+
public void setBatteryState(int status, int health, int plugType, int level,
int temp, int volt) {
synchronized(this) {
@@ -6021,6 +6061,23 @@
if (changed) {
addHistoryRecordLocked(elapsedRealtime, uptime);
}
+ if (onBattery) {
+ if (mLastDischargeStepLevel != level) {
+ mNumDischargeStepDurations = addLevelSteps(mDischargeStepDurations,
+ mNumDischargeStepDurations, mLastDischargeStepTime,
+ mLastDischargeStepLevel - level, elapsedRealtime);
+ mLastDischargeStepLevel = level;
+ mLastDischargeStepTime = elapsedRealtime;
+ }
+ } else {
+ if (mLastChargeStepLevel != level) {
+ mNumChargeStepDurations = addLevelSteps(mChargeStepDurations,
+ mNumChargeStepDurations, mLastChargeStepTime,
+ level - mLastChargeStepLevel, elapsedRealtime);
+ mLastChargeStepLevel = level;
+ mLastChargeStepTime = elapsedRealtime;
+ }
+ }
}
if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
// We don't record history while we are plugged in and fully charged.
@@ -6235,11 +6292,51 @@
return mOnBatteryScreenOffTimeBase.computeRealtime(curTime, which);
}
+ private long computeTimePerLevel(long[] steps, int numSteps) {
+ // For now we'll do a simple average across all steps.
+ if (numSteps <= 0) {
+ return -1;
+ }
+ long total = 0;
+ for (int i=0; i<numSteps; i++) {
+ total += steps[i];
+ }
+ return total / numSteps;
+ /*
+ long[] buckets = new long[numSteps];
+ int numBuckets = 0;
+ int numToAverage = 4;
+ int i = 0;
+ while (i < numSteps) {
+ long totalTime = 0;
+ int num = 0;
+ for (int j=0; j<numToAverage && (i+j)<numSteps; j++) {
+ totalTime += steps[i+j];
+ num++;
+ }
+ buckets[numBuckets] = totalTime / num;
+ numBuckets++;
+ numToAverage *= 2;
+ i += num;
+ }
+ if (numBuckets < 1) {
+ return -1;
+ }
+ long averageTime = buckets[numBuckets-1];
+ for (i=numBuckets-2; i>=0; i--) {
+ averageTime = (averageTime + buckets[i]) / 2;
+ }
+ return averageTime;
+ */
+ }
+
@Override
public long computeBatteryTimeRemaining(long curTime) {
if (!mOnBattery) {
return -1;
}
+ /* Simple implementation just looks at the average discharge per level across the
+ entire sample period.
int discharge = (getLowDischargeAmountSinceCharge()+getHighDischargeAmountSinceCharge())/2;
if (discharge < 2) {
return -1;
@@ -6250,14 +6347,32 @@
}
long usPerLevel = duration/discharge;
return usPerLevel * mCurrentBatteryLevel;
+ */
+ if (mNumDischargeStepDurations < 1) {
+ return -1;
+ }
+ long msPerLevel = computeTimePerLevel(mDischargeStepDurations, mNumDischargeStepDurations);
+ if (msPerLevel <= 0) {
+ return -1;
+ }
+ return (msPerLevel * mCurrentBatteryLevel) * 1000;
+ }
+
+ public int getNumDischargeStepDurations() {
+ return mNumDischargeStepDurations;
+ }
+
+ public long[] getDischargeStepDurationsArray() {
+ return mDischargeStepDurations;
}
@Override
public long computeChargeTimeRemaining(long curTime) {
- if (true || mOnBattery) {
+ if (mOnBattery) {
// Not yet working.
return -1;
}
+ /* Broken
int curLevel = mCurrentBatteryLevel;
int plugLevel = mDischargePlugLevel;
if (plugLevel < 0 || curLevel < (plugLevel+1)) {
@@ -6269,6 +6384,23 @@
}
long usPerLevel = duration/(curLevel-plugLevel);
return usPerLevel * (100-curLevel);
+ */
+ if (mNumChargeStepDurations < 1) {
+ return -1;
+ }
+ long msPerLevel = computeTimePerLevel(mChargeStepDurations, mNumChargeStepDurations);
+ if (msPerLevel <= 0) {
+ return -1;
+ }
+ return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;
+ }
+
+ public int getNumChargeStepDurations() {
+ return mNumChargeStepDurations;
+ }
+
+ public long[] getChargeStepDurationsArray() {
+ return mChargeStepDurations;
}
long getBatteryUptimeLocked() {
@@ -6776,6 +6908,10 @@
mHighDischargeAmountSinceCharge = in.readInt();
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mNumDischargeStepDurations = in.readInt();
+ in.readLongArray(mDischargeStepDurations);
+ mNumChargeStepDurations = in.readInt();
+ in.readLongArray(mChargeStepDurations);
mStartCount++;
@@ -7030,6 +7166,10 @@
out.writeInt(getHighDischargeAmountSinceCharge());
out.writeInt(getDischargeAmountScreenOnSinceCharge());
out.writeInt(getDischargeAmountScreenOffSinceCharge());
+ out.writeInt(mNumDischargeStepDurations);
+ out.writeLongArray(mDischargeStepDurations);
+ out.writeInt(mNumChargeStepDurations);
+ out.writeLongArray(mChargeStepDurations);
mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
@@ -7344,6 +7484,10 @@
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOff = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mNumDischargeStepDurations = in.readInt();
+ in.readLongArray(mDischargeStepDurations);
+ mNumChargeStepDurations = in.readInt();
+ in.readLongArray(mChargeStepDurations);
mLastWriteTime = in.readLong();
mBluetoothPingCount = in.readInt();
@@ -7464,6 +7608,10 @@
out.writeInt(mDischargeAmountScreenOnSinceCharge);
out.writeInt(mDischargeAmountScreenOff);
out.writeInt(mDischargeAmountScreenOffSinceCharge);
+ out.writeInt(mNumDischargeStepDurations);
+ out.writeLongArray(mDischargeStepDurations);
+ out.writeInt(mNumChargeStepDurations);
+ out.writeLongArray(mChargeStepDurations);
out.writeLong(mLastWriteTime);
out.writeInt(getBluetoothPingCount());
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index d9e3ef6..40834ba 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -85,7 +85,27 @@
public void sendMessage(Message msg) {
mH.sendMessage(msg);
}
-
+
+ public SomeArgs sendMessageAndWait(Message msg) {
+ if (Looper.myLooper() == mH.getLooper()) {
+ throw new IllegalStateException("Can't wait on same thread as looper");
+ }
+ SomeArgs args = (SomeArgs)msg.obj;
+ args.mWaitState = SomeArgs.WAIT_WAITING;
+ mH.sendMessage(msg);
+ synchronized (args) {
+ while (args.mWaitState == SomeArgs.WAIT_WAITING) {
+ try {
+ args.wait();
+ } catch (InterruptedException e) {
+ return null;
+ }
+ }
+ }
+ args.mWaitState = SomeArgs.WAIT_NONE;
+ return args;
+ }
+
public Message obtainMessage(int what) {
return mH.obtainMessage(what);
}
@@ -136,6 +156,14 @@
return mH.obtainMessage(what, arg1, 0, args);
}
+ public Message obtainMessageIOOO(int what, int arg1, Object arg2, Object arg3, Object arg4) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = arg2;
+ args.arg2 = arg3;
+ args.arg3 = arg4;
+ return mH.obtainMessage(what, arg1, 0, args);
+ }
+
public Message obtainMessageOO(int what, Object arg1, Object arg2) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = arg1;
@@ -161,6 +189,17 @@
return mH.obtainMessage(what, 0, 0, args);
}
+ public Message obtainMessageOOOOO(int what, Object arg1, Object arg2,
+ Object arg3, Object arg4, Object arg5) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = arg1;
+ args.arg2 = arg2;
+ args.arg3 = arg3;
+ args.arg4 = arg4;
+ args.arg5 = arg5;
+ return mH.obtainMessage(what, 0, 0, args);
+ }
+
public Message obtainMessageIIII(int what, int arg1, int arg2,
int arg3, int arg4) {
SomeArgs args = SomeArgs.obtain();
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index 6fb72f1..7edf4cc 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -35,6 +35,11 @@
private boolean mInPool;
+ static final int WAIT_NONE = 0;
+ static final int WAIT_WAITING = 1;
+ static final int WAIT_FINISHED = 2;
+ int mWaitState = WAIT_NONE;
+
public Object arg1;
public Object arg2;
public Object arg3;
@@ -70,6 +75,9 @@
if (mInPool) {
throw new IllegalStateException("Already recycled.");
}
+ if (mWaitState != WAIT_NONE) {
+ return;
+ }
synchronized (sPoolLock) {
clear();
if (sPoolSize < MAX_POOL_SIZE) {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index c5fa0a1..54c532a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -18,8 +18,8 @@
import dalvik.system.ZygoteHooks;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
+import android.system.ErrnoException;
+import android.system.Os;
/** @hide */
public final class Zygote {
@@ -141,7 +141,7 @@
public static void execShell(String command) {
String[] args = { "/system/bin/sh", "-c", command };
try {
- Libcore.os.execv(args[0], args);
+ Os.execv(args[0], args);
} catch (ErrnoException e) {
throw new RuntimeException(e);
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 58a8e62..0c48368 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -21,6 +21,8 @@
import android.os.Process;
import android.os.SELinux;
import android.os.SystemProperties;
+import android.system.ErrnoException;
+import android.system.Os;
import android.util.Log;
import dalvik.system.PathClassLoader;
import java.io.BufferedReader;
@@ -34,9 +36,7 @@
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
-import libcore.io.Libcore;
/**
* A connection that can make spawn requests.
@@ -186,7 +186,7 @@
}
if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) {
- FileDescriptor[] pipeFds = Libcore.os.pipe();
+ FileDescriptor[] pipeFds = Os.pipe();
childPipeFd = pipeFds[1];
serverPipeFd = pipeFds[0];
ZygoteInit.setCloseOnExec(serverPipeFd, true);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index ef2908d..3ea749e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -16,8 +16,8 @@
package com.android.internal.os;
-import static libcore.io.OsConstants.S_IRWXG;
-import static libcore.io.OsConstants.S_IRWXO;
+import static android.system.OsConstants.S_IRWXG;
+import static android.system.OsConstants.S_IRWXO;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -28,14 +28,15 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.EventLog;
import android.util.Log;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
-import libcore.io.Libcore;
-import libcore.io.OsConstants;
import java.io.BufferedReader;
import java.io.FileDescriptor;
@@ -195,12 +196,12 @@
FileDescriptor fd = sServerSocket.getFileDescriptor();
sServerSocket.close();
if (fd != null) {
- Libcore.os.close(fd);
+ Os.close(fd);
}
}
} catch (IOException ex) {
Log.e(TAG, "Zygote: error closing sockets", ex);
- } catch (libcore.io.ErrnoException ex) {
+ } catch (ErrnoException ex) {
Log.e(TAG, "Zygote: error closing descriptor", ex);
}
@@ -481,7 +482,7 @@
closeServerSocket();
// set umask to 0077 so new files and directories will default to owner-only permissions.
- Libcore.os.umask(S_IRWXG | S_IRWXO);
+ Os.umask(S_IRWXG | S_IRWXO);
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
diff --git a/core/java/com/android/server/SystemService.java b/core/java/com/android/server/SystemService.java
index e374563..bf36bb1 100644
--- a/core/java/com/android/server/SystemService.java
+++ b/core/java/com/android/server/SystemService.java
@@ -123,6 +123,38 @@
public void onBootPhase(int phase) {}
/**
+ * Called when a new user is starting, for system services to initialize any per-user
+ * state they maintain for running users.
+ * @param userHandle The identifier of the user.
+ */
+ public void onStartUser(int userHandle) {}
+
+ /**
+ * Called when switching to a different foreground user, for system services that have
+ * special behavior for whichever user is currently in the foreground. This is called
+ * before any application processes are aware of the new user.
+ * @param userHandle The identifier of the user.
+ */
+ public void onSwitchUser(int userHandle) {}
+
+ /**
+ * Called when an existing user is stopping, for system services to finalize any per-user
+ * state they maintain for running users. This is called prior to sending the SHUTDOWN
+ * broadcast to the user; it is a good place to stop making use of any resources of that
+ * user (such as binding to a service running in the user).
+ * @param userHandle The identifier of the user.
+ */
+ public void onStopUser(int userHandle) {}
+
+ /**
+ * Called when an existing user is stopping, for system services to finalize any per-user
+ * state they maintain for running users. This is called after all application process
+ * teardown of the user is complete.
+ * @param userHandle The identifier of the user.
+ */
+ public void onCleanupUser(int userHandle) {}
+
+ /**
* Publish the service so it is accessible to other services and apps.
*/
protected final void publishBinderService(String name, IBinder service) {
diff --git a/core/java/com/android/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java
index eb8df0e..87a50a9 100644
--- a/core/java/com/android/server/SystemServiceManager.java
+++ b/core/java/com/android/server/SystemServiceManager.java
@@ -131,6 +131,58 @@
}
}
+ public void startUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onStartUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting start of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
+ public void switchUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onSwitchUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
+ public void stopUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onStopUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
+ public void cleanupUser(final int userHandle) {
+ final int serviceLen = mServices.size();
+ for (int i = 0; i < serviceLen; i++) {
+ final SystemService service = mServices.get(i);
+ try {
+ service.onCleanupUser(userHandle);
+ } catch (Exception ex) {
+ Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
+ + " to service " + service.getClass().getName(), ex);
+ }
+ }
+ }
+
/** Sets the safe mode flag for services to query. */
public void setSafeMode(boolean safeMode) {
mSafeMode = safeMode;
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 711f28a..ee59c8a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -61,6 +61,7 @@
android_view_MotionEvent.cpp \
android_view_PointerIcon.cpp \
android_view_RenderNode.cpp \
+ android_view_RenderNodeAnimator.cpp \
android_view_VelocityTracker.cpp \
android_text_AndroidCharacter.cpp \
android_text_AndroidBidi.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index aa635c6..f964cd2 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -120,6 +120,7 @@
extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
extern int register_android_view_RenderNode(JNIEnv* env);
+extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
extern int register_android_view_GraphicBuffer(JNIEnv* env);
extern int register_android_view_GLES20Canvas(JNIEnv* env);
extern int register_android_view_GLRenderer(JNIEnv* env);
@@ -1193,6 +1194,7 @@
REG_JNI(register_android_graphics_Graphics),
REG_JNI(register_android_view_DisplayEventReceiver),
REG_JNI(register_android_view_RenderNode),
+ REG_JNI(register_android_view_RenderNodeAnimator),
REG_JNI(register_android_view_GraphicBuffer),
REG_JNI(register_android_view_GLES20Canvas),
REG_JNI(register_android_view_GLRenderer),
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index 1ad1a8a..420a17f 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -163,42 +163,35 @@
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
obj->close();
}
-
- static void addRect__RectFI(JNIEnv* env, jobject clazz, jlong objHandle, jobject jrect, jint dirHandle) {
- SkRect rect;
- SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
- SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
- GraphicsJNI::jrectf_to_rect(env, jrect, &rect);
- obj->addRect(rect, dir);
- }
-
- static void addRect__FFFFI(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
+
+ static void addRect(JNIEnv* env, jobject clazz, jlong objHandle,
+ jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
obj->addRect(left, top, right, bottom, dir);
}
-
- static void addOval(JNIEnv* env, jobject clazz, jlong objHandle, jobject oval, jint dirHandle) {
+
+ static void addOval(JNIEnv* env, jobject clazz, jlong objHandle,
+ jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
- SkRect oval_;
- GraphicsJNI::jrectf_to_rect(env, oval, &oval_);
- obj->addOval(oval_, dir);
+ SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+ obj->addOval(oval, dir);
}
-
+
static void addCircle(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y, jfloat radius, jint dirHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
obj->addCircle(x, y, radius, dir);
}
-
+
static void addArc(JNIEnv* env, jobject clazz, jlong objHandle, jobject oval, jfloat startAngle, jfloat sweepAngle) {
SkRect oval_;
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
GraphicsJNI::jrectf_to_rect(env, oval, &oval_);
obj->addArc(oval_, startAngle, sweepAngle);
}
-
+
static void addRoundRectXY(JNIEnv* env, jobject clazz, jlong objHandle, jobject jrect,
jfloat rx, jfloat ry, jint dirHandle) {
SkRect rect;
@@ -496,9 +489,8 @@
{"native_rCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo},
{"native_arcTo","(JLandroid/graphics/RectF;FFZ)V", (void*) SkPathGlue::arcTo},
{"native_close","(J)V", (void*) SkPathGlue::close},
- {"native_addRect","(JLandroid/graphics/RectF;I)V", (void*) SkPathGlue::addRect__RectFI},
- {"native_addRect","(JFFFFI)V", (void*) SkPathGlue::addRect__FFFFI},
- {"native_addOval","(JLandroid/graphics/RectF;I)V", (void*) SkPathGlue::addOval},
+ {"native_addRect","(JFFFFI)V", (void*) SkPathGlue::addRect},
+ {"native_addOval","(JFFFFI)V", (void*) SkPathGlue::addOval},
{"native_addCircle","(JFFFI)V", (void*) SkPathGlue::addCircle},
{"native_addArc","(JLandroid/graphics/RectF;FF)V", (void*) SkPathGlue::addArc},
{"native_addRoundRect","(JLandroid/graphics/RectF;FFI)V", (void*) SkPathGlue::addRoundRectXY},
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp
index 799782d..6591d60 100644
--- a/core/jni/android/graphics/YuvToJpegEncoder.cpp
+++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp
@@ -226,16 +226,17 @@
jint* imgOffsets = env->GetIntArrayElements(offsets, NULL);
jint* imgStrides = env->GetIntArrayElements(strides, NULL);
YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides);
- if (encoder == NULL) {
- return JNI_FALSE;
+ jboolean result = JNI_FALSE;
+ if (encoder != NULL) {
+ encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality);
+ delete encoder;
+ result = JNI_TRUE;
}
- encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality);
- delete encoder;
env->ReleaseByteArrayElements(inYuv, yuv, 0);
env->ReleaseIntArrayElements(offsets, imgOffsets, 0);
env->ReleaseIntArrayElements(strides, imgStrides, 0);
- return JNI_TRUE;
+ return result;
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index c83541d..7ae21a7 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -22,6 +22,7 @@
#include <android_runtime/Log.h>
#include <utils/Log.h>
#include <input/Input.h>
+#include <ScopedUtfChars.h>
#include "android_view_KeyEvent.h"
namespace android {
@@ -102,20 +103,25 @@
return OK;
}
-static jboolean native_isSystemKey(JNIEnv* env, jobject clazz, jint keyCode) {
- return KeyEvent::isSystemKey(keyCode);
+static jstring android_view_KeyEvent_nativeKeyCodeToString(JNIEnv* env, jobject clazz,
+ jint keyCode) {
+ return env->NewStringUTF(KeyEvent::getLabel(keyCode));
}
-static jboolean native_hasDefaultAction(JNIEnv* env, jobject clazz, jint keyCode) {
- return KeyEvent::hasDefaultAction(keyCode);
+static jint android_view_KeyEvent_nativeKeyCodeFromString(JNIEnv* env, jobject clazz,
+ jstring label) {
+ ScopedUtfChars keyLabel(env, label);
+ return KeyEvent::getKeyCodeFromLabel(keyLabel.c_str());
}
// ----------------------------------------------------------------------------
static const JNINativeMethod g_methods[] = {
- { "native_isSystemKey", "(I)Z", (void*)native_isSystemKey },
- { "native_hasDefaultAction", "(I)Z", (void*)native_hasDefaultAction },
+ { "nativeKeyCodeToString", "(I)Ljava/lang/String;",
+ (void*)android_view_KeyEvent_nativeKeyCodeToString},
+ { "nativeKeyCodeFromString", "(Ljava/lang/String;)I",
+ (void*)android_view_KeyEvent_nativeKeyCodeFromString},
};
#define FIND_CLASS(var, className) \
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 76e145b..6ae02e0 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -23,6 +23,7 @@
#include <android_runtime/Log.h>
#include <utils/Log.h>
#include <input/Input.h>
+#include <ScopedUtfChars.h>
#include "android_os_Parcel.h"
#include "android_view_MotionEvent.h"
#include "android_util_Binder.h"
@@ -724,6 +725,17 @@
}
}
+static jstring android_view_MotionEvent_nativeAxisToString(JNIEnv* env, jclass clazz,
+ jint axis) {
+ return env->NewStringUTF(MotionEvent::getLabel(static_cast<int32_t>(axis)));
+}
+
+static jint android_view_MotionEvent_nativeAxisFromString(JNIEnv* env, jclass clazz,
+ jstring label) {
+ ScopedUtfChars axisLabel(env, label);
+ return static_cast<jint>(MotionEvent::getAxisFromLabel(axisLabel.c_str()));
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gMotionEventMethods[] = {
@@ -840,6 +852,10 @@
{ "nativeWriteToParcel",
"(JLandroid/os/Parcel;)V",
(void*)android_view_MotionEvent_nativeWriteToParcel },
+ { "nativeAxisToString", "(I)Ljava/lang/String;",
+ (void*)android_view_MotionEvent_nativeAxisToString },
+ { "nativeAxisFromString", "(Ljava/lang/String;)I",
+ (void*)android_view_MotionEvent_nativeAxisFromString },
};
#define FIND_CLASS(var, className) \
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 8dacfeb..4715c26 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -23,6 +23,7 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
+#include <Animator.h>
#include <DisplayListRenderer.h>
#include <RenderNode.h>
@@ -164,6 +165,12 @@
renderNode->mutateStagingProperties().setHasOverlappingRendering(hasOverlappingRendering);
}
+static void android_view_RenderNode_setElevation(JNIEnv* env,
+ jobject clazz, jlong renderNodePtr, float elevation) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ renderNode->mutateStagingProperties().setElevation(elevation);
+}
+
static void android_view_RenderNode_setTranslationX(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float tx) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
@@ -331,6 +338,12 @@
return renderNode->stagingProperties().getScaleY();
}
+static jfloat android_view_RenderNode_getElevation(JNIEnv* env,
+ jobject clazz, jlong renderNodePtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ return renderNode->stagingProperties().getElevation();
+}
+
static jfloat android_view_RenderNode_getTranslationX(JNIEnv* env,
jobject clazz, jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
@@ -426,6 +439,25 @@
return renderNode->stagingProperties().getPivotY();
}
+// ----------------------------------------------------------------------------
+// RenderProperties - Animations
+// ----------------------------------------------------------------------------
+
+static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz,
+ jlong renderNodePtr, jlong animatorPtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
+ renderNode->addAnimator(animator);
+}
+
+static void android_view_RenderNode_removeAnimator(JNIEnv* env, jobject clazz,
+ jlong renderNodePtr, jlong animatorPtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
+ renderNode->removeAnimator(animator);
+}
+
+
#endif // USE_OPENGL_RENDERER
// ----------------------------------------------------------------------------
@@ -457,6 +489,7 @@
{ "nSetAlpha", "(JF)V", (void*) android_view_RenderNode_setAlpha },
{ "nSetHasOverlappingRendering", "(JZ)V",
(void*) android_view_RenderNode_setHasOverlappingRendering },
+ { "nSetElevation", "(JF)V", (void*) android_view_RenderNode_setElevation },
{ "nSetTranslationX", "(JF)V", (void*) android_view_RenderNode_setTranslationX },
{ "nSetTranslationY", "(JF)V", (void*) android_view_RenderNode_setTranslationY },
{ "nSetTranslationZ", "(JF)V", (void*) android_view_RenderNode_setTranslationZ },
@@ -485,6 +518,7 @@
{ "nGetCameraDistance", "(J)F", (void*) android_view_RenderNode_getCameraDistance },
{ "nGetScaleX", "(J)F", (void*) android_view_RenderNode_getScaleX },
{ "nGetScaleY", "(J)F", (void*) android_view_RenderNode_getScaleY },
+ { "nGetElevation", "(J)F", (void*) android_view_RenderNode_getElevation },
{ "nGetTranslationX", "(J)F", (void*) android_view_RenderNode_getTranslationX },
{ "nGetTranslationY", "(J)F", (void*) android_view_RenderNode_getTranslationY },
{ "nGetTranslationZ", "(J)F", (void*) android_view_RenderNode_getTranslationZ },
@@ -499,6 +533,9 @@
{ "nGetPivotX", "(J)F", (void*) android_view_RenderNode_getPivotX },
{ "nGetPivotY", "(J)F", (void*) android_view_RenderNode_getPivotY },
+
+ { "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator },
+ { "nRemoveAnimator", "(JJ)V", (void*) android_view_RenderNode_removeAnimator },
#endif
};
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
new file mode 100644
index 0000000..b92c992
--- /dev/null
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "android_view_RenderNodeAnimator.h"
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <Animator.h>
+#include <Interpolator.h>
+#include <RenderProperties.h>
+
+namespace android {
+
+using namespace uirenderer;
+
+static struct {
+ jclass clazz;
+
+ jmethodID callOnFinished;
+} gRenderNodeAnimatorClassInfo;
+
+#ifdef USE_OPENGL_RENDERER
+
+static JNIEnv* getEnv(JavaVM* vm) {
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ return 0;
+ }
+ return env;
+}
+
+RenderNodeAnimator::RenderNodeAnimator(JNIEnv* env, jobject weakThis,
+ RenderProperty property, DeltaValueType deltaType, float delta)
+ : RenderPropertyAnimator(property, deltaType, delta) {
+ mWeakThis = env->NewGlobalRef(weakThis);
+ env->GetJavaVM(&mJvm);
+}
+
+RenderNodeAnimator::~RenderNodeAnimator() {
+ JNIEnv* env = getEnv(mJvm);
+ env->DeleteGlobalRef(mWeakThis);
+ mWeakThis = NULL;
+}
+
+void RenderNodeAnimator::callOnFinished() {
+ JNIEnv* env = getEnv(mJvm);
+ env->CallStaticVoidMethod(
+ gRenderNodeAnimatorClassInfo.clazz,
+ gRenderNodeAnimatorClassInfo.callOnFinished,
+ mWeakThis);
+}
+
+static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis,
+ jint property, jint deltaType, jfloat deltaValue) {
+ LOG_ALWAYS_FATAL_IF(property < 0 || property > RenderNodeAnimator::ALPHA,
+ "Invalid property %d", property);
+ LOG_ALWAYS_FATAL_IF(deltaType != RenderPropertyAnimator::DELTA
+ && deltaType != RenderPropertyAnimator::ABSOLUTE,
+ "Invalid delta type %d", deltaType);
+
+ RenderNodeAnimator* animator = new RenderNodeAnimator(env, weakThis,
+ static_cast<RenderPropertyAnimator::RenderProperty>(property),
+ static_cast<RenderPropertyAnimator::DeltaValueType>(deltaType),
+ deltaValue);
+ animator->incStrong(0);
+ return reinterpret_cast<jlong>( animator );
+}
+
+static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jint duration) {
+ LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative");
+ RenderNodeAnimator* animator = reinterpret_cast<RenderNodeAnimator*>(animatorPtr);
+ animator->setDuration(duration);
+}
+
+static void unref(JNIEnv* env, jobject clazz, jlong objPtr) {
+ VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr);
+ obj->decStrong(0);
+}
+
+#endif
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/view/RenderNodeAnimator";
+
+static JNINativeMethod gMethods[] = {
+#ifdef USE_OPENGL_RENDERER
+ { "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IIF)J", (void*) createAnimator },
+ { "nSetDuration", "(JI)V", (void*) setDuration },
+ { "nUnref", "(J)V", (void*) unref },
+#endif
+};
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className);
+
+#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+int register_android_view_RenderNodeAnimator(JNIEnv* env) {
+ FIND_CLASS(gRenderNodeAnimatorClassInfo.clazz, kClassPathName);
+ gRenderNodeAnimatorClassInfo.clazz = jclass(env->NewGlobalRef(gRenderNodeAnimatorClassInfo.clazz));
+
+ GET_STATIC_METHOD_ID(gRenderNodeAnimatorClassInfo.callOnFinished, gRenderNodeAnimatorClassInfo.clazz,
+ "callOnFinished", "(Ljava/lang/ref/WeakReference;)V");
+
+ return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+
+} // namespace android
diff --git a/core/jni/android_view_RenderNodeAnimator.h b/core/jni/android_view_RenderNodeAnimator.h
new file mode 100644
index 0000000..760ca91
--- /dev/null
+++ b/core/jni/android_view_RenderNodeAnimator.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+
+#ifdef USE_OPENGL_RENDERER
+
+#include <Animator.h>
+
+namespace android {
+
+class RenderNodeAnimator : public uirenderer::RenderPropertyAnimator {
+public:
+ RenderNodeAnimator(JNIEnv* env, jobject callbackObject,
+ RenderProperty property, DeltaValueType deltaType, float delta);
+ virtual ~RenderNodeAnimator();
+
+ void callOnFinished();
+
+private:
+ JavaVM* mJvm;
+ jobject mWeakThis;
+};
+
+}
+
+#endif
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 8141a00..c293c7a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -508,7 +508,7 @@
nsecs_t presentedTimesNanoSrc[frameCount];
for (size_t i = 0; i < frameCount; i++) {
- nsecs_t presentedTimeNano = stats.desiredPresentTimesNano[i];
+ nsecs_t presentedTimeNano = stats.actualPresentTimesNano[i];
if (presentedTimeNano == INT64_MAX) {
presentedTimeNano = gWindowContentFrameStatsClassInfo.UNDEFINED_TIME_NANO;
}
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index b5f489d..58fc1e1 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "ThreadedRenderer"
+#include <algorithm>
+
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
@@ -24,6 +26,8 @@
#include <android_runtime/android_view_Surface.h>
#include <system/window.h>
+#include "android_view_RenderNodeAnimator.h"
+#include <RenderNode.h>
#include <renderthread/RenderProxy.h>
#include <renderthread/RenderTask.h>
#include <renderthread/RenderThread.h>
@@ -63,15 +67,75 @@
jobject mRunnable;
};
+class InvokeAnimationListeners : public MessageHandler {
+public:
+ InvokeAnimationListeners(std::vector< sp<RenderNodeAnimator> >& animators) {
+ mAnimators.swap(animators);
+ }
+
+ static void callOnFinished(const sp<RenderNodeAnimator>& animator) {
+ animator->callOnFinished();
+ }
+
+ virtual void handleMessage(const Message& message) {
+ std::for_each(mAnimators.begin(), mAnimators.end(), callOnFinished);
+ mAnimators.clear();
+ }
+
+private:
+ std::vector< sp<RenderNodeAnimator> > mAnimators;
+};
+
+class RootRenderNode : public RenderNode, public AnimationListener {
+public:
+ RootRenderNode() : RenderNode() {
+ mLooper = Looper::getForThread();
+ LOG_ALWAYS_FATAL_IF(!mLooper.get(),
+ "Must create RootRenderNode on a thread with a looper!");
+ }
+
+ virtual ~RootRenderNode() {}
+
+ void onAnimationFinished(const sp<RenderPropertyAnimator>& animator) {
+ mFinishedAnimators.push_back(
+ reinterpret_cast<RenderNodeAnimator*>(animator.get()));
+ }
+
+ virtual void prepareTree(TreeInfo& info) {
+ info.animationListener = this;
+ RenderNode::prepareTree(info);
+ info.animationListener = NULL;
+
+ // post all the finished stuff
+ if (mFinishedAnimators.size()) {
+ sp<InvokeAnimationListeners> message
+ = new InvokeAnimationListeners(mFinishedAnimators);
+ mLooper->sendMessage(message, 0);
+ }
+ }
+
+private:
+ sp<Looper> mLooper;
+ std::vector< sp<RenderNodeAnimator> > mFinishedAnimators;
+};
+
static void android_view_ThreadedRenderer_postToRenderThread(JNIEnv* env, jobject clazz,
jobject jrunnable) {
RenderTask* task = new JavaTask(env, jrunnable);
RenderThread::getInstance().queue(task);
}
+static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
+ RootRenderNode* node = new RootRenderNode();
+ node->incStrong(0);
+ node->setName("RootRenderNode");
+ return reinterpret_cast<jlong>(node);
+}
+
static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
- jboolean translucent) {
- return (jlong) new RenderProxy(translucent);
+ jboolean translucent, jlong rootRenderNodePtr) {
+ RenderNode* rootRenderNode = reinterpret_cast<RenderNode*>(rootRenderNodePtr);
+ return (jlong) new RenderProxy(translucent, rootRenderNode);
}
static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz,
@@ -113,12 +177,11 @@
proxy->setup(width, height);
}
-static void android_view_ThreadedRenderer_drawDisplayList(JNIEnv* env, jobject clazz,
- jlong proxyPtr, jlong displayListPtr, jint dirtyLeft, jint dirtyTop,
+static void android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jint dirtyLeft, jint dirtyTop,
jint dirtyRight, jint dirtyBottom) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
- proxy->drawDisplayList(displayList, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+ proxy->syncAndDrawFrame(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
}
static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz,
@@ -187,13 +250,14 @@
static JNINativeMethod gMethods[] = {
#ifdef USE_OPENGL_RENDERER
{ "postToRenderThread", "(Ljava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_postToRenderThread },
- { "nCreateProxy", "(Z)J", (void*) android_view_ThreadedRenderer_createProxy },
+ { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
+ { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
{ "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
{ "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
{ "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
{ "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
{ "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup },
- { "nDrawDisplayList", "(JJIIII)V", (void*) android_view_ThreadedRenderer_drawDisplayList },
+ { "nSyncAndDrawFrame", "(JIIII)V", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
{ "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
{ "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
{ "nRunWithGlContext", "(JLjava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_runWithGlContext },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index a61fa87..c58bf04 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -92,14 +92,10 @@
if (WIFEXITED(status)) {
if (WEXITSTATUS(status)) {
ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
- } else if (false) {
- ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
}
} else if (WIFSIGNALED(status)) {
if (WTERMSIG(status) != SIGKILL) {
- ALOGI("Process %d exited cleanly (%d)", pid, WTERMSIG(status));
- } else if (false) {
- ALOGI("Process %d exited cleanly (%d)", pid, WTERMSIG(status));
+ ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status));
}
#ifdef WCOREDUMP
if (WCOREDUMP(status)) {
@@ -117,8 +113,10 @@
}
}
- if (pid < 0) {
- ALOGW("Zygote SIGCHLD error in waitpid: %d", errno);
+ // Note that we shouldn't consider ECHILD an error because
+ // the secondary zygote might have no children left to wait for.
+ if (pid < 0 && errno != ECHILD) {
+ ALOGW("Zygote SIGCHLD error in waitpid: %s", strerror(errno));
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 57e845f..3d3e86f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2056,6 +2056,13 @@
android:description="@string/permdesc_bindWallpaper"
android:protectionLevel="signature|system" />
+ <!-- Must be required by a {@link android.service.voice.VoiceInteractionService},
+ to ensure that only the system can bind to it. -->
+ <permission android:name="android.permission.BIND_VOICE_INTERACTION"
+ android:label="@string/permlab_bindVoiceInteraction"
+ android:description="@string/permdesc_bindVoiceInteraction"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider},
to ensure that only the system can bind to it.
@hide -->
@@ -2620,6 +2627,15 @@
android:description="@string/permdesc_bindNotificationListenerService"
android:protectionLevel="signature" />
+ <!-- Must be required by an {@link
+ android.service.notification.ConditionProviderService},
+ to ensure that only the system can bind to it.
+ @hide -->
+ <permission android:name="android.permission.BIND_CONDITION_PROVIDER_SERVICE"
+ android:label="@string/permlab_bindConditionProviderService"
+ android:description="@string/permdesc_bindConditionProviderService"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to call into a carrier setup flow. It is up to the
carrier setup application to enforce that this permission is required
@hide This is not a third-party API (intended for OEMs and system apps). -->
@@ -2665,6 +2681,26 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+ <activity android:name="com.android.internal.app.IntentForwarderActivity"
+ android:finishOnCloseSystemDialogs="true"
+ android:theme="@style/Theme.NoDisplay"
+ android:excludeFromRecents="true"
+ android:label="@string/user_owner_label"
+ android:exported="true"
+ >
+ </activity>
+ <activity-alias android:name="com.android.internal.app.ForwardIntentToUserOwner"
+ android:targetActivity="com.android.internal.app.IntentForwarderActivity"
+ android:icon="@drawable/personal_icon"
+ android:exported="true"
+ android:label="@string/user_owner_label">
+ </activity-alias>
+ <activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
+ android:targetActivity="com.android.internal.app.IntentForwarderActivity"
+ android:icon="@drawable/work_icon"
+ android:exported="true"
+ android:label="@string/managed_profile_label">
+ </activity-alias>
<activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity"
android:theme="@style/Theme.Holo.Dialog"
android:label="@string/heavy_weight_switcher_title"
diff --git a/core/res/res/drawable-hdpi/personal_icon.png b/core/res/res/drawable-hdpi/personal_icon.png
new file mode 100644
index 0000000..8d96b5e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/personal_icon.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/work_icon.png b/core/res/res/drawable-hdpi/work_icon.png
new file mode 100644
index 0000000..e90866b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/work_icon.png
Binary files differ
diff --git a/core/res/res/drawable/btn_borderless_quantum.xml b/core/res/res/drawable/btn_borderless_quantum.xml
index 69a891a..2e3c515 100644
--- a/core/res/res/drawable/btn_borderless_quantum.xml
+++ b/core/res/res/drawable/btn_borderless_quantum.xml
@@ -16,7 +16,6 @@
<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="?attr/colorButtonPressed">
- <item android:drawable="@color/transparent" />
<item android:id="@id/mask"
android:drawable="@drawable/btn_qntm_alpha" />
</touch-feedback>
diff --git a/packages/SystemUI/res/layout/notification_adaptive_wrapper.xml b/core/res/res/drawable/dialog_background_quantum.xml
similarity index 64%
rename from packages/SystemUI/res/layout/notification_adaptive_wrapper.xml
rename to core/res/res/drawable/dialog_background_quantum.xml
index 15d0890..7e5b003 100644
--- a/packages/SystemUI/res/layout/notification_adaptive_wrapper.xml
+++ b/core/res/res/drawable/dialog_background_quantum.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,8 +13,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<SizeAdaptiveLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="@android:color/background_dark"
- android:id="@+id/notification_adaptive_wrapper"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+
+ <corners
+ android:radius="2dp" />
+ <solid
+ android:color="?attr/colorBackground" />
+
+</shape>
diff --git a/packages/SystemUI/res/layout/notification_adaptive_wrapper.xml b/core/res/res/drawable/list_selector_quantum.xml
similarity index 64%
copy from packages/SystemUI/res/layout/notification_adaptive_wrapper.xml
copy to core/res/res/drawable/list_selector_quantum.xml
index 15d0890..c007117 100644
--- a/packages/SystemUI/res/layout/notification_adaptive_wrapper.xml
+++ b/core/res/res/drawable/list_selector_quantum.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,8 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<SizeAdaptiveLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="@android:color/background_dark"
- android:id="@+id/notification_adaptive_wrapper"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="?attr/colorButtonPressed">
+ <item android:id="@id/mask">
+ <color android:color="@color/white" />
+ </item>
+</touch-feedback>
diff --git a/core/res/res/layout/alert_dialog_quantum.xml b/core/res/res/layout/alert_dialog_quantum.xml
index 537162a..80e34cb 100644
--- a/core/res/res/layout/alert_dialog_quantum.xml
+++ b/core/res/res/layout/alert_dialog_quantum.xml
@@ -20,7 +20,10 @@
android:id="@+id/parentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:background="@drawable/dialog_background_quantum"
+ android:translationZ="@dimen/floating_window_z"
+ android:layout_margin="@dimen/floating_window_margin">
<LinearLayout android:id="@+id/topPanel"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/simple_list_item_2_single_choice.xml b/core/res/res/layout/simple_list_item_2_single_choice.xml
index 65c5856..940c6b8 100644
--- a/core/res/res/layout/simple_list_item_2_single_choice.xml
+++ b/core/res/res/layout/simple_list_item_2_single_choice.xml
@@ -21,7 +21,8 @@
android:gravity="center_vertical"
android:paddingStart="16dip"
android:paddingEnd="12dip"
- android:minHeight="?android:attr/listPreferredItemHeightSmall">
+ android:minHeight="?attr/listPreferredItemHeightSmall"
+ android:background="@color/transparent">
<LinearLayout
android:layout_width="wrap_content"
@@ -33,8 +34,8 @@
<TextView android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceListItem"
- android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:textAppearance="?attr/textAppearanceListItem"
+ android:textColor="?attr/textColorAlertDialogListItem"
android:gravity="center_vertical|start"
android:singleLine="true"
android:ellipsize="marquee" />
@@ -42,8 +43,8 @@
<TextView android:id="@android:id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceListItemSecondary"
- android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:textAppearance="?attr/textAppearanceListItemSecondary"
+ android:textColor="?attr/textColorAlertDialogListItem"
android:gravity="center_vertical|start"
android:singleLine="true"
android:ellipsize="marquee" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index db4092e..eb68c39 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Dit laat die houer toe om aan die topvlak-koppelvlak van \'n VPN-diens te bind. Dit moet nooit vir normale programme nodig wees nie."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"bind aan \'n muurpapier"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Dit laat die houer toe om aan die topvlak-koppelvlak van \'n muurpapier te bind. Dit moet nooit vir normale programme nodig wees nie."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"koppel aan \'n afstandskerm"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Dit laat die houer toe om aan die top-koppelvlak van \'n afstandskerm te koppel. Behoort nooit vir gewone programme nodig te wees nie."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bind aan \'n legstukdiens"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Laat die program toe om kennisgewings op te haal, te bestudeer en te verwyder, insluitende die kennisgewings wat deur ander programme geplaas is."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bind aan \'n kennisgewingluisteraardiens"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Laat die houer toe om aan die top-koppelvlak van \'n kennisgewingluisteraardiens te bind. Behoort nooit vir gewone programme nodig te wees nie."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"roep die opstellingprogram op wat deur die diensverskaffer voorsien is"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Laat die houer toe om die opstellingsprogram wat deur die diensverskaffer voorsien word, op te roep. Behoort nooit vir gewone programme nodig te wees nie."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"luister vir waarnemings oor netwerktoestande"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Muurpapier"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Verander muurpapier"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Kennisgewingluisteraar"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN geaktiveer"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is geaktiveer deur <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Raak om die netwerk te bestuur."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index de89ec6..05594b3 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"የአውሮፕላን ሁነታ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"የአውሮፕላንሁነታ በርቷል"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"የአውሮፕላንሁነታ ጠፍቷል"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"ቅንብሮች"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"የሚያስተማምን ሁነታ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android ስርዓት"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"የVPN ግልጋሎትን ወደ ከፍተኛ-ደረጃ በየነ ገጽ ለማሳር ለመያዣው ይፈቅዳሉ፡፡ለተለመዱ መተግበሪያዎች አያስፈልግም፡፡"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"በልጣፍ ጠርዝ"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"ያዡ ግቤት ስልቱን ወደ ከፍተኛ-ደረጃ ልጣፍ ለመጠረዝ ይፈቅዳሉ። ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ከአንድ የርቀት ማሳያ ጋር ይጠርዛል"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"ያዢው ከአንድ የርቀት ማሳያ ከፍተኛ-ደረጃ በይነገጽ ጋር እንዲጠርዝ ይፈቅድለታል። ለመደበኛ መተግበሪያዎች በጭራሽ አያስፈልግም።"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ወደ ፍርግም አገልግሎት አያይዝ"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"መተግበሪያው ማሳወቂያዎችን እንዲያስመጣ፣ እንዲመረምር እና እንዲያጸዳ ያስችለዋል፣ በሌሎች መተግበሪያዎች የተለጠፉትንም ጨምሮ።"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"ከአንድ የማሳወቂያ አዳማጭ አገልግሎት ጋር ይሰሩ"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"ያዢው የማሳወቂያ አዳማጭ አገልግሎቱን ከከፍተኛ-ደረጃ በይነገጹ ጋር እንዲያስር ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ አያስፈልግም።"</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"በድምጸ-ተያያዥ ሞደም የቀረበው የውቅር መተግበሪያውን መጥራት"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"ያዢው በድምጸ-ተያያዥ ሞደም የቀረበው የውቅር መተግበሪያውን እንዲጠራው ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ አያስፈልግም።"</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"በአውታረ መረብ ሁኔታዎች ላይ የተስተዋሉ ነገሮችን ያዳምጣል"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ልጣፍ"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ልጣፍ ለውጥ"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"ማሳወቂያ አዳማጭ"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN ነቅቷል።"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN በ<xliff:g id="APP">%s</xliff:g>ገብሯል"</string>
<string name="vpn_text" msgid="3011306607126450322">"አውታረመረብ ለማደራጀት ንካ።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 2ee49bd..fa662fd 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة الشبكة الظاهرية الخاصة (VPN). لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"الالتزام بخلفية ما"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"للسماح للمالك بالالتزام بواجهة المستوى العلوي للخلفية. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"الربط بالشاشة عن بُعد"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"للسماح للمالك بالالتزام بواجهة المستوى العلوي للعرض عن بُعد. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"الالتزام بخدمة أداة"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"يتيح للتطبيق استرجاع الإشعارات وفحصها ومسحها، بما في ذلك تلك التي نشرتها تطبيقات أخرى."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"الربط بخدمة تلقّي الإشعارات الصوتية"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"يتيح للمالك الربط بواجهة المستوى العلوي لخدمة تلقّي الإشعارات الصوتية. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"استدعاء تطبيق التهيئة الذي يوفره مشغل شبكة الجوال"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"للسماح للمالك باستدعاء تطبيق التهيئة الذي يوفره مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"الاستماع إلى ملاحظات حول أحوال الشبكة"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"الخلفية"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"تغيير الخلفية"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"برنامج تلقّي الإشعارات الصوتية"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"تم تنشيط الشبكة الظاهرية الخاصة (VPN)"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"تم تنشيط VPN بواسطة <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"المس لإدارة الشبكة."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 3f00f1f..d347db6 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Самолетен режим"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Самолетният режим е ВКЛЮЧЕН"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Самолетният режим е ИЗКЛЮЧЕН"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Настройки"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Безопасен режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Системно от Android"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на услуга за VPN. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"обвързване с тапет"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Разрешава на притежателя да се обвърже с интерфейса от най-високото ниво на тапет. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"свързване с отдалечен екран"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Разрешава на притежателя да се свърже с интерфейса от първо ниво на отдалечен екран. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"обвързване с услуга за приспособления"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Разрешава на приложението да извлича, преглежда и изчиства известия, включително публикуваните от други приложения."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"обвързване с услуга за слушател на известия"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Разрешава на притежателя да се обвърже с интерфейса от първо ниво на услуга за слушател на известия. Нормалните приложения не би трябвало никога да се нуждаят от това."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"извикване на предоставеното от оператора приложение за конфигуриране"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Разрешава на притежателя да извиква предоставеното от оператора приложение за конфигуриране. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"слушане за наблюдения на мрежовите условия"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Тапет"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Промяна на тапета"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Слушател на известия"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN е активирана"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN е активирана от <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Докоснете за управление на мрежата."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index afb0852..fce2c4e 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permet que el titular vinculi a la interfície de nivell superior d\'un servei de VPN. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"enllaça amb un fons de pantalla"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permet que el titular vinculi a la interfície de nivell superior d\'un fons de pantalla. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"vincula a una pantalla remota"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Permet que el titular es vinculi a la interfície de nivell superior d\'una pantalla remota. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"vincula a un servei de widget"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permet que l\'aplicació recuperi, examini i esborri les notificacions, incloses les que han publicat altres aplicacions."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincula a un servei oient de notificacions"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permet que el titular vinculi la interfície de nivell superior d\'un servei oient de notificacions. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"invoca l\'aplicació de configuració proporcionada per l\'operador"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permet que el titular invoqui l\'aplicació de configuració proporcionada per l\'operador. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"conèixer les observacions sobre les condicions de la xarxa"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fons de pantalla"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Canvia el fons de pantalla"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Oient de notificacions"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ha activat VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca per gestionar la xarxa."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 39c6c08..2f73544 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Režim V letadle"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Režim V letadle je ZAPNUTÝ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Režim V letadle je VYPNUTÝ"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Nastavení"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Nouzový režim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Systém Android"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Umožňuje držiteli navázat se na nejvyšší úroveň služby VPN. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"vazba na tapetu"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Umožňuje držiteli navázat se na nejvyšší úroveň rozhraní tapety. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"připojit se ke vzdálenému displeji"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Umožňuje držiteli připojit se k vysokoúrovňovému rozhraní vzdáleného displeje. Běžné aplikace by toto oprávnění neměly nikdy potřebovat."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"navázat se na službu widgetu"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Umožňuje aplikacím načítat, zobrazovat a mazat oznámení včetně těch přidaných jinými aplikacemi."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"navázání na službu pro poslouchání oznámení"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Umožňuje držiteli navázat se na nejvyšší úroveň služby pro poslouchání oznámení. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"vyvolat konfigurační aplikaci poskytnutou operátorem"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Umožňuje vyvolání konfigurační aplikace poskytnuté operátorem. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"naslouchat informacím o stavu sítě"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapeta"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Změnit tapetu"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Aplikace poslouchající oznámení"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"Síť VPN je aktivována"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikace <xliff:g id="APP">%s</xliff:g> aktivovala síť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykem zobrazíte správu sítě."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index c06ecbb..d1ab769 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Tillader, at brugeren forpligter sig til en VPN-tjenestes grænseflade på øverste niveau. Bør aldrig være nødvendigt i almindelige apps."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"knyt til en baggrund"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Tillader, at indehaveren kan binde en baggrunds grænseflade på øverste niveau. Dette bør aldrig være nødvendigt for almindelige apps."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"bind til en ekstern skærm"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Tillader, at brugeren kan foretage en binding til grænsefladens øverste niveau på en ekstern skærm. Bør aldrig være nødvendigt til almindelige apps."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"forpligt til en widgettjeneste"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillader, at appen kan hente, undersøge og rydde underretninger, f.eks. dem, der er sendt af andre apps."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"forpligte sig til en underretningslyttertjeneste"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Tillader brugeren at forpligte sig til en underretningslyttertjenestes grænseflade på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"aktivere konfigurationsappen, der leveres af mobilselskabet"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Tillader, at brugeren aktiverer konfigurationsappen, der er forsynet af mobilselskabet. Dette bør aldrig være nødvendigt for almindelige apps."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"observer netværksforhold"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Baggrund"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Skift baggrund"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Underretningslytter"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN er aktiveret."</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveres af <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tryk for at administrere netværket."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index e1ee025..31b9c9d 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Ermöglicht dem Halter, sich an die Oberfläche eines VPN-Dienstes auf oberster Ebene zu binden. Sollte nie für normale Apps benötigt werden."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"An einen Hintergrund binden"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Ermöglicht dem Halter, sich an die Oberfläche eines Hintergrunds auf oberster Ebene zu binden. Sollte nie für normale Apps benötigt werden."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"An Remote-Display binden"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Ermöglicht dem Halter, sich an die Oberfläche eines Remote-Displays auf oberster Ebene zu binden. Sollte für normale Apps nie benötigt werden."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"An einen Widget-Dienst binden"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Ermöglicht der App das Abrufen, Überprüfen und Löschen von Benachrichtigungen, einschließlich Benachrichtigungen, die von anderen Apps gepostet wurden"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"An Benachrichtigungs-Listener-Dienst binden"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ermöglicht dem Inhaber, sich an die Oberfläche der obersten Ebene eines Benachrichtigungs-Listener-Dienstes zu binden. Sollte nie für normale Apps benötigt werden."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"Vom Mobilfunkanbieter bereitgestellte Konfigurations-App aufrufen"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Ermöglicht dem Inhaber, die vom Mobilfunkanbieter bereitgestellte Konfigurations-App aufzurufen. Sollte für normale Apps nie benötigt werden."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"Informationen zu den Netzwerkbedingungen erfassen"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Hintergrund"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Hintergrund ändern"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Benachrichtigungs-Listener"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN aktiviert"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN wurde von <xliff:g id="APP">%s</xliff:g> aktiviert."</string>
<string name="vpn_text" msgid="3011306607126450322">"Zum Verwalten des Netzwerks berühren"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 5af0277..a1d6049 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας υπηρεσίας Vpn. Δεν απαιτείται για κανονικές εφαρμογές."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"δέσμευση σε ταπετσαρία"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας ταπετσαρίας. Δεν απαιτείται για συνήθεις εφαρμογές."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"μεταφορά σε μια απομακρυσμένη οθόνη"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας απομακρυσμένης οθόνης. Δεν απαιτείται ποτέ για κανονικές εφαρμογές."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"δέσμευση σε υπηρεσία γραφικών στοιχείων"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Επιτρέπει στην εφαρμογή να ανακτά, να εξετάζει και να απαλείφει ειδοποιήσεις, συμπεριλαμβανομένων εκείνων που δημοσιεύονται από άλλες εφαρμογές."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"δέσμευση σε υπηρεσία ακρόασης ειδοποίησης"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας υπηρεσίας ακρόασης ειδοποιήσεων. Δεν απαιτείται σε κανονικές εφαρμογές."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"κλήση της εφαρμογής διαμόρφωσης που παρέχεται από την εταιρεία κινητής τηλεφωνίας"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Επιτρέπει στον κάτοχο την κλήση της εφαρμογής διαμόρφωσης που παρέχεται από την εταιρεία κινητής τηλεφωνίας. Δεν απαιτείται για κανονικές εφαρμογές."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"λήψη παρατηρήσεων σχετικά με την κατάσταση δικτύου"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Ταπετσαρία"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Αλλαγή ταπετσαρίας"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Υπηρεσία ακρόασης ειδοποίησης"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"Το VPN ενεργοποιήθηκε"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Το VPN ενεργοποιήθηκε από την εφαρμογή <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Αγγίξτε για τη διαχείριση του δικτύου."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ad45887..33f0010 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Aeroplane mode"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Aeroplane mode is ON"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Aeroplane mode is OFF"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Settings"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Allows the holder to bind to the top-level interface of a Vpn service. Should never be needed for normal apps."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"bind to wallpaper"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Allows the holder to bind to the top-level interface of wallpaper. Should never be needed for normal applications."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"bind to a remote display"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Allows the holder to bind to the top-level interface of a remote display. Should never be needed for normal apps."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bind to a widget service"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Allows the app to retrieve, examine, and clear notifications, including those posted by other apps."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bind to a notification listener service"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"invoke the carrier-provided configuration app"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Allows the holder to invoke the carrier-provided configuration app. Should never be needed for normal apps."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"listen for observations on network conditions"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Change wallpaper"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Notification listener"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN activated"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is activated by <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Touch to manage the network."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index ad45887..33f0010 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Aeroplane mode"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Aeroplane mode is ON"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Aeroplane mode is OFF"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Settings"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Allows the holder to bind to the top-level interface of a Vpn service. Should never be needed for normal apps."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"bind to wallpaper"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Allows the holder to bind to the top-level interface of wallpaper. Should never be needed for normal applications."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"bind to a remote display"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Allows the holder to bind to the top-level interface of a remote display. Should never be needed for normal apps."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bind to a widget service"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Allows the app to retrieve, examine, and clear notifications, including those posted by other apps."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bind to a notification listener service"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"invoke the carrier-provided configuration app"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Allows the holder to invoke the carrier-provided configuration app. Should never be needed for normal apps."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"listen for observations on network conditions"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Change wallpaper"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Notification listener"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN activated"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is activated by <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Touch to manage the network."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 6d032ca..620d745 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permite al titular vincularse a la interfaz de nivel superior de un servicio de VPN. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"vincular a un fondo de pantalla"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite al propietario vincularse a la interfaz de nivel superior de un fondo de pantalla. Las aplicaciones normales no deben utilizar este permiso."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"vincular a una pantalla remota"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Permite al propietario vincularse a la interfaz de nivel superior de una pantalla remota. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"vincular a un servicio de widget"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que la aplicación recupere, examine y elimine notificaciones, incluidas aquellas publicadas por otras aplicaciones."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"Vincular a un servicio de agente de escucha de notificaciones"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite al propietario vincularse a la interfaz de nivel superior de un servicio de agente de escucha de notificaciones. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"ejecutar la aplicación de configuración proporcionada por el proveedor"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permite al propietario ejecutar la aplicación de configuración proporcionada por el proveedor. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"Detectar cambios en el estado de la red"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Papel tapiz"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambiar fondo de pantalla"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Agente de escucha de notificaciones"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN está activado por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 63bed24..cd96a08 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permite enlazar con la interfaz de nivel superior de un servicio de VPN. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"enlazar con un fondo de pantalla"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite enlazar con la interfaz de nivel superior de un fondo de pantalla. Las aplicaciones normales no deberían necesitar este permiso."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"enlazar a una pantalla remota"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Permite enlazar con la interfaz de nivel superior de una pantalla remota. Las aplicaciones normales no deberían necesitar este permiso."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"enlazar con un servicio de widget"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que la aplicación recupere, examine y borre notificaciones, incluidas las que han publicado otras aplicaciones."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"enlazar con un servicio de detector de notificaciones"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite enlazar con la interfaz de nivel superior de un servicio de detector de notificaciones. No debe ser necesario para las aplicaciones normales."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"ejecutar la aplicación de configuración proporcionada por el operador"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permite ejecutar la aplicación de configuración proporcionada por el operador. No debe ser necesario para aplicaciones normales."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"detectar cambios en el estado de la red"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fondo de pantalla"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambiar fondo de pantalla"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Detector de notificaciones"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activada por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 00e4a20..1d905df 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lennurežiim"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Lennurežiim on SEES"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Lennurežiim on VÄLJAS"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Seaded"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Turvarežiim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-süsteem"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Võimaldab omanikul siduda VPN-teenuse ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"taustapildiga sidumine"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Lubab omanikul siduda taustapildi ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"kaugekraaniga sidumine"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Lubab omanikul siduda rakenduse kaugekraani ülataseme liidesega. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"vidinateenusega sidumine"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Võimaldab rakendusel tuua, kontrollida ja kustutada märguandeid, sh neid, mille on postitanud teised rakendused."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"seo märguannete kuulamisteenusega"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Võimaldab omanikul siduda märguannete kuulamisteenuse ülemise taseme kasutajaliidese. Seda ei tohiks tavarakenduste puhul kunagi vaja olla."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"operaatoripoolse konfiguratsioonirakenduse aktiveerimine"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Lubab omanikul aktiveerida operaatoripoolse konfiguratsioonirakenduse. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"võrgutingimuste teabe kuulamine"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Taustapilt"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Muutke taustapilti"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Märguannete kuulamisteenus"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN on aktiveeritud"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-i aktiveeris <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Võrgu haldamiseks puudutage."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 42d654e..a198fbb 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"حالت هواپیما"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"حالت هواپیما روشن است"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"حالت هواپیما خاموش است"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"تنظیمات"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"بیشتر از 999"</string>
<string name="safeMode" msgid="2788228061547930246">"حالت ایمن"</string>
<string name="android_system_label" msgid="6577375335728551336">"سیستم Android"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"به دارنده اجازه میدهد که به رابط سطح بالای سرویس Vpn متصل شود. هرگز برای برنامههای معمولی مورد نیاز نیست."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"پیوند شده به تصویر زمینه"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"به دارنده اجازه میدهد تا به رابط سطح بالای تصویر زمینه متصل شود. برنامههای معمولی هرگز به این ویژگی نیاز ندارند."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"اتصال به نمایشگر راه دور"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"به دارنده امکان میدهد تا به رابط سطح بالای نمایشگر راه دور وصل شود. نباید هرگز برای برنامههای عادی لازم باشد."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"اتصال به یک سرویس ابزارک"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"به برنامه اجازه میدهد به بازیابی، بررسی و پاک کردن اعلانها از جمله موارد پست شده توسط سایر برنامهها بپردازد."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"اتصال به یک سرویس شنونده اعلان"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"به دارنده اجازه میدهد به یک رابط سطح بالای سرویس شنونده اعلان متصل شود. هرگز نباید برای برنامههای عادی لازم شود."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"لغو برنامه پیکربندی ارائه شده توسط شرکت مخابراتی"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"به دارنده اجازه میدهد که تنظیمات برنامه شرکت مخابراتی را لغو کند. هرگز برای برنامههای معمولی مورد نیاز نیست."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"گوش دادن برای بررسی شرایط شبکه"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"تصویر زمینه"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"تغییر تصویر زمینه"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"شنونده اعلان"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN فعال شد"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN توسط <xliff:g id="APP">%s</xliff:g> فعال شده است"</string>
<string name="vpn_text" msgid="3011306607126450322">"برای مدیریت شبکه لمس کنید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 7698dda..5a01910 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lentokonetila"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Lentokonetila on KÄYTÖSSÄ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Lentokonetila on POIS KÄYTÖSTÄ"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Asetukset"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Suojattu tila"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-järjestelmä"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Antaa sovelluksen sitoutua VPN-palvelun ylemmän tason käyttöliittymään. Ei tavallisten sovellusten käyttöön."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"sido taustakuvaan"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Antaa sovelluksen sitoutua taustakuvan ylätason käyttöliittymään. Ei tavallisten sovellusten käyttöön."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"etänäyttöön sitoutuminen"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Antaa sovelluksen sitoutua etänäytön ylemmän tason käyttöliittymään. Ei tavallisten sovelluksien käyttöön."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"sitoudu widget-palveluun"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Antaa sovelluksen noutaa, tutkia ja tyhjentää ilmoituksia (myös muiden sovelluksien lähettämiä)."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"sido ilmoituskuuntelijapalveluun"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Antaa sovelluksen sitoutua ilmoituskuuntelijan ylimmän tason käyttöliittymään. Ei tavallisten sovelluksien käyttöön."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"Palveluntarjoajan määrityssovelluksen käynnistäminen"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Antaa luvanhaltijan käynnistää palveluntarjoajan määrityssovelluksen. Ei tavallisten sovelluksien käyttöön."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"verkon tilahavaintojen kuunteleminen"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Taustakuva"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Vaihda taustakuvaa"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Ilmoituskuuntelija"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN on aktivoitu"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> on aktivoinut VPN-yhteyden"</string>
<string name="vpn_text" msgid="3011306607126450322">"Voit hallinnoida verkkoa koskettamalla."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index cc3bcd2..17cda8d 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service RPV. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"se fixer à un fond d\'écran"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un fond d\'écran. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"lier à un écran distant"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un écran distant. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"s\'associer à un service de widget"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permet aux applications de récupérer, d\'examiner et d\'autoriser les notifications, y compris celles envoyées par d\'autres applications."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"s\'associer à l\'interface de niveau supérieur d\'un service d\'écoute des notifications"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permet à l\'application de s\'associer à l\'interface de niveau supérieur d\'un service d\'écoute des notifications. Ne devrait jamais être nécessaire pour les applications normales."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"faire appel à l\'application de configuration du fournisseur de services"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permet à l\'application autorisée de faire appel à l\'application de configuration fournie par le fournisseur de services. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"détecter des observations sur les conditions du réseau"</string>
@@ -1284,11 +1292,11 @@
<string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"Si vous activez la mémoire de stockage USB, certaines applications en cours d\'utilisation vont être fermées et risquent de rester indisponibles jusqu\'à ce que la mémoire de stockage USB soit désactivée."</string>
<string name="dlg_error_title" msgid="7323658469626514207">"Échec du fonctionnement de la mémoire de stockage USB."</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_mtp_notification_title" msgid="3699913097391550394">"Connecté en tant qu\'appareil multimédia"</string>
+ <string name="usb_mtp_notification_title" msgid="3699913097391550394">"Connecté en tant qu\'app. multimédia"</string>
<string name="usb_ptp_notification_title" msgid="1960817192216064833">"Connecté en tant qu\'appareil photo"</string>
<string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"Connecté en tant que programme d\'installation"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connecté à un accessoire USB"</string>
- <string name="usb_notification_message" msgid="2290859399983720271">"Appuyez ici pour accéder aux autres options USB."</string>
+ <string name="usb_notification_message" msgid="2290859399983720271">"Appuyez pour accéder aux autres options USB."</string>
<string name="extmedia_format_title" product="nosdcard" msgid="9020092196061007262">"Formater mémoire?"</string>
<string name="extmedia_format_title" product="default" msgid="3648415921526526069">"Formater la carte SD?"</string>
<string name="extmedia_format_message" product="nosdcard" msgid="3934016853425761078">"Tous les fichiers stockés sur la mémoire de stockage USB vont être effacés. Cette action est irréversible."</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fond d\'écran"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Changer de fond d\'écran"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Outil d\'écoute des notifications"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN activé"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activé par <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Appuyez ici pour gérer le réseau."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 30c7767..3c0266f 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mode Avion"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Le mode Avion est activé."</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Le mode Avion est désactivé."</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Paramètres"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
<string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service VPN. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"Se fixer sur un fond d\'écran"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un fond d\'écran. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"s\'associer à un écran à distance"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Permettre à l\'application autorisée de s\'associer à l\'interface de niveau supérieur d\'un écran à distance. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"associer à un service widget"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permet aux applications de récupérer, d\'examiner et d\'autoriser les notifications, y compris celles envoyées par d\'autres applications."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"s\'associer à l\'interface de niveau supérieur d\'un service d\'écoute des notifications"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permet à l\'application de s\'associer à l\'interface de niveau supérieur d\'un service d\'écoute des notifications. Ne devrait jamais être nécessaire pour les applications normales."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"faire appel à l\'application de configuration fournie par l\'opérateur"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permet à l\'application autorisée de faire appel à l\'application de configuration fournie par l\'opérateur. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"détecter des observations sur les conditions du réseau"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fond d\'écran"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Changer de fond d\'écran"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Outil d\'écoute des notifications"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN activé"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activé par <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Appuyez ici pour gérer le réseau."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 852215f..1638b6b 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"धारक को किसी Vpn सेवा के शीर्ष-स्तर इंटरफ़ेस से आबद्ध होने देता है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"वॉलपेपर से आबद्ध करें"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"धारक को किसी वॉलपेपर के शीर्ष-स्तर इंटरफ़ेस से आबद्ध होने देता है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"रिमोट डिस्प्ले से आबद्ध करें"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"धारक को किसी रिमोट डिस्प्ले के शीर्ष-स्तरीय इंटरफ़ेस से आबद्ध होने देती है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"किसी विजेट सेवा से आबद्ध करें"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"ऐप्स को सूचनाओं को प्राप्त करने, जांच करने, और साफ़ करने देता है, जिनमें अन्य ऐप्स के द्वारा पोस्ट की गई सूचनाएं भी शामिल हैं."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"सूचना श्रवणकर्ता सेवा से जुड़ें"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"धारक को सूचना श्रवणकर्ता सेवा के शीर्ष स्तरीय इंटरफ़ेस से जुड़ने देती है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होनी चाहिए."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"वाहक के द्वारा उपलब्ध कराया गया कॉन्फ़िगरेशन ऐप्स प्रारंभ करें"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"धारक को वाहक के द्वारा उपलब्ध कराया गया कॉन्फ़िगरेशन ऐप्स प्रारंभ करने देता है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"नेटवर्क स्थितियों के अवलोकनों को सुनें"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"वॉलपेपर"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"वॉलपेपर बदलें"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"सूचना श्रवणकर्ता"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN सक्रिय"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN को <xliff:g id="APP">%s</xliff:g> द्वारा सक्रिय किया गया है"</string>
<string name="vpn_text" msgid="3011306607126450322">"नेटवर्क प्रबंधित करने के लिए स्पर्श करें."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 4ab25af..6e44150 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Način rada u zrakoplovu"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Uključen je način rada u zrakoplovu"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Isključen je način rada u zrakoplovu"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Postavke"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Siguran način rada"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sustav Android"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Nositelju omogućuje vezanje uz sučelje najviše razine VPN usluge. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"povezano s pozadinskom slikom"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Nositelju omogućuje povezivanje sa sučeljem pozadinske slike najviše razine. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"vezanje uz udaljeni zaslon"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Nositelju omogućuje vezanje uza sučelje najviše razine udaljenog zaslona. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"vezanje na uslugu widgeta"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Omogućuje aplikaciji dohvaćanje, pregledavanje i brisanje obavijesti, uključujući obavijesti drugih aplikacija."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vezanje uz uslugu slušatelja obavijesti"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Nositelju omogućuje vezanje uz sučelje najviše razine usluge slušatelja obavijesti. Ne bi smjelo biti potrebno za uobičajene aplikacije."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"pozovi operaterovu aplikaciju za konfiguraciju"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Dopušta nositelju pozivanje operaterove aplikacije za konfiguraciju. Ne bi smjelo biti potrebno za uobičajene aplikacije."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"praćenje motrenja mrežnih uvjeta"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Pozadinska slika"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Promjena pozadinske slike"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Slušatelj obavijesti"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN aktiviran"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikacija <xliff:g id="APP">%s</xliff:g> aktivirala je VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dodirnite za upravljanje mrežom."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index cc47d26..9697bae 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Lehetővé teszi a használó számára, hogy csatlakozzon egy VPN-szolgáltatás legfelső szintű kezelőfelületéhez. A normál alkalmazásoknak erre soha nincs szüksége."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"összekapcsolás háttérképpel"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Lehetővé teszi, hogy a tulajdonos kötelezővé tegye egy háttérkép legfelső szintű felületét. A normál alkalmazásoknak erre soha nincs szüksége."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"csatlakozás egy távoli kijelzőhöz"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Lehetővé teszi a használó számára, hogy csatlakozzon egy távoli kijelző legfelső szintű kezelőfelületéhez. A normál alkalmazásoknak erre soha nincs szükségük."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"csatlakozás modulszolgáltatáshoz"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Lehetővé teszi, hogy az alkalmazás értesítéseket kérdezzen le, vizsgáljon és tisztítson meg, beleértve az egyéb alkalmazások által közzétett értesítéseket is."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"csatlakozzon értesítésfigyelő szolgáltatáshoz"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lehetővé teszi a használó számára, hogy csatlakozzon egy értesítésfigyelő szolgáltatás legfelső szintű felületéhez. A normál alkalmazásoknak erre soha nincs szükségük."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"a szolgáltatói konfigurációs alkalmazás hívása"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Lehetővé teszi a használó számára a szolgáltató által biztosított konfigurációs alkalmazás hívását. A normál alkalmazásoknak erre soha nincs szükségük."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"hálózati körülményekkel kapcsolatos észrevételek figyelemmel kísérése"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Háttérkép"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Háttérkép megváltoztatása"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Értesítésfigyelő"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN aktiválva"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"A(z) <xliff:g id="APP">%s</xliff:g> aktiválta a VPN-t"</string>
<string name="vpn_text" msgid="3011306607126450322">"Érintse meg a hálózat kezeléséhez."</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 34bebb2..682e191 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Ինքնաթիռային ռեժիմ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Ինքնաթիռային ռեժիմը միացված է"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Ինքնաթիռային ռեժիմը անջատված է"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Կարգավորումներ"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Անվտանգ ռեժիմ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android համակարգ"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Թույլ է տալիս սեփականատիրոջը միանալ Vpn ծառայության վերին մակարդակի ինտերֆեյսին: Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"միանալ պաստառին"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Թույլ է տալիս սեփականատիրոջը միանալ պաստառի վերին մակարդակի ինտերֆեյսին: Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"միանալ հեռակա էկրանին"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Թույլ է տալիս սեփականատիրոջը միանալ հեռակա էկրանի վերին մակարդակի ինտերֆեյսին: Սովորական ծրագրերի համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"միանալ վիջեթ ծառայությանը"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Թույլ է տալիս հավելվածին առբերել, ուսումնասիրել և մաքրել ծանուցումներն, այդ թվում նաև այլ հավելվածների կողմից գրառվածները:"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"միանալ ծանուցումների ունկնդրիչ ծառայությանը"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Թույլ է տալիս սեփականատիրոջը միանալ ծանուցումները ունկնդրող ծառայության վերին մակարդակի ինտերֆեյսին: Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"գործարկել օպերատորի կողմից տրամադրված կազմաձևման ծրագիրը"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Թույլ է տալիս սեփականատիրոջը գործարկել օպերատորի կողմից տրամադրված կազմաձևման ծրագիրը: Սովորական ծրագրերի համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"լսել դիտարկումներ ցանցային պայմանների վերաբերյալ"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Պաստառ"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Փոխել պաստառը"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Ծանուցման ունկնդիր"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN-ը ակտիվացված է"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-ն ակտիվացված է <xliff:g id="APP">%s</xliff:g>-ի կողմից"</string>
<string name="vpn_text" msgid="3011306607126450322">"Հպեք` ցանցի կառավարման համար:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 1100bd0..105da53 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu layanan Vpn. Tidak pernah diperlukan oleh apl normal."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"mengikat ke wallpaper"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu wallpaper. Tidak pernah diperlukan oleh apl normal."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"mengikat ke layar jarak jauh"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Mengizinkan pemegang mengikat ke antarmuka tingkat atas dari layar jarak jauh. Tidak pernah diperlukan untuk aplikasi normal."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"mengikat ke layanan widget"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Mengizinkan aplikasi mengambil, memeriksa, dan menghapus pemberitahuan, termasuk pemberitahuan yang diposkan oleh aplikasi lain."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"mengikat layanan pendengar pemberitahuan"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Memungkinkan pemegang mengikat antarmuka tingkat teratas dari suatu layanan pendengar pemberitahuan. Tidak pernah diperlukan oleh aplikasi normal."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"memanggil aplikasi konfigurasi yang disediakan operator"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Memungkinkan pemegang meminta aplikasi konfigurasi yang disediakan operator. Tidak pernah diperlukan aplikasi normal."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"mendengar untuk observasi kondisi jaringan"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Ubah wallpaper"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Pendengar pemberitahuan"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN diaktifkan"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengelola jaringan."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 243dd15..17530b6 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Consente l\'associazione all\'interfaccia principale di un servizio VPN. Non dovrebbe mai essere necessario per le normali applicazioni."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"associazione a sfondo"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Consente l\'associazione di uno sfondo all\'interfaccia principale. Non dovrebbe mai essere necessaria per le normali applicazioni."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"collega a un display remoto"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Consente al titolare di collegarsi all\'interfaccia di primo livello di un display remoto. Non dovrebbe essere mai necessaria per le normali applicazioni."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"associazione a un servizio widget"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Consente all\'app di recuperare, esaminare e cancellare notifiche, comprese quelle pubblicate da altre app."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincolo a un servizio listener di notifica"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Consente al titolare di vincolarsi all\'interfaccia di primo livello di un servizio listener di notifica. Non dovrebbe mai essere necessaria per le normali applicazioni."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"richiamo dell\'app di configurazione operatore-provider"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Consente al titolare di richiamare l\'app di configurazione dell\'operatore-provider. Non dovrebbe essere mai necessaria per le normali applicazioni."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"ascolto delle osservazioni sulle condizioni di rete"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Sfondo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambia sfondo"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Listener di notifica"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN attiva"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN attivata da <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tocca per gestire la rete."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 537aac6..a633d1f 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"מאפשר למשתמש לבצע איגוד לממשק ברמה עליונה של שירות VPN. הרשאה זו לעולם אינה נחוצה לאפליקציות רגילים."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"קשור לטפט"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"מאפשר למשתמש לבצע איגוד לממשק הרמה העליונה של טפט. הרשאה זו לעולם אינה נחוצה לאפליקציות רגילים."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"איגוד לצג מרוחק"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"הרשאה זו מאפשרת למשתמש לבצע איגוד לממשק הרמה העליונה של צג רחוק. לעולם אינה אמורה להיות נחוצה לאפליקציות רגילות."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"הכפפה לשירות Widget"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"מאפשר לאפליקציה לאחזר, לבדוק ולמחוק התראות, כולל כאלה שפורסמו על ידי אפליקציות אחרות."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"איגוד לשירות של מאזין להתראות"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"הרשאה זו מאפשרת למשתמש לבצע איגוד לממשק הרמה העליונה של שירות מאזין להתראות. הרשאה זו אף פעם אינה נחוצה לאפליקציות רגילים."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"הפעלה של אפליקציית תצורה שסופקה על ידי ספק"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"ההרשאה הזו מאפשרת לבעלים להפעיל את אפליקציית התצורה שסופקה על ידי ספק. לעולם לא אמורה להיות נחוצה עבור אפליקציות רגילות."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"קליטת מעקב אחר תנאי רשת"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"טפט"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"שנה טפט"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"מאזין להתראות"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN מופעל"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN מופעל על ידי <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"גע כדי לנהל את הרשת."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index e334773..8905af6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"機内モード"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"機内モードON"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"機内モードOFF"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"設定"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"セーフモード"</string>
<string name="android_system_label" msgid="6577375335728551336">"Androidシステム"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"VPNサービスのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"壁紙にバインド"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"壁紙のトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"リモートディスプレイへのバインド"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"リモートディスプレイのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ウィジェットサービスにバインド"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"通知(他のアプリから投稿されたものも含む)を取得、調査、クリアすることをアプリに許可します。"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"通知リスナーサービスにバインド"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"通知リスナーサービスのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"携帯通信会社が提供する設定アプリの呼び出し"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"携帯通信会社が提供する設定アプリを呼び出すことを所有者に許可します。通常のアプリでは不要です。"</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"ネットワーク状況監視のためのリッスン"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"壁紙"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"壁紙を変更"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"通知リスナー"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPNが有効になりました"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPNが<xliff:g id="APP">%s</xliff:g>により有効化されました"</string>
<string name="vpn_text" msgid="3011306607126450322">"タップしてネットワークを管理します。"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index b6856c6..a500b8e 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"თვითმფრინავის რეჟიმი"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"თვითმფრინავის რეჟიმი ჩართულია."</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"თვითმფრინავის რეჟიმი გამორთულია."</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"პარამეტრები"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"უსაფრთხო რეჟიმი"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-ის სისტემა"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"აპს შეეძლება Vpn სერვისის ზედა დონის ინტერფეისთან დაკავშირება. არასდროს გამოიყენება ჩვეულებრივ აპებში."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"ფონზე მიჭედება"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"მფლობელს შეეძლება ფონის ზედა დონის ინტერფეისთან დაკავშირება. არასდროს გამოიყენება ჩვეულებრივ აპებში."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"დისტანციურ მონიტორზე მიბმა"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"მფლობელს შეეძლება მიებას დისტანციურ მონიტორის ზედა დონის ინტერფეისს. ჩვეულებრივ აპს ეს წესით არასოდეს უნდა დაჭირდეს."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ვიჯეტ სერვისთან დაკავშირება"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"აპს შეეძლება მოიძიოს, გამოიკვლიოს და წაშალოს შეტყობინებები, მათ შორის სხვა აპების მიერ გამოქვეყნებული."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"შეტყობინებების მოსმენის სერვისთან დაკავშირება"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"მფლობელს შეეძლება შეტყობინებების მსმენლის სერვისის ზედა დონის ინტერფეისთან დაკავშირება. არ უნდა მოხდეს მისი გამოყენება ჩვეუელებრივი აპებისთვის.ფ"</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"ოპერატორის მიერ მოწოდებული კოფიგურაციის აპის გამოხმობა"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"საშუალებას აძლევს მფლობელს გამოიწვიოს ოპერატორის მიერ მოწოდებული კონფიგურაციის აპი. ჩვეულებრივ აპს ეს წესით არასოდეს არ უნდა დაჭირდეს."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"განხორციელდეს ქსელის მდგომარეობის მონიტორინგი"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ფონი"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ფონის შეცვლა"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"შეტყობინებების მსმენელი"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN გააქტიურებულია"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN გააქტიურებულია <xliff:g id="APP">%s</xliff:g>-ის მიერ"</string>
<string name="vpn_text" msgid="3011306607126450322">"შეეხეთ ქსელის სამართავად."</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index bac95d577..43199fa 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្ម Vpn ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"ចងទៅផ្ទាំងរូបភាព"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃផ្ទាំងរូបភាព។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ភ្ជាប់ទៅការបង្ហាញពីចម្ងាយ"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតកំពូលនៃការបង្ហាញពីចម្ងាយ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"ឲ្យកម្មវិធីទៅយក ពិនិត្យ និងសម្អាតការជូនដំណឹង រួមមានប្រកាសដោយកម្មវិធីផ្សេងៗ។"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"ចងទៅសេវាកម្មស្ដាប់ការជូនដំណឹង"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មកម្មវិធីស្ដាប់ការជូនដំណឹង។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"ដកហូតកម្មវិធីកំណត់រចនាសម្ព័ន្ធដែលបានផ្ដល់ដោយក្រុមហ៊ុនបញ្ជូន"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"អនុញ្ញាតឲ្យម្ចាស់ដកហូតកម្មវិធីកំណត់រចនាសម្ព័ន្ធដែលបានផ្ដល់ដោយក្រុមហ៊ុនបញ្ជូន។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"សង្កេតមើលលើលក្ខខណ្ឌបណ្ដាញ"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ផ្ទាំងរូបភាព"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ប្ដូរផ្ទាំងរូបភាព"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"កម្មវិធីស្ដាប់ការជូនដំណឹង"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"បានធ្វើឲ្យ VPN សកម្ម"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"បានធ្វើឲ្យ VPN សកម្មដោយ <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"ប៉ះ ដើម្បីគ្រប់គ្រងបណ្ដាញ។"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 7b064a3..c01ddd8 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"비행기 모드"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"비행기 모드 사용중"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"비행기 모드 사용중이 아님"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"설정"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"안전 모드"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 시스템"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"권한을 가진 프로그램이 VPN 서비스에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"배경화면 연결"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"권한을 가진 프로그램이 배경화면에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"원격 디스플레이에 연결"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"권한을 가진 프로그램이 원격 디스플레이에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"위젯 서비스와 연결"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"앱이 다른 앱에서 게시한 알림을 비롯하여 알림을 검색하고 살펴보며 삭제할 수 있도록 허용합니다."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"알림 수신기 서비스 사용"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"권한을 가진 프로그램이 알림 수신기 서비스에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"이동통신사에서 제공한 구성 앱 호출"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"권한을 가진 프로그램이 이동통신사에서 제공한 구성 앱을 호출하도록 합니다. 일반 앱에는 필요하지 않습니다."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"네트워크 상태에 대한 관측 보고 수신"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"배경화면"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"배경화면 변경"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"알림 수신기"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN이 활성화됨"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN이 <xliff:g id="APP">%s</xliff:g>에 의해 활성화됨"</string>
<string name="vpn_text" msgid="3011306607126450322">"네트워크를 관리하려면 터치하세요."</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index d486092..548d2c0 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ໂໝດໃນຍົນ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ເປີດໂໝດຢູ່ໃນຍົນແລ້ວ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ປິດໂໝດໃນຍົນແລ້ວ"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"ການຕັ້ງຄ່າ"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"ລະບົບ Android"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"ອະນຸຍາດໃຫ້ເຈົ້າຂອງເຊື່ອມໂຍງກັບສ່ວນຕິດຕໍ່ລະດັບເທິງສຸດ ຂອງບໍລິການ VPN. ແອັບຯທົ່ວໄປບໍ່ຄວນຈຳເປັນຕ້ອງໃຊ້."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"ເຊື່ອມໂຍງກັບພາບພື້ນຫຼັງ"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"ອະນຸຍາດໃຫ້ຜູ່ໃຊ້ເຊື່ອມໂຍງກັບສ່ວນຕິດຕໍ່ລະດັບສູງສຸດ ຂອງພາບພື້ນຫຼັງໃດນຶ່ງ. ແອັບຯທຳມະດາບໍ່ຄວນຈຳເປັນຕ້ອງໃຊ້."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ຜູກກັນເພື່ອສະແດງຜົນທາງໄກ."</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"ອະນຸຍາດໃຫ້ຜູ່ຖືຜູກກັບສ່ວນຕິດຕໍ່ລະດັບສູງສຸດ ຂອງການສະແດງຜົນທາງໄກ. ບໍ່ຈຳເປັນສຳລັບແອັບຯທົ່ວໄປ."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ເຊື່ອມໂຍງໄປຫາບໍລິການວິດເຈັດ"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"ອະນຸຍາດໃຫ້ແອັບຯດຶງຂໍ້ມູນ, ກວດສອບ ແລະລຶບລ້າງການແຈ້ງເຕືອນ ຮວມທັງພວກທີ່ໂພສໂດຍແອັບຯອື່ນໆນຳ."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"ເຊື່ອມໂຍງກັບບໍລິການໂຕຟັງການແຈ້ງເຕືອນ"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"ອະນຸຍາດໃຫ້ເຈົ້າຂອງເຊື່ອມໂຍງສ່ວນຕິດຕໍ່ລະດັບເທິງສຸດ ຂອງຜູ່ຟັງບໍລິການການແຈ້ງເຕືອນ. ບໍ່ຈຳເປັນສຳລັບແອັບຯທົ່ວໄປ."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"ຮ້ອງຂໍແອັບຯປັບຄ່າທີ່ສະໜອງໂດຍຜູ່ໃຫ້ບໍລິການ"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"ອະນຸຍາດໃຫ້ເຈົ້າຂອງຮ້ອງຂໍແອັບຯປັບຄ່າທີ່ສະໜອງໂດຍຜູ່ໃຫ້ບໍລິການ. ບໍ່ໜ້າຈະຕ້ອງການສຳລັບແອັບຯທົ່ວໄປ."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"ຕິດຕາມເພື່ອສັງເກດສະພາບຂອງເຄືອຂ່າຍ"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ພາບພື້ນຫຼັງ"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ປ່ຽນພາບພື້ນຫຼັງ"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"ໂຕຟັງການແຈ້ງເຕືອນ"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"ເປີດນຳໃຊ້ VPN ແລ້ວ"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"ເປີດໃຊ້ VPN ໂດຍ <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"ແຕະເພື່ອຈັດການເຄືອຂ່າຍ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index c8724a3..0b25703 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Leidžiama savininkui susisaistyti su aukščiausio lygio VPN paslaugos sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"susaistyti su darbalaukio fonu"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Leidžiama savininką susaistyti su aukščiausio lygio darbalaukio fono sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"susisaistyti su nuotoliniu ekranu"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Leidžiama savininkui susisaistyti su aukščiausiojo lygio nuotolinio ekrano sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"susaistyti su valdiklio paslauga"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Programai leidžiama gauti, patikrinti ir išvalyti pranešimus, įskaitant pranešimus, kuriuos paskelbė kitos programos."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"susisaistyti su pranešimų skaitymo priemonės paslauga"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Leidžiama turėtojui susisaistyti su pranešimų skaitymo priemonės paslaugos aukščiausio lygio sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"iškviesti operatoriaus pateiktą konfigūravimo programą"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Turėtojui leidžiama iškviesti operatoriaus pateiktą konfigūravimo programą. Įprastoms programoms to neturėtų prireikti."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"vykdyti tinklo sąlygų stebėjimą"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Darbalaukio fonas"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Keisti darbalaukio foną"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Pranešimų skaitymo priemonė"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN suaktyvintas"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN suaktyvino „<xliff:g id="APP">%s</xliff:g>“"</string>
<string name="vpn_text" msgid="3011306607126450322">"Palieskite, kad valdytumėte tinklą."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index f2aec67..f2415d5 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Ļauj īpašniekam izveidot saiti ar VPN pakalpojuma augšējā līmeņa saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"saistīt ar tapeti"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Ļauj īpašniekam piesaistīt tapetes augstākā līmeņa lietotāja saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"Saites izveide ar attālu displeju"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Ļauj īpašniekam izveidot saiti ar attāla displeja augšējā līmeņa saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"saistīt ar logrīka pakalpojumu"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Ļauj lietotnei izgūt, pārbaudīt un dzēst paziņojumus, tostarp lietotņu publicētos paziņojumus."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"saites izveidošana ar paziņojumu uztvērēja pakalpojumu"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ļauj īpašniekam izveidot saiti ar paziņojumu uztvērēja pakalpojuma augšējā līmeņa saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"Operatora nodrošinātas konfigurācijas lietotnes izsaukšana"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Ļauj īpašniekam izsaukt operatora nodrošināto konfigurācijas lietotni. Parastām lietotnēm tas nekad nav nepieciešams."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"iegūt informāciju par tīkla stāvokļa novērojumiem"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fona tapete"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Tapetes maiņa"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Paziņojumu uztvērējs"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN ir aktivizēts."</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Lietojumprogramma <xliff:g id="APP">%s</xliff:g> aktivizēja VPN."</string>
<string name="vpn_text" msgid="3011306607126450322">"Pieskarieties, lai pārvaldītu tīklu."</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 1006907f..8fd4fba 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Эзэмшигч нь VPN үйлчилгээний дээд-төвшиний интерфейстэй холбох боломжтой. Энгийн апп-д шаардлагагүй."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"ханын зурагтай холбох"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Эзэмшигч нь ханын зурагны дээд-төвшиний интерфейстэй холбох боломжтой. Энгийн апп-уудад шаардлагагүй."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"алсын дэлгэцтэй холбогдох"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Эзэмшигчид алсын дэлгэц дэх дээд давхаргын интерфэйстэй холбогдох боломж олгоно. Энгийн апп-д шаардагдахгүй."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"виджет үйлчилгээтэй холбох"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Апп нь бусад апп-уудын илгээсэн мэдэгдлүүдийг дуудах, шалгах, болон цэвэрлэх боломжтой."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"мэдэгдэл сонсогч үйлчилгээтэй холбох"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Эзэмшигч нь мэдэгдэл сонсох үйлчилгээний дээд-төвшиний интерфейстэй холбох боломжтой. Энгийн апп-д шаардлагагүй."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"үүрэн компанийн нийлүүлсэн тохируулгын апп-г өдөөх"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Эзэмшигчид үүрэн компанийн нийлүүлсэн тохируулах апп-г өдөөх боломж олгоно. Энгийн апп-уудад хэзээ ч ашиглагдахгүй."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"Сүлжээний байдлын талаар ажиглалтуудыг хүлээн авах"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Ханын зураг"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Ханын зураг солих"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Мэдэгдэл сонсогч"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN идэвхтэй болов"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-г <xliff:g id="APP">%s</xliff:g> идэвхтэй болгов"</string>
<string name="vpn_text" msgid="3011306607126450322">"Сүлжээг удирдах бол хүрнэ үү."</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index d3b5987..d0d4223 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mod pesawat"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Mod Pesawat DIHIDUPKAN"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Mod Pesawat DIMATIKAN"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Tetapan"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Mod selamat"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan Vpn. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"terikat pada kertas dinding"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi kertas dinding. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"terikat kepada paparan jauh"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi paparan jauh. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"terikat kepada perkhidmatan widget"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Membenarkan apl untuk mendapatkan semula, memeriksa dan memadam bersih pemberitahuan, termasuk yang disiarkan oleh apl lain."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"ikat kepada perkhidmatan pendengar pemberitahuan"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan pendengar pemberitahuan. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"gunakan apl konfigurasi yang disediakan oleh pembawa"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Membenarkan pemegang menggunakan apl konfigurasi yang diberikan oleh pembawa. Tidak sekali-kali diperlukan untuk apl biasa."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"dengar pemerhatian mengenai keadaan rangkaian"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Kertas dinding"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Tukar kertas dinding"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Pendengar pemberitahuan"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN diaktifkan"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengurus rangkaian."</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index d95d6f1..2c90ba3 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flymodus"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Flymodus er på"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flymodus er av"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Innstillinger"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikkermodus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Lar innehaveren binde seg til det øverste nivået av grensesnittet for en VPN-tjeneste. Skal aldri være nødvendig for vanlige apper."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"binde til bakgrunnsbilde"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Lar innehaveren binde det øverste nivået av grensesnittet til en bakgrunn. Skal aldri være nødvendig for vanlige apper."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"binde til ekstern skjerm"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Lar innehaveren binde seg til det øverste grensesnittnivået for ekstern skjerm. Skal aldri være nødvendig for vanlige apper."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"binde til modultjenste"</string>
@@ -396,7 +399,7 @@
<string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"Innehaveren av tillatelsen kan binde seg til ruteleverandører. Dette er ikke nødvendig for vanlige apper."</string>
<string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"kommunisere med enhetsadministrator"</string>
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"Lar innehaveren sende hensikter til en enhetsadministrator. Skal aldri være nødvendig for normale apper."</string>
- <string name="permlab_bindTvInput" msgid="5601264742478168987">"binde applikasjonen til en TV-inngang"</string>
+ <string name="permlab_bindTvInput" msgid="5601264742478168987">"binde appen til en TV-inngang"</string>
<string name="permdesc_bindTvInput" msgid="2371008331852001924">"Lar innehaveren binde appen til det øverste grensesnittnivået for en TV-inngang. Dette skal aldri være nødvendig for vanlige apper."</string>
<string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"legge til eller fjerne en enhetsadministrator"</string>
<string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"Tillater innehaveren å legge til eller fjerne aktive enhetsadministratorer. Dette skal aldri være nødvendig for vanlige apper."</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Lar appen hente, gjennomgå og fjerne varsler, inkludert de som sendes fra andre apper."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"binding til en varsellyttertjeneste"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lar innehaveren binde seg til det øverste grensesnittnivået for en varsellyttertjeneste. Skal aldri være nødvendig for vanlige apper."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"starte konfigurasjonsappen som ble levert av operatøren"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Gir innehaveren tillatelse til å kalle opp den konfigurasjonsappen som ble levert av operatøren. Dette skal ikke være nødvendig for vanlige apper."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"lytte etter observasjoner om nettverksforhold"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Bakgrunnsbilde"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Velg bakgrunnsbilde"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Varsellytteren"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN er aktivert"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN er aktivert av <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Trykk for å administrere nettverket."</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index c629254..7821648 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Staat de houder toe verbinding te maken met de hoofdinterface van een VPN-service. Nooit vereist voor normale apps."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"verbinden met een achtergrond"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Hiermee wordt de houder toegestaan zich te verbinden met de hoofdinterface van een achtergrond. Nooit vereist voor normale apps."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"verbinding maken met een extern display"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Hiermee wordt de houder toegestaan verbinding te maken met de hoofdinterface van een extern display. Nooit vereist voor normale apps."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"verbinden met een widgetservice"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Hiermee kan de app meldingen ophalen, onderzoeken en wissen, waaronder meldingen die zijn verzonden door andere apps."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"koppelen aan een listener-service voor meldingen"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Hiermee kan de houder koppelen aan de hoofdinterface van een listener-service voor meldingen. Nooit vereist voor normale apps."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"de door de provider geleverde configuratie-app aanroepen"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Hiermee kan de houder de door de provider geleverde configuratie-app aanroepen. Nooit vereist voor normale apps."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"controleren op waarnemingen met betrekking tot netwerkomstandigheden"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Achtergrond"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Achtergrond wijzigen"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Listener voor meldingen"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN is geactiveerd"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN wordt geactiveerd door <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Raak aan om het netwerk te beheren."</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 34d43de..3cbd980 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Pozwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi VPN. Nie powinno być nigdy potrzebne w przypadku zwykłych aplikacji."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"powiązanie z tapetą"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Pozwala na tworzenie powiązania z interfejsem najwyższego poziomu tapety. Nieprzeznaczone dla zwykłych aplikacji."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"powiązanie z wyświetlaczem zdalnym"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Zezwala na tworzenie powiązania z interfejsem najwyższego poziomu wyświetlacza zdalnego. Nieprzeznaczone dla zwykłych aplikacji."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"powiązanie z usługą widżetów"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Umożliwia aplikacji pobieranie, sprawdzanie i usuwanie powiadomień, także tych, które pochodzą z innych aplikacji."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"utwórz połączenie z usługą odbiornika powiadomień"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Zezwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi odbiornika powiadomień. Nie powinno być nigdy potrzebne dla zwykłych aplikacji."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"wywoływanie aplikacji konfiguracyjnej udostępnionej przez operatora"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Zezwala na wywoływanie aplikacji konfiguracyjnej udostępnionej przez operatora. Nieprzeznaczone dla zwykłych aplikacji."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"śledź stan sieci"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapeta"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Zmień tapetę"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Odbiornik powiadomień"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN aktywny"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Obsługa sieci VPN została włączona przez aplikację <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotknij, aby zarządzać siecią."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6996d13..1ad8fad 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permite que o titular se vincule à interface de nível superior de um serviço de VPN. Nunca deverá ser necessário para aplicações normais."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"vincular a uma imagem de fundo"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite ao titular vincular-se à interface de nível superior de uma imagem de fundo. Nunca deverá ser necessário para aplicações normais."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"associar a um ecrã remoto"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Permite ao detentor associar a interface de nível superior a um ecrã remoto. Nunca deve ser necessário para aplicações normais."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"vincular a um serviço de widget"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que a aplicação obtenha, examine e limpe notificações, incluindo as que foram publicadas por outras aplicações."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincular a um serviço de escuta de notificações"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite que o titular vincule a interface de nível superior de um serviço de escuta de notificações. Nunca deverá ser necessário para aplicações normais."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"invocar a aplicação de configuração fornecida pela operadora"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permite que o titular invoque a aplicação de configuração fornecida pela operadora. Nunca deverá ser necessário para aplicações normais."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"ouvir observações sobre as condições da rede"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Imagem de fundo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Alterar imagem de fundo"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Serviço de escuta de notificações"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN ativada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"A VPN foi ativada pelo <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toque para gerir a rede."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a0124ca..77e54a4 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avião"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modo avião ATIVADO"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modo avião DESATIVADO"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Configurações"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permite que seu proprietário sujeite a interface de alto nível de um serviço de VPN. Nunca deve ser necessário para aplicativos normais."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"sujeitar-se a um plano de fundo"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite que o proprietário utilize interface de nível superior de um plano de fundo. Nunca deve ser necessário para aplicativos normais."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"usar uma tela remota"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Permite que o proprietário use a interface de nível superior de uma tela remota. Não deve ser necessário para aplicativos comuns."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"sujeitar-se a um serviço de widget"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que o aplicativo recupere, examine e limpe notificações, inclusive as postadas por outros aplicativos."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"sujeitar a um serviço ouvinte de notificações"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite que o proprietário sujeite a interface de nível superior a um serviço ouvinte de notificações. Não deve ser necessário para aplicativos comuns."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"invocar o aplicativo de configuração fornecido pela operadora"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permite que o proprietário invoque o aplicativo de configuração fornecido pela operadora. Não deve ser necessário para aplicativos comuns."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"detectar observações nas condições da rede"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Plano de fundo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Alterar plano de fundo"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Ouvinte de notificações"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN ativada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"A VPN está ativada por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toque para gerenciar a rede."</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index dc710d4..a4d58411 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -608,6 +608,10 @@
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"sa fixar vid in fund davos"</string>
<!-- no translation found for permdesc_bindWallpaper (7108428692595491668) -->
<skip />
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<!-- no translation found for permlab_bindRemoteDisplay (1782923938029941960) -->
<skip />
<!-- no translation found for permdesc_bindRemoteDisplay (1261242718727295981) -->
@@ -1190,6 +1194,10 @@
<skip />
<!-- no translation found for permdesc_bindNotificationListenerService (985697918576902986) -->
<skip />
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
<skip />
<!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
@@ -2188,6 +2196,8 @@
<string name="chooser_wallpaper" msgid="7873476199295190279">"Midar il fund davos"</string>
<!-- no translation found for notification_listener_binding_label (2014162835481906429) -->
<skip />
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<!-- no translation found for vpn_title (19615213552042827) -->
<skip />
<!-- no translation found for vpn_title_long (6400714798049252294) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 0757578..8a69824 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mod Avion"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modul Avion este ACTIVAT"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modul avion este DEZACTIVAT"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Setări"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"˃999"</string>
<string name="safeMode" msgid="2788228061547930246">"Mod sigur"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistemul Android"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Permite proprietarului să se conecteze la interfaţa de nivel superior a unui serviciu VPN. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"conectare la o imagine de fundal"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Permite proprietarului să se conecteze la interfaţa de nivel superior a unei imagini de fundal. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"conectare la un ecran la distanță"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Permite proprietarului să se conecteze la interfața de nivel superior a unui ecran la distanță. Nu ar trebui să fie niciodată necesară pentru aplicațiile obișnuite."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"conectare la un serviciu widget"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite aplicației să recupereze, să examineze și să șteargă notificări, inclusiv pe cele postate de alte aplicații."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"conectare la un serviciu de citire a notificărilor"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite proprietarului să se conecteze la interfața de nivel superior a unui serviciu de citire a notificărilor. În mod normal aplicațiile nu ar trebui să aibă nevoie de această permisiune."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"apelarea aplicației de configurare furnizată de operator"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permite proprietarului să apeleze aplicația de configurare furnizată de operator. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"ascultă observații despre starea rețelei"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Imagine de fundal"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Modificaţi imaginea de fundal"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Serviciu de citire a notificărilor"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN activat"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN este activată de <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Atingeţi pentru a gestiona reţeaua."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index f826f1a..70e7044 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Режим полета"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Выключить"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Включить"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Настройки"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
<string name="safeMode" msgid="2788228061547930246">"Безопасный режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Система Android"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Приложение сможет подключаться к базовому интерфейсу службы VPN. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"Привязка к фоновому рисунку"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Приложение сможет подключаться к базовому интерфейсу службы обоев. Это разрешение не используется обычными приложениями."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"Подключение к удаленному дисплею"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Приложение сможет подключаться к базовому интерфейсу удаленного дисплея. Это разрешение обычно используется только специальными приложениями."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"Подключение к службе виджетов"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Приложение сможет получать, проверять и удалять уведомления, включая те, что опубликованы другими приложениями."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"Подключение к службе просмотра уведомлений"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Приложение сможет подключаться к базовому интерфейсу службы просмотра уведомлений. Это разрешение не используется обычными приложениями."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"Запуск приложения настроек, предоставленного оператором"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Владелец сможет запускать приложение настроек, предоставленное оператором. Это разрешение не используется обычными приложениями."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"Использование данных о состоянии сети"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Фоновый рисунок"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Сменить обои"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Служба просмотра уведомлений"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"Сеть VPN активна"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Сеть VPN активирована приложением <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Нажмите, чтобы открыть настройки."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 09cf7f6..055d23b 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Režim V lietadle"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Režim V lietadle je ZAPNUTÝ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Režim V lietadle je VYPNUTÝ"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Nastavenia"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Núdzový režim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Systém Android"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania služby VPN. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"väzba na tapetu"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania tapety. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"viazať na vzdialený displej"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Umožňuje držiteľovi viazať sa na najvyššiu úroveň rozhrania vzdialeného displeja. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"viazať sa k službe miniaplikácie"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Umožňuje aplikácii načítať, zobrazovať a mazať upozornenia vrátane tých, ktoré boli uverejnené inými aplikáciami."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"naviazanie sa na službu na počúvanie upozornení"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Umožňuje držiteľovi naviazať sa na najvyššiu úroveň služby na počúvanie upozornení. Bežné aplikácie by toto nastavenie nemali nikdy požadovať."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"vyvolanie aplikácie pre konfiguráciu poskytnutú operátorom"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Umožňuje držiteľovi vyvolať aplikáciu pre konfiguráciu poskytnutú operátorom. Bežné aplikácie by toto povolenie nemali nikdy potrebovať."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"zachytávať informácie o stave siete"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapeta"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Zmeniť tapetu"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Aplikácia na počúvanie upozornení"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"Sieť VPN je aktivovaná"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikáciu <xliff:g id="APP">%s</xliff:g> aktivovala sieť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykom môžete spravovať sieť."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 00649f4..2a04337 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Lastniku omogoča povezovanje z vmesnikom storitve navideznega zasebnega omrežja najvišje ravni. Ne uporabljajte za navadne programe."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"povezovanje z ozadjem"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Imetniku omogoča povezavo z vmesnikom ozadja najvišje ravni. Tega nikoli ni treba uporabiti za navadne programe."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"povezava z oddaljenim prikazom"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Imetniku omogoča povezovanje z vmesnikom oddaljenega prikaza najvišje ravni. Tega ni treba nikoli uporabiti za navadne aplikacije."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"poveži s storitvijo pripomočka"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Dovoli aplikaciji, da prenese, razišče in izbriše obvestila, tudi tista, ki so jih objavile druge aplikacije."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"poveži se s storitvijo poslušalca obvestil"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lastniku omogoča povezovanje z vmesnikom storitve poslušalca obvestil najvišje ravni. Tega nikoli ni treba uporabiti za navadne aplikacije."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"sprožitev operaterjeve aplikacije za konfiguracijo"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Lastniku omogoča sproženje operaterjeve aplikacije za konfiguracijo. Tega nikoli ni treba uporabiti za navadne aplikacije."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"spremljanje razmer v omrežju"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Ozadje"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Spreminjanje ozadja"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Poslušalec obvestil"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN aktiviran"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN je aktiviral program <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotaknite se, če želite upravljati omrežje."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index d363771..5d03ffa 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Режим рада у авиону"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Режим рада у авиону је УКЉУЧЕН"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Режим рада у авиону је ИСКЉУЧЕН"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Подешавања"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Безбедни режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android систем"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Дозвољава власнику да се повеже са интерфејсом VPN услуге највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"обавезивање на позадину"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Дозвољава власнику да се повеже са интерфејсом позадине највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"повезивање са удаљеним екраном"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Дозвољава власнику да се повеже са интерфејсом удаљеног екрана највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"обавезивање на услугу виџета"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Дозвољава апликацији да преузима, испитује и брише обавештења, укључујући она која постављају друге апликације."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"повезивање са услугом монитора обавештења"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Дозвољава власнику да се повеже са интерфејсом услуге монитора обавештења највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"позивање апликације са конфигурацијом коју одређује оператер"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Дозвољава власнику да позива апликацију са конфигурацијом коју одређује оператер. Уобичајене апликације никада не би требало да је користе."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"праћење података о условима на мрежи"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Позадина"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Промена позадине"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Монитор обавештења"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN је активиран"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Апликација <xliff:g id="APP">%s</xliff:g> је активирала VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Додирните да бисте управљали мрежом."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 7ec4f41..3599074 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en VPN-tjänst. Ska inte behövas för vanliga appar."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"binda till en bakgrund"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Innehavaren kan binda till den översta nivåns gränssnitt för en bakgrund. Ska inte behövas för vanliga appar."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"bind till en fjärrskärm"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en fjärrskärm. Ska inte behövas för vanliga appar."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bind till en widget"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillåter att appen hämtar, granskar och raderar meddelanden, även sådana som skickats av andra appar."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"binda till en meddelandelyssnare"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en meddelandelyssnare. Ska inte behövas för vanliga appar."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"anropa konfigurationsappen från operatören"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Innehavaren tillåts att anropa konfigurationsappen från operatören. Ska inte behövas för vanliga appar."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"lyssna efter information om nätverksförhållanden"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Bakgrund"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Ändra bakgrund"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Meddelandelyssnare"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN är aktiverat"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveras av <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tryck om du vill hantera nätverket."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 40adc3e..0c21017 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Hali ya ndege"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Hali ya ndege IMEWASHWA"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Hali ya ndege IMEZIMWA"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Mipangilio"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Mtindo salama"</string>
<string name="android_system_label" msgid="6577375335728551336">"Mfumo wa Android"</string>
@@ -255,7 +254,7 @@
<string name="permdesc_statusBarService" msgid="716113660795976060">"Inaruhusu programu kuwa upau wa hali."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"panua/kunja mwambaa hali"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Inaruhusu programu kupanua au kukunja upau wa hali."</string>
- <string name="permlab_install_shortcut" msgid="4279070216371564234">"sakinisha njia za mkato"</string>
+ <string name="permlab_install_shortcut" msgid="4279070216371564234">"kuweka njia za mkato"</string>
<string name="permdesc_install_shortcut" msgid="8341295916286736996">"Huruhusu programu kuongeza njia za mkato za Skrini ya kwanza bila mtumiaji kuingilia."</string>
<string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"ondoa njia za mikato"</string>
<string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Huruhusu programu kuondoa njia za mkato za Skrini ya kwanza bila mtumiaji kuingilia."</string>
@@ -283,7 +282,7 @@
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Inaruhusu programu kupokea na kuchakata ujumbe wa WAP. Idhini hii inajumuisha uwezo wa kuchunguza na kufuta ujumbe uliotumwa kwako bila ya kukuonyesha."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"rudisha programu zinazoendeshwa"</string>
<string name="permdesc_getTasks" msgid="7454215995847658102">"Inaruhusu programu kurudisha taarifa kuhusu kazi zinazoendeshwa sasa na hivi karibuni. Hii inaweza kuruhusu programu kugundua taarifa kuhusu ni programu zipi zinazotumika kwenye kifaa."</string>
- <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"Tagusana na watumiaji"</string>
+ <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kuwasiliana na watumiaji wengine"</string>
<string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Inaruhusu programu kutenda vitendo kwa watumiaji tofauti kwenye kifaa. Programu hasidi huenda zikatumia hii ili kukiuka ulinzi kati ya watumiaji."</string>
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"leseni kamili ili kushirikiana na watumiaji"</string>
<string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Inaruhusu miingialiano yote inayowezekana kwa watumiaji."</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Inaruhusu kishikiliaji kushurutisha kusano ya kiwango cha juu cha huduma ya Vpn. Haipaswi kuhitajika kwa programu za kawaida."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"funga kwa mandhari"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Inaruhusu kishikiliaji kushurutisha kwa kusano ya kiwango cha juu cha mandhari. Haipaswi kamwe kuhitajika kwa programu za kawaida."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"fungisha kwenye mwonekano wa mbali"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Huruhusu mtumiaji kujifungia kiolesura cha kiwango cha juu cha mwonekano wa mbali. Haipaswi kuhitajika kwa programu za kawaida."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"funga kwenye huduma ya widget"</string>
@@ -421,7 +424,7 @@
<string name="permdesc_getPackageSize" msgid="3921068154420738296">"Inaruhusu Programu kupata tena msimbo, data na ukubwa wa kache yake."</string>
<string name="permlab_installPackages" msgid="2199128482820306924">"sakinisha programu moja kwa moja"</string>
<string name="permdesc_installPackages" msgid="5628530972548071284">"Inaruhusu programu kusakanisha au kusasisha furushi mpya za Android. Programu hasidi zinaweza kutumia hii kuongeza programu mpya ambazo zina ruhusa zenye nguvu."</string>
- <string name="permlab_clearAppCache" msgid="7487279391723526815">"Futa data yote kwenye kache ya programu"</string>
+ <string name="permlab_clearAppCache" msgid="7487279391723526815">"kufuta data yote kwenye akiba ya programu"</string>
<string name="permdesc_clearAppCache" product="tablet" msgid="8974640871945434565">"Inaruhusu programu kutoa nafasi ya hifadhi ya kompyuta ndogo kwa kufuta faili katika saraka za kache za programu nyingine. Huenda hii ikasababisha programu nyingine kuanza polepole zaidi kwa sababu zinahitaji kuepua data zazo."</string>
<string name="permdesc_clearAppCache" product="default" msgid="2459441021956436779">"Inaruhusu programu kutoa nafasi ya hifadhi ya simu kwa kufuta faili katika saraka za kache za programu nyingine. Huenda hii ikasababisha programu nyingine kuanza polepole zaidi kwa sababu zinahitaji kuepua data zazo."</string>
<string name="permlab_movePackage" msgid="3289890271645921411">"songesha rasilimali ya programu"</string>
@@ -614,13 +617,13 @@
<string name="permdesc_manageAccounts" msgid="8698295625488292506">"Inaruhusu programu kutekeleza shughuli kama vile kuongeza na kutoa akaunti, na kufuta manenosiri yazo."</string>
<string name="permlab_useCredentials" msgid="235481396163877642">"kutumia akaunti zilizo kwenye kifaa"</string>
<string name="permdesc_useCredentials" msgid="7984227147403346422">"Inaruhusu programu kuomba shuhuda za uthibitisho."</string>
- <string name="permlab_accessNetworkState" msgid="4951027964348974773">"Kuangalia mitandao"</string>
+ <string name="permlab_accessNetworkState" msgid="4951027964348974773">"kuona mitandao"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Inaruhusu programu kuona taarifa kuhusu miunganisho ya mtandao kama vile mitandao ipi iliyopo na imeunganishwa."</string>
<string name="permlab_createNetworkSockets" msgid="8018758136404323658">"ufikiaji kamili wa mtandao"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Inaruhusu programu kuunda soketi za mtandao na kutumia itifaki za mtandao maalum. Kivinajri na programu nyingine zilizotolewa zinamaanisha kutuma data kwenye mtandao, kwa hivyo kibali hiki hakihitajiki kutuma data kwenye mtandao."</string>
<string name="permlab_writeApnSettings" msgid="505660159675751896">"mabadiliko / kuingilia mipangilio ya mtandao/msonmgamano"</string>
<string name="permdesc_writeApnSettings" msgid="5333798886412714193">"Inaruhusu programu kubadilisha mipangilio ya mtandao na kukatiza na kukagua uendaji wa mtandao, kwa mfano kubadilisha kituo tarishi na mbadala cha APN yoyote. Programu hasidi zinaweza kuangalia, kuelekeza kwingine, au kurekebisha furushi za mtandao bila ya wewe kujua."</string>
- <string name="permlab_changeNetworkState" msgid="958884291454327309">"badilisha muunganisho wa mtandao"</string>
+ <string name="permlab_changeNetworkState" msgid="958884291454327309">"kubadilisha muunganisho wa mtandao"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Inaruhusu programu kubadilisha hali ya muunganisho wa mtandao."</string>
<string name="permlab_changeTetherState" msgid="5952584964373017960">"Badilisha muunganisho uliofunganishwa"</string>
<string name="permdesc_changeTetherState" msgid="1524441344412319780">"Inaruhusu programu kubadilisha hali ya muunganisho wa mtandao uliofungwa."</string>
@@ -650,25 +653,25 @@
<string name="permlab_bluetooth" msgid="6127769336339276828">"oanisha na vifaa vya Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Inaruhusu programu kuona usanidi wa Bluetooth kwenye kompyuta kibao, na kuunda na kukubali miunganisho kwa vifaa vilivyooanishwa."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Inaruhusu programu kuona usanidi wa Bluetooth kwenye simu, na kuunda na kukubali miunganisho kwa vifaa vilivyooanishwa."</string>
- <string name="permlab_nfc" msgid="4423351274757876953">"dhibiti Mawasiliano ya vifaa vilivyo Karibu"</string>
+ <string name="permlab_nfc" msgid="4423351274757876953">"kudhibiti Mawasiliano ya Vifaa Vilivyokaribu (NFC)"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"Inaruhusu programu kuwasiliana na lebo, kadi na wasomaji wa Near Field Communication (NFC)."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"zima kufuli la skrini yako"</string>
<string name="permdesc_disableKeyguard" msgid="6034203065077122992">"Inaruhusu programu kulemaza ufunguo wa vitufe na usalama mwingine ambata wa nenosiri. Kwa mfano, simu inalemaza ufunguo wa viitufe inapopokea simu inayoingia, kisha inawezesha upya ufunguo wa vitufe wakati simu inapokamilika."</string>
- <string name="permlab_readSyncSettings" msgid="6201810008230503052">"soma mipangilio ya usawazishaji"</string>
+ <string name="permlab_readSyncSettings" msgid="6201810008230503052">"kusoma mipangilio ya usawazishaji"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Inaruhusu programu kusoma mipangilio ya upatanishi wa akaunti. Kwa mfano, huku kunaweza kuamua kama programu ya Watu imepatanishwa na akaunti."</string>
- <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"washa na uzime usawazishaji"</string>
+ <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"kuwasha na kuzima usawazishaji"</string>
<string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Inaruhusu programu kurekebisha mipangalio ya upatanishi wa akaunti. Kwa mfano, hii inaweza kuwezesha programu ya upatanishi wa Watu na akaunti."</string>
- <string name="permlab_readSyncStats" msgid="7396577451360202448">"soma takwimu za usawazishaji"</string>
+ <string name="permlab_readSyncStats" msgid="7396577451360202448">"kusoma takwimu za usawazishaji"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"Inaruhusu programu kusoma takwimu za upatanishi za akaunti, ikiwa ni pamoja na historia ya matukio ya upatanishi na kiasi cha data kimepatanishwa."</string>
- <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"soma milisho ya kujiunga"</string>
+ <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"kusoma mipasho kutoka vyanzo unavyofuatilia"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Inaruhusu programu kupata maelezo kuhusu mlisho iliyolandanishwa kwa sasa."</string>
- <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"andika milisho ya kujiunga"</string>
+ <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"kuandika mipasho kutoka vyanzo unavyofuatilia"</string>
<string name="permdesc_subscribedFeedsWrite" msgid="6928930188826089413">"Inaruhusu programu kurekebisha milisho yako iliyolandanishwa kwa sasa. Programu hasidi zinaweza kubadilisha milisho yako iliyolandanishwa."</string>
<string name="permlab_readDictionary" msgid="4107101525746035718">"soma maneno uliyoongeza kwenye kamusi"</string>
<string name="permdesc_readDictionary" msgid="659614600338904243">"Inaruhusu programu kusoma maneno, majina na misemo yote ambayo mtumiaji alihifadhi katika kamusi ya mtumiaji."</string>
<string name="permlab_writeDictionary" msgid="2183110402314441106">"ongeza maneno katika kamusi ya mtumiaji iliyofafanuliwa"</string>
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Inaruhusu programu kuandika maneno mapya katika kamusi ya mtumiaji."</string>
- <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"soma maudhui ya hifadhi yako ya USB"</string>
+ <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"kusoma maudhui yaliyo kwenye hifadhi yako ya USB"</string>
<string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"soma maudhui ya kadi yako ya SD"</string>
<string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"Huruhusu programu kusoma maudhui ya hifadhi ya USB."</string>
<string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"Huruhusu programu kusoma maudhui ya kadi yako ya SD."</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Huruhusu programu kurejesha, kuchunguza, na kuondoa arifa, ikiwa ni pamoja na zile zilizochapishwa na programu nyingine."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"unganisha kwenye huduma ya kisikilizi cha arifa"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Inaruhusu kishikilizi kuunganishwa kwenye kusano cha kiwango cha juu cha huduma ya kisikilizi cha arifa. Haipaswi kuhitajika tena kwa programu za kawaida."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"omba programu ya usakinishaji inayotolewa na mtoa huduma."</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Inaruhusu kishikiliaji kuomba programu ya usakinishaji inayotolewa na mto huduma. Haipaswi kuhitajika kwa programu za kawaida."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"sikiliza matukio katika hali za mtandao"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Mandhari"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Badilisha mandhari"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Kisikilizi cha arifa"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN imewezeshwa"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN imeamilishwa na <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Gusa ili kudhibiti mtandao."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index c211018..ded9706 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"อนุญาตให้เจ้าของเชื่อมโยงกับส่วนติดต่อผู้ใช้ระดับสูงสุดของบริการ VPN ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"เชื่อมโยงกับวอลเปเปอร์"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"อนุญาตให้ผู้ใช้เชื่อมโยงกับส่วนติดต่อผู้ใช้ระดับสูงสุดของวอลเปเปอร์ ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ผูกกับจอแสดงผลระยะไกล"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"อนุญาตให้ผู้ใช้ผูกกับอินเทอร์เฟซระดับสูงสุดของจอแสดงผลระยะไกล ซึ่งแอปพลิเคชันทั่วไปไม่จำเป็นต้องใช้"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"เชื่อมโยงกับบริการวิดเจ็ต"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"ทำให้แอปสามารถเรียกดู ตรวจสอบ และล้างการแจ้งเตือนได้ ซึ่งรวมถึงการแจ้งเตือนที่โพสต์โดยแอปอื่นๆ ด้วย"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"เชื่อมโยงกับบริการตัวฟังการแจ้งเตือน"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"อนุญาตให้เจ้าของเชื่อมโยงกับอินเตอร์เฟซระดับสูงสุดของบริการตัวฟังการแจ้งเตือน ซึ่งไม่มีความจำเป็นสำหรับแอปธรรมดา"</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"เรียกใช้แอปการกำหนดค่าของผู้ให้บริการ"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"อนุญาตให้ผู้ใช้สามารถเรียกใช้แอปการกำหนดค่าของผู้ให้บริการ ซึ่งแอปทั่วไปไม่จำเป็นต้องใช้"</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"ฟังข้อสังเกตเกี่ยวกับสภาวะของเครือข่าย"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"วอลเปเปอร์"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"เปลี่ยนวอลเปเปอร์"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"ตัวฟังการแจ้งเตือน"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN เปิดใช้งานแล้ว"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"เปิดใช้งาน VPN โดย <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"แตะเพื่อจัดการเครือข่าย"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index a7b7b8f..4d2d0db 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Airplane mode"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Naka-ON ang airplane mode"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Naka-OFF ang airplane mode"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Mga Setting"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Pinapayagan ang may-hawak na sumailalim sa nangungunang interface ng serbisyo ng Vpn. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"sumailalim sa wallpaper"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Pinapayagan ang may-hawak na sumailalim sa nangungunang interface ng isang wallpaper. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"magpasaklaw sa isang remote na display"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Binibigyang-daan ang may-hawak na masaklaw ang pinakamataas na antas ng interface ng isang remote na display. Hindi dapat kailanman kailanganin ng normal na apps."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"itali sa serbisyo ng widget"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Pinapayagan ang app na kumuha, sumuri, at mag-clear ng mga notification, kabilang ang mga na-post ng iba pang apps."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"mapailalim sa isang serbisyo ng notification listener"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Nagbibigay-daan sa may-ari na mapailalim sa interface sa tuktok na antas ng isang serbisyo ng notification listener. Hindi dapat kailanganin para sa karaniwang apps kahit kailan."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"paganahin ang app ng configuration na ibinigay ng carrier"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Nagbibigay-daan sa may-ari na paganahin ang app ng configuration na ibinigay ng carrier. Hindi dapat kailanganin para sa normal na apps kahit kailan."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"makinig sa mga obserbasyon sa mga kundisyon ng network"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Baguhin ang wallpaper"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Notification listener"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"Naka-activate ang VPN"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Isinaaktibo ang VPN ng <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Pindutin upang pamahalaan ang network."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index bc73a93f8..8c97507 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Uçak modu"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Uçak modu AÇIK"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Uçak modu KAPALI"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Ayarlar"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Güvenli mod"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android Sistemi"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Cihazın sahibine bir VPN hizmetinin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"bir duvar kağıdına tabi kıl"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Cihazın sahibine, duvar kağıdının en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"uzak ekrana bağlan"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"İzin sahibine, bir uzak ekranın en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bir widget hizmetine bağla"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Uygulamanın bildirimler almasına, bildirimleri incelemesine ve temizlemesine izin verir. Buna diğer uygulamalar tarafından yayınlanan bildirimler de dahildir."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bildirim dinleyici hizmetine bağlan"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"İzin sahibine bir bildirim dinleyici hizmetinin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"operatör tarafından sağlanan yapılandırma uygulamasını çalıştır"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"İzin sahibine, operatör tarafından sağlanan yapılandırma uygulamasını çalıştırma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"ağ koşullarındaki gözlemleri dinle"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Duvar Kağıdı"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Duvar kağıdını değiştir"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Bildirim dinleyici"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN etkinleştirildi"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN, <xliff:g id="APP">%s</xliff:g> tarafından etkinleştirildi"</string>
<string name="vpn_text" msgid="3011306607126450322">"Ağı yönetmek için dokunun."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 75d77c8..104a45c 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Дозволяє власникові прив’язуватися до інтерфейсу верхнього рівня служби VPN. Ніколи не застосовується для звичайних програм."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"прив’язати до фонового малюнка"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Дозволяє власнику прив’язуватися до інтерфейсу верхнього рівня фонового малюнка. Ніколи не застосовується для звичайних програм."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"прив’язуватися до віддаленого екрана"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Дозволяє власникові прив’язуватися до інтерфейсу верхнього рівня віддаленого екрана. Ніколи не застосовується для звичайних програм."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"прив\'язувати до служби віджетів"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Дозволяє програмі отримувати, перевіряти й очищати сповіщення, зокрема опубліковані іншими програмами."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"прив’язуватися до служби читання сповіщень"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Дозволяє власнику прив’язуватися до інтерфейсу верхнього рівня служби читання сповіщень. Ніколи не застосовується для звичайних програм."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"викликати надану оператором програму конфігурації"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Дозволяє власнику викликати надану оператором програму конфігурації. Ніколи не застосовується для звичайних програм."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"прослуховувати дані спостережень за станом мережі"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Фоновий мал."</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Змінити фоновий малюнок"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Служба читання сповіщень"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"Мережу VPN активовано"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Мережу VPN активовано програмою <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Торкніться, щоб керувати мережею."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index a1c03ad..24d468c 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -135,8 +135,7 @@
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Đồng bộ hóa"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Quá nhiều lần xóa <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Bộ nhớ máy tính bảng đã đầy. Hãy xóa một số tệp để tạo thêm dung lượng."</string>
- <!-- no translation found for low_memory (4415914910770005166) -->
- <skip />
+ <string name="low_memory" product="watch" msgid="4415914910770005166">"Bộ nhớ đồng hồ đã đầy. Hãy xóa một số tệp để giải phóng dung lượng."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Bộ nhớ điện thoại đã đầy. Hãy xóa một số tệp để tạo thêm dung lượng."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Mạng có thể được giám sát"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Bởi một bên thứ ba không xác định"</string>
@@ -154,8 +153,7 @@
<string name="silent_mode_ring" msgid="8592241816194074353">"Bật chuông"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Đang tắt…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Máy tính bảng của bạn sẽ tắt."</string>
- <!-- no translation found for shutdown_confirm (3490275567476369184) -->
- <skip />
+ <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Đồng hồ của bạn sẽ tắt."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Điện thoại của bạn sẽ tắt."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Bạn có muốn tắt không?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Khởi động lại ở chế độ an toàn"</string>
@@ -175,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Chế độ trên máy bay"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Chế độ trên máy bay BẬT"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Chế độ trên máy bay TẮT"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"Cài đặt"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Chế độ an toàn"</string>
<string name="android_system_label" msgid="6577375335728551336">"Hệ thống Android"</string>
@@ -390,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của dịch vụ Vpn. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"liên kết với hình nền"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của hình nền. Không cần thiết cho các ứng dụng thông thường."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"liên kết với màn hình từ xa"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của màn hình từ xa. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"liên kết với dịch vụ tiện ích con"</string>
@@ -702,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Cho phép ứng dụng truy xuất, kiểm tra và xóa thông báo, bao gồm những thông báo được đăng bởi các ứng dụng khác."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"liên kết với dịch vụ trình xử lý thông báo"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của dịch vụ trình xử lý thông báo. Không cần thiết cho các ứng dụng thông thường."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"gọi ra ứng dụng cấu hình do nhà cung cấp dịch vụ cung cấp"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Cho phép chủ sở hữu gọi ra ứng dụng cấu hình do nhà cung cấp dịch vụ cung cấp. Không cần thiết cho các ứng dụng thông thường."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"quan sát các điều kiện mạng"</string>
@@ -1372,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Hình nền"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Thay đổi hình nền"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Trình xử lý thông báo"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"Đã kích hoạt VPN"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN được <xliff:g id="APP">%s</xliff:g> kích hoạt"</string>
<string name="vpn_text" msgid="3011306607126450322">"Chạm để quản lý mạng."</string>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 4c4d0ce..1dd4608 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -27,4 +27,7 @@
<item>settings</item>
</string-array>
-</resources>
\ No newline at end of file
+ <!-- Base "touch slop" value used by ViewConfiguration as a
+ movement threshold where scrolling should begin. -->
+ <dimen name="config_viewConfigurationTouchSlop">4dp</dimen>
+</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 134cf9a..95ec384 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -77,14 +77,14 @@
<string name="serviceNotProvisioned" msgid="8614830180508686666">"未提供服务。"</string>
<string name="CLIRPermanent" msgid="3377371145926835671">"您无法更改来电显示设置。"</string>
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"网络可用情况发生变化"</string>
- <string name="RestrictedOnData" msgid="8653794784690065540">"数据服务已禁用。"</string>
- <string name="RestrictedOnEmergency" msgid="6581163779072833665">"紧急服务已禁用。"</string>
- <string name="RestrictedOnNormal" msgid="4953867011389750673">"已禁用语音服务。"</string>
- <string name="RestrictedOnAllVoice" msgid="3396963652108151260">"已停用所有语音服务。"</string>
- <string name="RestrictedOnSms" msgid="8314352327461638897">"已禁用短信服务。"</string>
- <string name="RestrictedOnVoiceData" msgid="996636487106171320">"已停用语音/数据服务。"</string>
- <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"已禁用语音/短信服务。"</string>
- <string name="RestrictedOnAll" msgid="5643028264466092821">"已停用所有语音/数据/短信服务。"</string>
+ <string name="RestrictedOnData" msgid="8653794784690065540">"数据网络服务已停用。"</string>
+ <string name="RestrictedOnEmergency" msgid="6581163779072833665">"紧急服务已停用。"</string>
+ <string name="RestrictedOnNormal" msgid="4953867011389750673">"语音服务已停用。"</string>
+ <string name="RestrictedOnAllVoice" msgid="3396963652108151260">"所有语音服务都已停用。"</string>
+ <string name="RestrictedOnSms" msgid="8314352327461638897">"短信服务已停用。"</string>
+ <string name="RestrictedOnVoiceData" msgid="996636487106171320">"语音/数据服务已停用。"</string>
+ <string name="RestrictedOnVoiceSms" msgid="1888588152792023873">"语音/短信服务已停用。"</string>
+ <string name="RestrictedOnAll" msgid="5643028264466092821">"所有语音/数据/短信服务都已停用。"</string>
<string name="serviceClassVoice" msgid="1258393812335258019">"语音"</string>
<string name="serviceClassData" msgid="872456782077937893">"数据"</string>
<string name="serviceClassFAX" msgid="5566624998840486475">"传真"</string>
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"飞行模式"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"已开启飞行模式"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"未开启飞行模式"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"设置"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 系统"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"允许用户绑定到 VPN 服务的顶级接口。普通应用绝不需要此权限。"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"绑定到壁纸"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"允许用户绑定到壁纸的顶级接口。普通应用绝不需要此权限。"</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"绑定至远程显示屏"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"允许应用绑定至远程显示屏的顶级接口。普通应用绝不需要此权限。"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"绑定到小部件服务"</string>
@@ -610,8 +613,8 @@
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"允许该应用获取手机已知的帐户列表,其中可能包括由已安装的应用创建的所有帐户。"</string>
<string name="permlab_authenticateAccounts" msgid="5265908481172736933">"创建帐户并设置密码"</string>
<string name="permdesc_authenticateAccounts" msgid="5472124296908977260">"允许应用使用 AccountManager 的帐户身份验证程序功能,包括创建帐户以及获取和设置其密码。"</string>
- <string name="permlab_manageAccounts" msgid="4983126304757177305">"添加或删除帐户"</string>
- <string name="permdesc_manageAccounts" msgid="8698295625488292506">"允许应用执行添加帐户、删除帐户、删除帐户密码等操作。"</string>
+ <string name="permlab_manageAccounts" msgid="4983126304757177305">"添加或移除帐户"</string>
+ <string name="permdesc_manageAccounts" msgid="8698295625488292506">"允许应用执行添加帐户、移除帐户、删除帐户密码等操作。"</string>
<string name="permlab_useCredentials" msgid="235481396163877642">"使用设备上的帐户"</string>
<string name="permdesc_useCredentials" msgid="7984227147403346422">"允许应用请求身份验证令牌。"</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"查看网络连接"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"允许该应用检索、检查并清除通知,包括其他应用发布的通知。"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"绑定到通知侦听器服务"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"允许应用绑定到通知侦听器服务的顶级接口(普通应用绝不需要此权限)。"</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"调用运营商提供的配置应用"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"允许应用调用运营商提供的配置应用。普通应用绝不需要此权限。"</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"监听网络状况的观测信息"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"壁纸"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"更改壁纸"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"通知侦听器"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN 已激活"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"“<xliff:g id="APP">%s</xliff:g>”已激活 VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"触摸可管理网络。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 2d3c418..d78f298 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"允許應用程式繫結至 VPN 服務的頂層介面 (不建議一般應用程式使用)。"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"繫結至桌布"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"允許應用程式繫結至桌布的頂層介面 (不建議一般應用程式使用)。"</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"繫結至遠端螢幕"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"允許應用程式繫結至遠端屏螢的頂層介面 (不建議一般應用程式使用)。"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"繫結至小工具服務"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"允許應用程式擷取、檢查及清除通知 (包括由其他應用程式發佈的通知)。"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"繫結至通知接聽器服務"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"允許應用程式繫結至通知接聽器服務的頂層介面 (不建議一般應用程式使用)。"</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"調用流動網絡供應商提供的設定應用程式"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"允許應用程式調用流動網絡供應商提供的設定應用程式 (不建議一般應用程式使用)。"</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"監聽對網絡狀況的觀察"</string>
@@ -981,7 +989,7 @@
<string name="permlab_setAlarm" msgid="1379294556362091814">"設定鬧鐘"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"允許應用程式在安裝的鬧鐘應用程式中設定鬧鐘,某些鬧鐘應用程式可能沒有這項功能。"</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"新增留言"</string>
- <string name="permdesc_addVoicemail" msgid="6604508651428252437">"允許應用程式將訊息加到您的留言信箱收件匣。"</string>
+ <string name="permdesc_addVoicemail" msgid="6604508651428252437">"允許應用程式將訊息加到您的留言信箱收件箱。"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"修改瀏覽器地理資訊的權限"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允許應用程式修改瀏覽器的地理資訊權限。惡意應用程式可能會藉此允許將您的位置資訊任意傳送給某些網站。"</string>
<string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"驗證套件"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"桌布"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"變更桌布"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"通知接聽器"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN 已啟用。"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> 已啟用 VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"輕觸即可管理網絡。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index e309acc..38a487b 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -173,8 +173,7 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"飛航模式"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"飛航模式為 [開啟]"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"飛航模式為 [關閉]"</string>
- <!-- no translation found for global_action_settings (1756531602592545966) -->
- <skip />
+ <string name="global_action_settings" msgid="1756531602592545966">"設定"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"超過 999"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 系統"</string>
@@ -388,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"允許應用程式聯繫至 VPN 服務的頂層介面 (一般應用程式不需使用)。"</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"連結至桌布"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"允許應用程式繫結至桌布的頂層介面 (一般應用程式不需使用)。"</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"繫結至遠端螢幕"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"允許應用程式繫結至遠端螢幕的頂層介面 (一般應用程式不需使用)。"</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"繫結至小工具服務"</string>
@@ -700,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"允許應用程式擷取、檢查及清除通知 (包括由其他應用程式發佈的通知)。"</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"繫結至通知接聽器服務"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"允許應用程式繫結至通知接聽器服務的頂層介面 (一般應用程式不需使用)。"</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"叫用行動通訊業者提供的設定應用程式"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"允許應用程式叫用行動通訊業者提供的設定應用程式 (一般應用程式並不需要)。"</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"監聽網路狀況觀察資訊"</string>
@@ -1370,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"桌布"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"變更桌布"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"通知接聽器"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"VPN 已啟用"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> 已啟用 VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"輕觸即可管理網路。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 4e36a1b..ede3d3c 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -387,6 +387,10 @@
<string name="permdesc_bindVpnService" msgid="2067845564581693905">"Ivumela umnini ukuthi abophele kwissekelo esingaphezulu sesevisi ye-Vpm. Ayidingakeli izinhlelo zokusebenza ezejwayelekile."</string>
<string name="permlab_bindWallpaper" msgid="8716400279937856462">"hlanganisa kwiphephadonga"</string>
<string name="permdesc_bindWallpaper" msgid="7108428692595491668">"Ivumela umbambi ukuhlanganisa uxhumano nomsebenzisi kwezinga eliphezulu lwephephadonga. Akusoze kwadingeka kwezinhlelo zokusebenza ezivamile."</string>
+ <!-- no translation found for permlab_bindVoiceInteraction (5334852580713715068) -->
+ <skip />
+ <!-- no translation found for permdesc_bindVoiceInteraction (2345721766501778101) -->
+ <skip />
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"bophezela kusibonisi sesilawuli kude"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Ivumela umbambi ukuhlanganisa isixhumi esibonakalayo esisezingeni eliphezulu sesibonisi sesilawuli kude. Akumele idingelwe izinhlelo zokusebenza ezijwayelekile."</string>
<string name="permlab_bindRemoteViews" msgid="5697987759897367099">"bophezela kube isevisi yesinqunjana"</string>
@@ -699,6 +703,10 @@
<string name="permdesc_accessNotifications" msgid="458457742683431387">"Ivumela uhlelo lokusebenza ukuthi lithole, lihlole, liphinde lisuse izaziso, ezifaka lezo ezithunyelwe ezinye izinhlelo zokusebenza."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bophezela kwisevisi yomlaleli wesaziso"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ivumela umbambi ukubophezela kwisixhumi esibonakalayo sezinga eliphezulu lesevisi yomlaleli wesaziso. Akusoze kwadingeka kwizinhlelo zokusebenza ezivamile."</string>
+ <!-- no translation found for permlab_bindConditionProviderService (1180107672332704641) -->
+ <skip />
+ <!-- no translation found for permdesc_bindConditionProviderService (1680513931165058425) -->
+ <skip />
<string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"buyisela uhlelo lokusebenza lokulungiselelwa okunikezwe yinkampani yenethiwekhi"</string>
<string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Ivumela umnikazi ukuthi abuyisele uhlelo lokusebenza lokulungiselelwa. Akumele idingelwe izinhlelo zokusebenza ezijwayelekile."</string>
<string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"Lalela okubonwayo kuzimo zenethiwekhi"</string>
@@ -1369,6 +1377,8 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Iphephadonga"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Shintsha iphephadonga"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Umlaleli wesaziso"</string>
+ <!-- no translation found for condition_provider_service_binding_label (1321343352906524564) -->
+ <skip />
<string name="vpn_title" msgid="19615213552042827">"I-VPN isiyasebenza"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"i-VPN ivuswe ngu <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Thinta ukuze wengamele inethiwekhi."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 508a557..efc1b55 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -462,24 +462,24 @@
transitions between different window content. -->
<attr name="windowContentTransitionManager" format="reference" />
- <!-- Reference to a TransitionManager XML resource defining the desired Transition
+ <!-- Reference to a Transition XML resource defining the desired Transition
used to move Views into the initial Window's content Scene. Corresponds to
{@link android.view.Window#setEnterTransition(android.transition.Transition)}. -->
<attr name="windowEnterTransition" format="reference"/>
- <!-- Reference to a TransitionManager XML resource defining the desired Transition
+ <!-- Reference to a Transition XML resource defining the desired Transition
used to move Views out of the Window's content Scene when launching a new Activity.
Corresponds to
{@link android.view.Window#setExitTransition(android.transition.Transition)}. -->
<attr name="windowExitTransition" format="reference"/>
- <!-- Reference to a TransitionManager XML resource defining the desired Transition
+ <!-- Reference to a Transition XML resource defining the desired Transition
used to move shared elements transferred into the Window's initial content Scene.
Corresponds to {@link android.view.Window#setSharedElementEnterTransition(
android.transition.Transition)}. -->
<attr name="windowSharedElementEnterTransition" format="reference"/>
- <!-- Reference to a TransitionManager XML resource defining the desired Transition
+ <!-- Reference to a Transition XML resource defining the desired Transition
used when starting a new Activity to move shared elements prior to transferring
to the called Activity.
Corresponds to {@link android.view.Window#setSharedElementExitTransition(
@@ -2191,6 +2191,9 @@
(completely opaque). -->
<attr name="alpha" format="float" />
+ <!-- base z depth of the view -->
+ <attr name="elevation" format="dimension" />
+
<!-- translation in x of the view. This value is added post-layout to the left
property of the view, which is set by its layout. -->
<attr name="translationX" format="dimension" />
@@ -2199,7 +2202,7 @@
property of the view, which is set by its layout. -->
<attr name="translationY" format="dimension" />
- <!-- translation in z of the view. This value is added post-layout to its position. -->
+ <!-- translation in z of the view. This value is added to its elevation. -->
<attr name="translationZ" format="dimension" />
<!-- x location of the pivot point around which the view will rotate and scale.
@@ -5022,6 +5025,12 @@
<declare-styleable name="TransitionTarget">
<!-- The id of a target on which this transition will animate changes. -->
<attr name="targetId" format="reference" />
+ <!-- The id of a target to exclude from this transition. -->
+ <attr name="excludeId" format="reference" />
+ <!-- The fully-qualified name of the Class to exclude from this transition. -->
+ <attr name="excludeClass" format="string" />
+ <!-- The fully-qualified name of the Class to include in this transition. -->
+ <attr name="targetClass" />
</declare-styleable>
<!-- Use <code>set</code> as the root tag of the XML resource that
@@ -6267,13 +6276,22 @@
</declare-styleable>
<!-- Use <code>recognition-service</code> as the root tag of the XML resource that
- describes a {@link android.speech.RecognitionService}, which is reference from
+ describes a {@link android.speech.RecognitionService}, which is referenced from
its {@link android.speech.RecognitionService#SERVICE_META_DATA} meta-data entry.
Described here are the attributes that can be included in that tag. -->
<declare-styleable name="RecognitionService">
<attr name="settingsActivity" />
</declare-styleable>
+ <!-- Use <code>voice-interaction-service</code> as the root tag of the XML resource that
+ describes a {@link android.service.voice.VoiceInteractionService}, which is referenced from
+ its {@link android.service.voice.VoiceInteractionService#SERVICE_META_DATA} meta-data entry.
+ Described here are the attributes that can be included in that tag. -->
+ <declare-styleable name="VoiceInteractionService">
+ <attr name="sessionService" format="string" />
+ <attr name="settingsActivity" />
+ </declare-styleable>
+
<!-- Attributes used to style the Action Bar. -->
<declare-styleable name="ActionBar">
<!-- The type of navigation to use. -->
diff --git a/core/res/res/values/dimens_quantum.xml b/core/res/res/values/dimens_quantum.xml
index 02e61e2..cebee12 100644
--- a/core/res/res/values/dimens_quantum.xml
+++ b/core/res/res/values/dimens_quantum.xml
@@ -47,4 +47,6 @@
<dimen name="text_size_menu_quantum">14sp</dimen>
<dimen name="text_size_button_quantum">14sp</dimen>
+ <dimen name="floating_window_z">16dp</dimen>
+ <dimen name="floating_window_margin">32dp</dimen>
</resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index a79e1fe..889c368 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -84,4 +84,5 @@
<item type="id" name="scene_layoutid_cache" />
<item type="id" name="shared_element_name" />
<item type="id" name="mask" />
+ <item type="id" name="shared_element" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6491b33..5ccb05b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2155,6 +2155,17 @@
<public type="attr" name="colorPrimaryDark" />
<public type="attr" name="colorAccent" />
<public type="attr" name="nestedScrollingEnabled" />
+ <public type="attr" name="windowEnterTransition" />
+ <public type="attr" name="windowExitTransition" />
+ <public type="attr" name="windowSharedElementEnterTransition" />
+ <public type="attr" name="windowSharedElementExitTransition" />
+ <public type="attr" name="windowAllowExitTransitionOverlap" />
+ <public type="attr" name="windowAllowEnterTransitionOverlap" />
+ <public type="attr" name="sessionService" />
+ <public type="attr" name="switchStyle" />
+ <public type="attr" name="elevation" />
+ <public type="attr" name="excludeId" />
+ <public type="attr" name="excludeClass" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
@@ -2165,6 +2176,7 @@
<public type="id" name="shared_element_name" />
<public type="id" name="mask" />
+ <public type="id" name="shared_element" />
<public-padding type="style" name="l_resource_pad" end="0x01030200" />
@@ -2385,17 +2397,14 @@
<public type="style" name="Widget.Holo.Light.Button.Borderless" />
+ <public-padding type="interpolator" name="l_resource_pad" end="0x010c0010" />
+
<!-- An interpolator which accelerates fast but decelerates slowly. -->
<public type="interpolator" name="fast_out_slow_in" />
<!-- An interpolator which starts with a peak non-zero velocity and decelerates slowly. -->
<public type="interpolator" name="linear_out_slow_in" />
<!-- An interpolator which accelerates fast and keeps accelerating until the end. -->
<public type="interpolator" name="fast_out_linear_in" />
- <public type="attr" name="windowEnterTransition" />
- <public type="attr" name="windowExitTransition" />
- <public type="attr" name="windowSharedElementEnterTransition" />
- <public type="attr" name="windowSharedElementExitTransition" />
- <public type="attr" name="windowAllowExitTransitionOverlap" />
- <public type="attr" name="windowAllowEnterTransitionOverlap" />
+
<public type="transition" name="no_transition" id="0x010f0000"/>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6d4ceef..97400b2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -36,6 +36,45 @@
the placeholders. -->
<string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g><xliff:g id="unit" example="KB">%2$s</xliff:g></string>
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration in days -->
+ <string name="durationDays"><xliff:g id="days">%1$d</xliff:g> days</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one day with hours -->
+ <string name="durationDayHours"><xliff:g id="days">%1$d</xliff:g> day
+ <xliff:g id="hours">%2$d</xliff:g> hrs</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one day with one hours -->
+ <string name="durationDayHour"><xliff:g id="days">%1$d</xliff:g> day
+ <xliff:g id="hours">%2$d</xliff:g> hr</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration in hours -->
+ <string name="durationHours"><xliff:g id="hours">%1$d</xliff:g> hrs</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one hour with minutes -->
+ <string name="durationHourMinutes"><xliff:g id="hours">%1$d</xliff:g> hr
+ <xliff:g id="minutes">%2$d</xliff:g> mins</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one hour with one minute -->
+ <string name="durationHourMinute"><xliff:g id="hours">%1$d</xliff:g> hr
+ <xliff:g id="minutes">%2$d</xliff:g> min</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration in minutes -->
+ <string name="durationMinutes"><xliff:g id="minutes">%1$d</xliff:g> mins</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one minute with seconds -->
+ <string name="durationMinuteSeconds"><xliff:g id="minutes">%1$d</xliff:g> min
+ <xliff:g id="seconds">%2$d</xliff:g> secs</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one minute with one second -->
+ <string name="durationMinuteSecond"><xliff:g id="minutes">%1$d</xliff:g> min
+ <xliff:g id="seconds">%2$d</xliff:g> sec</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration in seconds -->
+ <string name="durationSeconds"><xliff:g id="seconds">%1$d</xliff:g> secs</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one second -->
+ <string name="durationSecond"><xliff:g id="seconds">%1$d</xliff:g> sec</string>
+
<!-- Used in Contacts for a field that has no label and in Note Pad
for a note with no name. -->
<string name="untitled"><Untitled></string>
@@ -415,6 +454,12 @@
<!-- Label for the Android system components when they are shown to the user. -->
<string name="android_system_label">Android System</string>
+ <!-- Label for the user owner in the intent forwarding app. -->
+ <string name="user_owner_label">Personal</string>
+
+ <!-- Label for a corporate profile in the intent forwarding app. -->
+ <string name="managed_profile_label">Work</string>
+
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_costMoney">Services that cost you money</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1068,6 +1113,12 @@
interface of a wallpaper. Should never be needed for normal apps.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_bindVoiceInteraction">bind to a voice interactor</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_bindVoiceInteraction">Allows the holder to bind to the top-level
+ interface of a voice interaction service. Should never be needed for normal apps.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_bindRemoteDisplay">bind to a remote display</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_bindRemoteDisplay">Allows the holder to bind to the top-level
@@ -2048,6 +2099,11 @@
<string name="permdesc_bindNotificationListenerService">Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_bindConditionProviderService">bind to a condition provider service</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_bindConditionProviderService">Allows the holder to bind to the top-level interface of a condition provider service. Should never be needed for normal apps.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_invokeCarrierSetup">invoke the carrier-provided configuration app</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_invokeCarrierSetup">Allows the holder to invoke the carrier-provided configuration app. Should never be needed for normal apps.</string>
@@ -3797,6 +3853,8 @@
<!-- Label to show for a service that is running because it is observing
the user's notifications. -->
<string name="notification_listener_binding_label">Notification listener</string>
+ <!-- Label to show for a service that is running because it is providing conditions. -->
+ <string name="condition_provider_service_binding_label">Condition provider</string>
<!-- Do Not Translate: Alternate eri.xml -->
<string name="alternate_eri_file">/data/eri.xml</string>
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index 0cbb655..57f2443 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -480,7 +480,7 @@
<style name="Widget.Quantum.AbsListView" parent="Widget.AbsListView"/>
<style name="Widget.Quantum.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
- <item name="dropDownSelector">@drawable/list_selector_holo_dark</item>
+ <item name="dropDownSelector">@drawable/list_selector_quantum</item>
<item name="popupBackground">?attr/colorBackground</item>
</style>
@@ -654,7 +654,7 @@
<style name="Widget.Quantum.Spinner" parent="Widget.Spinner.DropDown">
<item name="background">@drawable/spinner_background_quantum</item>
- <item name="dropDownSelector">@drawable/list_selector_holo_dark</item>
+ <item name="dropDownSelector">@drawable/list_selector_quantum</item>
<item name="popupBackground">?attr/colorBackground</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
@@ -713,7 +713,7 @@
<style name="Widget.Quantum.QuickContactBadgeSmall.WindowLarge" parent="Widget.QuickContactBadgeSmall.WindowLarge"/>
<style name="Widget.Quantum.ListPopupWindow" parent="Widget.ListPopupWindow">
- <item name="dropDownSelector">@drawable/list_selector_holo_dark</item>
+ <item name="dropDownSelector">@drawable/list_selector_quantum</item>
<item name="popupBackground">?attr/colorBackground</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
@@ -851,7 +851,7 @@
<style name="Widget.Quantum.Light.AbsListView" parent="Widget.Quantum.AbsListView"/>
<style name="Widget.Quantum.Light.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
- <item name="dropDownSelector">@drawable/list_selector_holo_light</item>
+ <item name="dropDownSelector">@drawable/list_selector_quantum</item>
<item name="popupBackground">?attr/colorBackground</item>
</style>
@@ -938,7 +938,7 @@
<style name="Widget.Quantum.Light.Spinner" parent="Widget.Quantum.Spinner">
<item name="background">@drawable/spinner_background_quantum</item>
- <item name="dropDownSelector">@drawable/list_selector_holo_light</item>
+ <item name="dropDownSelector">@drawable/list_selector_quantum</item>
<item name="popupBackground">?attr/colorBackground</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
@@ -962,7 +962,7 @@
<style name="Widget.Quantum.Light.QuickContactBadgeSmall.WindowLarge" parent="Widget.Quantum.QuickContactBadgeSmall.WindowLarge"/>
<style name="Widget.Quantum.Light.ListPopupWindow" parent="Widget.ListPopupWindow">
- <item name="dropDownSelector">@drawable/list_selector_holo_light</item>
+ <item name="dropDownSelector">@drawable/list_selector_quantum</item>
<item name="popupBackground">?attr/colorBackground</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
@@ -1019,16 +1019,16 @@
<!-- Dialog styles -->
<style name="AlertDialog.Quantum" parent="AlertDialog">
- <item name="fullDark">?attr/colorBackground</item>
- <item name="topDark">?attr/colorBackground</item>
- <item name="centerDark">?attr/colorBackground</item>
- <item name="bottomDark">?attr/colorBackground</item>
- <item name="fullBright">?attr/colorBackground</item>
- <item name="topBright">?attr/colorBackground</item>
- <item name="centerBright">?attr/colorBackground</item>
- <item name="bottomBright">?attr/colorBackground</item>
- <item name="bottomMedium">?attr/colorBackground</item>
- <item name="centerMedium">?attr/colorBackground</item>
+ <item name="fullDark">@color/transparent</item>
+ <item name="topDark">@color/transparent</item>
+ <item name="centerDark">@color/transparent</item>
+ <item name="bottomDark">@color/transparent</item>
+ <item name="fullBright">@color/transparent</item>
+ <item name="topBright">@color/transparent</item>
+ <item name="centerBright">@color/transparent</item>
+ <item name="bottomBright">@color/transparent</item>
+ <item name="bottomMedium">@color/transparent</item>
+ <item name="centerMedium">@color/transparent</item>
<item name="layout">@layout/alert_dialog_quantum</item>
<item name="listLayout">@layout/select_dialog_quantum</item>
<item name="progressLayout">@layout/progress_dialog_quantum</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bb0d184..d4ac74a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -233,7 +233,6 @@
<java-symbol type="attr" name="searchDialogTheme" />
<java-symbol type="attr" name="searchViewSearchIcon" />
<java-symbol type="attr" name="stackViewStyle" />
- <java-symbol type="attr" name="switchStyle" />
<java-symbol type="attr" name="textAppearanceAutoCorrectionSuggestion" />
<java-symbol type="attr" name="textAppearanceEasyCorrectSuggestion" />
<java-symbol type="attr" name="textAppearanceMisspelledSuggestion" />
@@ -506,6 +505,17 @@
<java-symbol type="string" name="display_manager_overlay_display_secure_suffix" />
<java-symbol type="string" name="display_manager_overlay_display_title" />
<java-symbol type="string" name="double_tap_toast" />
+ <java-symbol type="string" name="durationDays" />
+ <java-symbol type="string" name="durationDayHours" />
+ <java-symbol type="string" name="durationDayHour" />
+ <java-symbol type="string" name="durationHours" />
+ <java-symbol type="string" name="durationHourMinutes" />
+ <java-symbol type="string" name="durationHourMinute" />
+ <java-symbol type="string" name="durationMinutes" />
+ <java-symbol type="string" name="durationMinuteSeconds" />
+ <java-symbol type="string" name="durationMinuteSecond" />
+ <java-symbol type="string" name="durationSeconds" />
+ <java-symbol type="string" name="durationSecond" />
<java-symbol type="string" name="elapsed_time_short_format_h_mm_ss" />
<java-symbol type="string" name="elapsed_time_short_format_mm_ss" />
<java-symbol type="string" name="emailTypeCustom" />
@@ -1580,6 +1590,7 @@
<java-symbol type="string" name="low_internal_storage_view_text" />
<java-symbol type="string" name="low_internal_storage_view_title" />
<java-symbol type="string" name="notification_listener_binding_label" />
+ <java-symbol type="string" name="condition_provider_service_binding_label" />
<java-symbol type="string" name="report" />
<java-symbol type="string" name="select_input_method" />
<java-symbol type="string" name="select_keyboard_layout_notification_title" />
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index c2e31f4..9f76eae 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -125,7 +125,7 @@
<item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item>
<item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
- <item name="listChoiceBackgroundIndicator">@drawable/list_selector_holo_dark</item>
+ <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
<item name="activatedBackgroundIndicator">@drawable/activated_background_quantum</item>
@@ -468,7 +468,7 @@
<item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item>
<item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
- <item name="listChoiceBackgroundIndicator">@drawable/list_selector_holo_light</item>
+ <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
<item name="activatedBackgroundIndicator">@drawable/activated_background_quantum</item>
@@ -898,7 +898,7 @@
<style name="Theme.Quantum.Dialog">
<item name="windowFrame">@null</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Quantum</item>
- <item name="windowBackground">?attr/colorBackground</item>
+ <item name="windowBackground">@drawable/dialog_background_quantum</item>
<item name="windowIsFloating">true</item>
<item name="windowContentOverlay">@null</item>
<item name="windowAnimationStyle">@style/Animation.Quantum.Dialog</item>
@@ -964,8 +964,6 @@
its pixels. -->
<style name="Theme.Quantum.Dialog.NoFrame">
<item name="windowBackground">@color/transparent</item>
- <item name="windowFrame">@null</item>
- <item name="windowContentOverlay">@null</item>
<item name="windowAnimationStyle">@null</item>
<item name="backgroundDimEnabled">false</item>
<item name="windowIsTranslucent">true</item>
@@ -981,7 +979,6 @@
<style name="Theme.Quantum.Dialog.Alert">
<item name="windowBackground">@color/transparent</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Quantum</item>
- <item name="windowContentOverlay">@null</item>
<item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
<item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
@@ -1102,7 +1099,6 @@
<style name="Theme.Quantum.Light.Dialog.Alert">
<item name="windowBackground">@color/transparent</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Quantum.Light</item>
- <item name="windowContentOverlay">@null</item>
<item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
<item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>
@@ -1112,7 +1108,6 @@
<style name="Theme.Quantum.Light.Dialog.TimePicker">
<item name="windowBackground">@color/transparent</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Quantum.Light</item>
- <item name="windowContentOverlay">@null</item>
</style>
<!-- Theme for a presentation window on a secondary display. -->
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
index 34393f9..0cd19f2 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
@@ -25,6 +25,7 @@
private BluetoothTestUtils mUtils = null;
private BluetoothAdapter mAdapter = null;
private Bundle mArgs = null;
+ private Bundle mSuccessResult = null;
private BluetoothTestUtils getBluetoothTestUtils() {
if (mUtils == null) {
@@ -46,6 +47,9 @@
public void onCreate(Bundle arguments) {
super.onCreate(arguments);
mArgs = arguments;
+ // create the default result response, but only use it in success code path
+ mSuccessResult = new Bundle();
+ mSuccessResult.putString("result", "SUCCESS");
start();
}
@@ -67,24 +71,23 @@
public void enable() {
getBluetoothTestUtils().enable(getBluetoothAdapter());
- finish(null);
+ finish(mSuccessResult);
}
public void disable() {
getBluetoothTestUtils().disable(getBluetoothAdapter());
- finish(null);
+ finish(mSuccessResult);
}
public void unpairAll() {
getBluetoothTestUtils().unpairAll(getBluetoothAdapter());
- finish(null);
+ finish(mSuccessResult);
}
public void getName() {
String name = getBluetoothAdapter().getName();
- Bundle bundle = new Bundle();
- bundle.putString("name", name);
- finish(bundle);
+ mSuccessResult.putString("name", name);
+ finish(mSuccessResult);
}
public void finish(Bundle result) {
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index e77564f..07a6a10 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -16,7 +16,7 @@
package android.content.pm;
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
import com.android.frameworks.coretests.R;
import com.android.internal.content.PackageHelper;
@@ -48,6 +48,9 @@
import android.os.storage.StorageResultCode;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
@@ -62,10 +65,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-import libcore.io.StructStat;
-
public class PackageManagerTests extends AndroidTestCase {
private static final boolean localLOGV = true;
@@ -503,7 +502,7 @@
final StructStat stat;
try {
- stat = Libcore.os.lstat(path);
+ stat = Os.lstat(path);
} catch (ErrnoException e) {
throw new AssertionError(reason + "\n" + "Got: " + path + " does not exist");
}
diff --git a/core/tests/coretests/src/android/net/LinkAddressTest.java b/core/tests/coretests/src/android/net/LinkAddressTest.java
index 17423be..bccf556 100644
--- a/core/tests/coretests/src/android/net/LinkAddressTest.java
+++ b/core/tests/coretests/src/android/net/LinkAddressTest.java
@@ -32,13 +32,13 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import static libcore.io.OsConstants.IFA_F_DEPRECATED;
-import static libcore.io.OsConstants.IFA_F_PERMANENT;
-import static libcore.io.OsConstants.IFA_F_TENTATIVE;
-import static libcore.io.OsConstants.RT_SCOPE_HOST;
-import static libcore.io.OsConstants.RT_SCOPE_LINK;
-import static libcore.io.OsConstants.RT_SCOPE_SITE;
-import static libcore.io.OsConstants.RT_SCOPE_UNIVERSE;
+import static android.system.OsConstants.IFA_F_DEPRECATED;
+import static android.system.OsConstants.IFA_F_PERMANENT;
+import static android.system.OsConstants.IFA_F_TENTATIVE;
+import static android.system.OsConstants.RT_SCOPE_HOST;
+import static android.system.OsConstants.RT_SCOPE_LINK;
+import static android.system.OsConstants.RT_SCOPE_SITE;
+import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
/**
* Tests for {@link LinkAddress}.
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index a602e07..553afe0 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -18,14 +18,13 @@
import android.net.LinkProperties;
import android.net.RouteInfo;
+import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
import java.net.InetAddress;
import java.util.ArrayList;
-import libcore.io.OsConstants;
-
public class LinkPropertiesTest extends TestCase {
private static InetAddress ADDRV4 = NetworkUtils.numericToInetAddress("75.208.6.1");
private static InetAddress ADDRV6 = NetworkUtils.numericToInetAddress(
diff --git a/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java
index 0e3c13a0..3f9e62e 100644
--- a/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java
@@ -46,7 +46,6 @@
import junit.framework.Assert;
import libcore.io.IoUtils;
-import libcore.io.Libcore;
/**
* Tests for {@link FileRotator}.
diff --git a/core/tests/inputmethodtests/run_core_inputmethod_test.sh b/core/tests/inputmethodtests/run_core_inputmethod_test.sh
index b0b119b..9029ba5 100755
--- a/core/tests/inputmethodtests/run_core_inputmethod_test.sh
+++ b/core/tests/inputmethodtests/run_core_inputmethod_test.sh
@@ -21,4 +21,4 @@
$COMMAND
fi
-adb shell am instrument -w -e class android.os.InputMethodTest,android.os.InputMethodSubtypeArrayTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner
+adb shell am instrument -w -e class android.os.InputMethodTest,android.os.InputMethodSubtypeArrayTest,android.os.InputMethodSubtypeSwitchingControllerTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
new file mode 100644
index 0000000..6d33529
--- /dev/null
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+
+import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
+import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTestCase {
+ final private static String DUMMY_PACKAGE_NAME = "dymmy package name";
+ final private static String DUMMY_SETTING_ACTIVITY_NAME = "";
+ final private static boolean DUMMY_IS_AUX_IME = false;
+ final private static boolean DUMMY_FORCE_DEFAULT = false;
+ final private static int DUMMY_IS_DEFAULT_RES_ID = 0;
+ final private static String SYSTEM_LOCALE = "en_US";
+
+ private static InputMethodSubtype createDummySubtype(final String locale) {
+ final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
+ return builder.setSubtypeNameResId(0)
+ .setSubtypeIconResId(0)
+ .setSubtypeLocale(locale)
+ .setIsAsciiCapable(true)
+ .build();
+ }
+
+ private static void addDummyImeSubtypeListItems(List<ImeSubtypeListItem> items,
+ String imeName, String imeLabel, List<String> subtypeLocales,
+ boolean supportsSwitchingToNextInputMethod) {
+ final ResolveInfo ri = new ResolveInfo();
+ final ServiceInfo si = new ServiceInfo();
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = DUMMY_PACKAGE_NAME;
+ ai.enabled = true;
+ si.applicationInfo = ai;
+ si.enabled = true;
+ si.packageName = DUMMY_PACKAGE_NAME;
+ si.name = imeName;
+ si.exported = true;
+ si.nonLocalizedLabel = imeLabel;
+ ri.serviceInfo = si;
+ final List<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ for (String subtypeLocale : subtypeLocales) {
+ subtypes.add(createDummySubtype(subtypeLocale));
+ }
+ final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
+ DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
+ DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
+ for (int i = 0; i < subtypes.size(); ++i) {
+ final String subtypeLocale = subtypeLocales.get(i);
+ final InputMethodSubtype subtype = subtypes.get(i);
+ items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
+ SYSTEM_LOCALE));
+ }
+ }
+
+ private static List<ImeSubtypeListItem> createTestData() {
+ final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
+ addDummyImeSubtypeListItems(items, "switchAwareLatinIme", "switchAwareLatinIme",
+ Arrays.asList("en_US", "es_US", "fr"),
+ true /* supportsSwitchingToNextInputMethod*/);
+ addDummyImeSubtypeListItems(items, "nonSwitchAwareLatinIme", "nonSwitchAwareLatinIme",
+ Arrays.asList("en_UK", "hi"),
+ false /* supportsSwitchingToNextInputMethod*/);
+ addDummyImeSubtypeListItems(items, "switchAwareJapaneseIme", "switchAwareJapaneseIme",
+ Arrays.asList("ja_JP"),
+ true /* supportsSwitchingToNextInputMethod*/);
+ addDummyImeSubtypeListItems(items, "nonSwitchAwareJapaneseIme", "nonSwitchAwareJapaneseIme",
+ Arrays.asList("ja_JP"),
+ false /* supportsSwitchingToNextInputMethod*/);
+ return items;
+ }
+
+ @SmallTest
+ public void testGetNextInputMethodImplWithNotOnlyCurrentIme() throws Exception {
+ final List<ImeSubtypeListItem> imList = createTestData();
+
+ final boolean ONLY_CURRENT_IME = false;
+ ImeSubtypeListItem currentIme;
+ ImeSubtypeListItem nextIme;
+
+ // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
+ currentIme = imList.get(0);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(1), nextIme);
+ // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
+ currentIme = imList.get(1);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(2), nextIme);
+ // "switchAwareLatinIme/fr" -> "nonSwitchAwareLatinIme/en_UK
+ currentIme = imList.get(2);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(3), nextIme);
+ // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
+ currentIme = imList.get(3);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(4), nextIme);
+ // "nonSwitchAwareLatinIme/hi" -> "switchAwareJapaneseIme/ja_JP"
+ currentIme = imList.get(4);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(5), nextIme);
+ // "switchAwareJapaneseIme/ja_JP" -> "nonSwitchAwareJapaneseIme/ja_JP"
+ currentIme = imList.get(5);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(6), nextIme);
+ // "nonSwitchAwareJapaneseIme/ja_JP" -> "switchAwareLatinIme/en_US"
+ currentIme = imList.get(6);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(0), nextIme);
+ }
+
+ @SmallTest
+ public void testGetNextInputMethodImplWithOnlyCurrentIme() throws Exception {
+ final List<ImeSubtypeListItem> imList = createTestData();
+
+ final boolean ONLY_CURRENT_IME = true;
+ ImeSubtypeListItem currentIme;
+ ImeSubtypeListItem nextIme;
+
+ // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
+ currentIme = imList.get(0);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(1), nextIme);
+ // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
+ currentIme = imList.get(1);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(2), nextIme);
+ // "switchAwareLatinIme/fr" -> "switchAwareLatinIme/en_US"
+ currentIme = imList.get(2);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(0), nextIme);
+
+ // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
+ currentIme = imList.get(3);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(4), nextIme);
+ // "nonSwitchAwareLatinIme/hi" -> "switchAwareLatinIme/en_UK"
+ currentIme = imList.get(4);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertEquals(imList.get(3), nextIme);
+
+ // "switchAwareJapaneseIme/ja_JP" -> null
+ currentIme = imList.get(5);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertNull(nextIme);
+
+ // "nonSwitchAwareJapaneseIme/ja_JP" -> null
+ currentIme = imList.get(6);
+ nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
+ imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
+ currentIme.mSubtypeName.toString()));
+ assertNull(nextIme);
+ }
+ }
diff --git a/data/keyboards/AVRCP.kl b/data/keyboards/AVRCP.kl
index 736b43c..ccd0209 100644
--- a/data/keyboards/AVRCP.kl
+++ b/data/keyboards/AVRCP.kl
@@ -14,10 +14,10 @@
# Key layout used for Bluetooth AVRCP support.
-key 200 MEDIA_PLAY WAKE
-key 201 MEDIA_PAUSE WAKE
-key 166 MEDIA_STOP WAKE
-key 163 MEDIA_NEXT WAKE
-key 165 MEDIA_PREVIOUS WAKE
-key 168 MEDIA_REWIND WAKE
-key 208 MEDIA_FAST_FORWARD WAKE
+key 200 MEDIA_PLAY
+key 201 MEDIA_PAUSE
+key 166 MEDIA_STOP
+key 163 MEDIA_NEXT
+key 165 MEDIA_PREVIOUS
+key 168 MEDIA_REWIND
+key 208 MEDIA_FAST_FORWARD
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 0cdcb1c..56423c9 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -135,7 +135,7 @@
key 113 VOLUME_MUTE
key 114 VOLUME_DOWN
key 115 VOLUME_UP
-key 116 POWER WAKE
+key 116 POWER
key 117 NUMPAD_EQUALS
# key 118 "KEY_KPPLUSMINUS"
key 119 BREAK
@@ -146,7 +146,7 @@
key 124 YEN
key 125 META_LEFT
key 126 META_RIGHT
-key 127 MENU WAKE_DROPPED
+key 127 MENU
key 128 MEDIA_STOP
# key 129 "KEY_AGAIN"
# key 130 "KEY_PROPS"
@@ -158,11 +158,11 @@
# key 136 "KEY_FIND"
# key 137 "KEY_CUT"
# key 138 "KEY_HELP"
-key 139 MENU WAKE_DROPPED
+key 139 MENU
key 140 CALCULATOR
# key 141 "KEY_SETUP"
-key 142 SLEEP WAKE
-key 143 WAKEUP WAKE
+key 142 SLEEP
+key 143 WAKEUP
# key 144 "KEY_FILE"
# key 145 "KEY_SENDFILE"
# key 146 "KEY_DELETEFILE"
@@ -171,13 +171,13 @@
# key 149 "KEY_PROG2"
key 150 EXPLORER
# key 151 "KEY_MSDOS"
-key 152 POWER WAKE
+key 152 POWER
# key 153 "KEY_DIRECTION"
# key 154 "KEY_CYCLEWINDOWS"
key 155 ENVELOPE
key 156 BOOKMARK
# key 157 "KEY_COMPUTER"
-key 158 BACK WAKE_DROPPED
+key 158 BACK
key 159 FORWARD
key 160 MEDIA_CLOSE
key 161 MEDIA_EJECT
diff --git a/data/keyboards/Vendor_0a5c_Product_8502.kl b/data/keyboards/Vendor_0a5c_Product_8502.kl
index f6dbfd4..2f07328 100644
--- a/data/keyboards/Vendor_0a5c_Product_8502.kl
+++ b/data/keyboards/Vendor_0a5c_Product_8502.kl
@@ -24,7 +24,7 @@
key 317 BUTTON_THUMBL
key 318 BUTTON_THUMBR
-key 158 BACK WAKE_DROPPED
+key 158 BACK
key 172 HOME
axis 0x00 X
diff --git a/data/keyboards/Vendor_18d1_Product_2c40.kl b/data/keyboards/Vendor_18d1_Product_2c40.kl
index 903f13b6..6efde4f 100644
--- a/data/keyboards/Vendor_18d1_Product_2c40.kl
+++ b/data/keyboards/Vendor_18d1_Product_2c40.kl
@@ -24,7 +24,7 @@
key 317 BUTTON_THUMBL
key 318 BUTTON_THUMBR
-key 158 BACK WAKE_DROPPED
+key 158 BACK
key 172 HOME
axis 0x00 X
diff --git a/data/keyboards/qwerty.kl b/data/keyboards/qwerty.kl
index f1caacd..58bf654 100644
--- a/data/keyboards/qwerty.kl
+++ b/data/keyboards/qwerty.kl
@@ -30,29 +30,29 @@
key 9 8
key 10 9
key 11 0
-key 158 BACK WAKE_DROPPED
-key 230 SOFT_RIGHT WAKE
-key 60 SOFT_RIGHT WAKE
-key 107 ENDCALL WAKE_DROPPED
-key 62 ENDCALL WAKE_DROPPED
-key 229 MENU WAKE_DROPPED
-key 139 MENU WAKE_DROPPED
-key 59 MENU WAKE_DROPPED
-key 127 SEARCH WAKE_DROPPED
-key 217 SEARCH WAKE_DROPPED
+key 158 BACK
+key 230 SOFT_RIGHT
+key 60 SOFT_LEFT
+key 107 ENDCALL
+key 62 ENDCALL
+key 229 MENU
+key 139 MENU
+key 59 MENU
+key 127 SEARCH
+key 217 SEARCH
key 228 POUND
key 227 STAR
-key 231 CALL WAKE_DROPPED
-key 61 CALL WAKE_DROPPED
-key 232 DPAD_CENTER WAKE_DROPPED
-key 108 DPAD_DOWN WAKE_DROPPED
-key 103 DPAD_UP WAKE_DROPPED
-key 102 HOME WAKE
-key 105 DPAD_LEFT WAKE_DROPPED
-key 106 DPAD_RIGHT WAKE_DROPPED
-key 115 VOLUME_UP WAKE
-key 114 VOLUME_DOWN WAKE
-key 116 POWER WAKE
+key 231 CALL
+key 61 CALL
+key 232 DPAD_CENTER
+key 108 DPAD_DOWN
+key 103 DPAD_UP
+key 102 HOME
+key 105 DPAD_LEFT
+key 106 DPAD_RIGHT
+key 115 VOLUME_UP
+key 114 VOLUME_DOWN
+key 116 POWER
key 212 CAMERA
key 16 Q
@@ -108,5 +108,5 @@
key 215 AT
# On an AT keyboard: ESC, F10
-key 1 BACK WAKE_DROPPED
-key 68 MENU WAKE_DROPPED
+key 1 BACK
+key 68 MENU
diff --git a/docs/html/guide/topics/manifest/manifest-element.jd b/docs/html/guide/topics/manifest/manifest-element.jd
index 20dc4ea..7717696 100644
--- a/docs/html/guide/topics/manifest/manifest-element.jd
+++ b/docs/html/guide/topics/manifest/manifest-element.jd
@@ -30,7 +30,7 @@
<br/><code><a href="{@docRoot}guide/topics/manifest/permission-element.html"><permission></a></code>
<br/><code><a href="{@docRoot}guide/topics/manifest/permission-group-element.html"><permission-group></a></code>
<br/><code><a href="{@docRoot}guide/topics/manifest/permission-tree-element.html"><permission-tree></a></code>
-<br/><code><a href="{@docRoot}guide/topics/manifest/supports-gl-texture.html"><supports-gl-texture></a></code
+<br/><code><a href="{@docRoot}guide/topics/manifest/supports-gl-texture-element.html"><supports-gl-texture></a></code>
<br/><code><a href="{@docRoot}guide/topics/manifest/supports-screens-element.html"><supports-screens></a></code>
<br/><code><a href="{@docRoot}guide/topics/manifest/uses-configuration-element.html"><uses-configuration></a></code> <!-- ##api level 3## -->
<br/><code><a href="{@docRoot}guide/topics/manifest/uses-feature-element.html"><uses-feature></a></code>
@@ -193,4 +193,4 @@
<dd>
<code><a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code></dd>
-</dl>
\ No newline at end of file
+</dl>
diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd
index e4cf1bc..e12dfd8 100644
--- a/docs/html/sdk/installing/installing-adt.jd
+++ b/docs/html/sdk/installing/installing-adt.jd
@@ -1,8 +1,8 @@
page.title=Installing the Eclipse Plugin
-adt.zip.version=22.6.2
-adt.zip.download=ADT-22.6.2.zip
-adt.zip.bytes=14586842
-adt.zip.checksum=f660959fa71262b4285bcb64be284bf5
+adt.zip.version=22.6.3
+adt.zip.download=ADT-22.6.3.zip
+adt.zip.bytes=14590813
+adt.zip.checksum=3982259fd2cc81e53bbbe05dcd6529a7
@jd:body
diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd
index 071492d..8ea5e7e 100644
--- a/docs/html/sdk/installing/studio.jd
+++ b/docs/html/sdk/installing/studio.jd
@@ -253,36 +253,36 @@
<td>Windows</td>
<td>
<a onclick="return onDownload(this)" id="win-studio"
- href="http://dl.google.com/android/studio/install/0.4.6/android-studio-bundle-133.1028713-windows.exe">
- android-studio-bundle-133.1028713-windows.exe
+ href="http://dl.google.com/android/studio/install/0.5.2/android-studio-bundle-135.1078000-windows.exe">
+ android-studio-bundle-135.1078000-windows.exe
</a>
</td>
- <td>519592042 bytes</td>
- <td>9029c18738a75830786326d62c96d557</td>
+ <td>519082997 bytes</td>
+ <td>ac69889210c4d02ee3ccc1c0f3c5cf3c</td>
</tr>
<tr>
<td><nobr>Mac OS X</nobr></td>
<td>
<a onclick="return onDownload(this)" id="mac-studio"
- href="http://dl.google.com/android/studio/install/0.4.6/android-studio-bundle-133.1028713-mac.dmg">
- android-studio-bundle-133.1028713-mac.dmg
+ href="http://dl.google.com/android/studio/install/0.5.2/android-studio-bundle-135.1078000-mac.dmg">
+ android-studio-bundle-135.1078000-mac.dmg
</a>
</td>
- <td>497595811 bytes</td>
- <td>eb2474e6d17537ddfa535e6fe8adcf0d</td>
+ <td>495989974 bytes</td>
+ <td>8c7b1ef376b8ca206c99823d9e8fd54d</td>
</tr>
<tr>
<td>Linux</td>
<td>
<a onclick="return onDownload(this)" id="linux-studio"
- href="http://dl.google.com/android/studio/install/0.4.6/android-studio-bundle-133.1028713-linux.tgz">
- android-studio-bundle-133.1028713-linux.tgz
+ href="http://dl.google.com/android/studio/install/0.5.2/android-studio-bundle-135.1078000-linux.tgz">
+ android-studio-bundle-135.1078000-linux.tgz
</a>
</td>
- <td>522177460 bytes</td>
- <td>cc847dd6249b3033737dabe0377c8c66</td>
+ <td>520523870 bytes</td>
+ <td>689238d5e632fd236b13f9c6d49f0cb4</td>
</tr>
</table>
@@ -430,6 +430,19 @@
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+ alt=""/>Android Studio v0.5.2</a> <em>(May 2014)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+ <ul>
+ <li>See <a href="http://tools.android.com/recent">tools.android.com</a> for a full list of changes.</li>
+ </ul>
+ </div>
+</div>
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
alt=""/>Android Studio v0.4.6</a> <em>(March 2014)</em>
</p>
@@ -650,7 +663,7 @@
if (os) {
/* set up primary ACE download button */
$('#download-ide-button').show();
- $('#download-ide-button').append("Download Android Studio <span class='small'>v0.4.6</span>"
+ $('#download-ide-button').append("Download Android Studio <span class='small'>v0.5.2</span>"
+ "<br/> <span class='small'>for " + os + "</span>");
$('#download-ide-button').click(function() {return onDownload(this,true);}).attr('href', bundlename);
diff --git a/docs/html/tools/sdk/eclipse-adt.jd b/docs/html/tools/sdk/eclipse-adt.jd
index d106f4a..124e58d 100644
--- a/docs/html/tools/sdk/eclipse-adt.jd
+++ b/docs/html/tools/sdk/eclipse-adt.jd
@@ -53,10 +53,49 @@
<p>For a summary of all known issues in ADT, see <a
href="http://tools.android.com/knownissues">http://tools.android.com/knownissues</a>.</p>
-
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+ alt=""/>ADT 22.6.3</a> <em>(April 2014)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+<dl>
+ <dt>Dependencies:</dt>
+
+ <dd>
+ <ul>
+ <li>Java 1.6 or higher is required.</li>
+ <li>Eclipse Indigo (Version 3.7.2) or higher is required.</li>
+ <li>This version of ADT is designed for use with
+ <a href="{@docRoot}tools/sdk/tools-notes.html">SDK Tools r22.6.3</a>.
+ If you haven't already installed SDK Tools r22.6.3 into your SDK, use the
+ Android SDK Manager to do so.</li>
+ </ul>
+ </dd>
+
+ <dt>General Notes:</dt>
+ <dd>
+ <ul>
+ <li>Fixed a problem where the AVD manager allowed creating Android Wear virtual devices
+ with a target API Level lower than 19.</li>
+ <li>Fixed the description of Android Wear system images in the SDK Manager.</li>
+ </ul>
+ </dd>
+
+ <dt>Known Issues:</dt>
+ <dd>
+ <p>When you create an Android Wear virtual device in the AVD manager, a target API Level
+ lower than 19 may be selected by default. Make sure you select the target API Level 19
+ when creating Android Wear virtual devices.</p>
+ </dd>
+</dl>
+</div>
+</div>
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
alt=""/>ADT 22.6.2</a> <em>(March 2014)</em>
</p>
diff --git a/docs/html/tools/sdk/ndk/index.jd b/docs/html/tools/sdk/ndk/index.jd
index a22dc90..0ac1881 100644
--- a/docs/html/tools/sdk/ndk/index.jd
+++ b/docs/html/tools/sdk/ndk/index.jd
@@ -245,8 +245,8 @@
but it always increases your app complexity. In general, you should only use the NDK
if it is essential to your app—never because you simply prefer to program in C/C++.</p>
- <p>Typical good candidates for the NDK are self-contained, CPU-intensive operations that don't
- allocate much memory, such as signal processing, physics simulation, and so on. When examining
+ <p>Typical good candidates for the NDK are CPU-intensive workloads such as game engines,
+ signal processing, physics simulation, and so on. When examining
whether or not you should develop in native code, think about your requirements and see if the
Android framework APIs provide the functionality that you need.</p>
diff --git a/docs/html/tools/sdk/tools-notes.jd b/docs/html/tools/sdk/tools-notes.jd
index 14b5505..9b06a9d 100644
--- a/docs/html/tools/sdk/tools-notes.jd
+++ b/docs/html/tools/sdk/tools-notes.jd
@@ -28,6 +28,46 @@
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+ alt=""/>SDK Tools, Revision 22.6.3</a> <em>(April 2014)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+
+ <dl>
+ <dt>Dependencies:</dt>
+
+ <dd>
+ <ul>
+ <li>Android SDK Platform-tools revision 18 or later.</li>
+ <li>If you are developing in Eclipse with ADT, note that this version of SDK Tools is
+ designed for use with ADT 22.6.3 and later. If you haven't already, update your
+ <a href="{@docRoot}tools/sdk/eclipse-adt.html">ADT Plugin</a> to 22.6.3.</li>
+ <li>If you are developing outside Eclipse, you must have
+ <a href="http://ant.apache.org/">Apache Ant</a> 1.8 or later.</li>
+ </ul>
+ </dd>
+
+ <dt>General Notes:</dt>
+ <dd>
+ <ul>
+ <li>Fixed a problem where the AVD manager allowed creating Android Wear virtual devices
+ with a target API Level lower than 19.</li>
+ <li>Fixed the description of Android Wear system images in the SDK Manager.</li>
+ </ul>
+ </dd>
+
+ <dt>Known Issues:</dt>
+ <dd>
+ <p>When you create an Android Wear virtual device in the AVD manager, a target API Level
+ lower than 19 may be selected by default. Make sure you select the target API Level 19
+ when creating Android Wear virtual devices.</p>
+ </dd>
+ </div>
+</div>
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
alt=""/>SDK Tools, Revision 22.6.2</a> <em>(March 2014)</em>
</p>
diff --git a/docs/html/wear/images/11_bundles_A.png b/docs/html/wear/images/11_bundles_A.png
index 7199a1f..0ffc6ec 100644
--- a/docs/html/wear/images/11_bundles_A.png
+++ b/docs/html/wear/images/11_bundles_A.png
Binary files differ
diff --git a/docs/html/wear/images/11_bundles_B.png b/docs/html/wear/images/11_bundles_B.png
index bb751a2..c188d3d 100644
--- a/docs/html/wear/images/11_bundles_B.png
+++ b/docs/html/wear/images/11_bundles_B.png
Binary files differ
diff --git a/docs/html/wear/images/11_bundles_C.png b/docs/html/wear/images/11_bundles_C.png
new file mode 100644
index 0000000..de71c59
--- /dev/null
+++ b/docs/html/wear/images/11_bundles_C.png
Binary files differ
diff --git a/docs/html/wear/notifications/stacks.jd b/docs/html/wear/notifications/stacks.jd
index 7f955f6..e723dab 100644
--- a/docs/html/wear/notifications/stacks.jd
+++ b/docs/html/wear/notifications/stacks.jd
@@ -2,7 +2,8 @@
@jd:body
-<img src="{@docRoot}wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
+<img src="{@docRoot}wear/images/11_bundles_C.png" height="154" width="273" style="float:right;margin:0 0 20px 40px"/>
+<img src="{@docRoot}wear/images/11_bundles_B.png" height="200" width="169" style="clear:both;float:right;margin:0 0 20px 40px" />
<img src="{@docRoot}wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
<p>When creating notifications for a handheld device, you should always aggregate similar
@@ -10,7 +11,7 @@
for received messages, you should not show more than one notification
on a handheld device—when more than one is message is received, use a single notification
to provide a summary such as "2 new messages."</p>
-
+<br />
<p>However, a summary notification is less useful on an Android wearable because users
are not able to read details from each message on the wearable (they must open your app on the
handheld to view more information). So for the wearable device, you should
@@ -25,25 +26,50 @@
Wear</a>.</p>
-<h2 id="AddGroup">Add Each Notification to a Group</h2>
+<h2 id="AddGroup">Create notifications in stacks</h2>
+
+<div style="float:right">
+ <script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
+ <div style="width:440;height:246; padding-left:20px">
+ <object type="application/x-shockwave-flash" id="ytapiplayer" data="//www.youtube.com/v/L4LvKOTkZ7Q?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1" width="440" height="246" style="visibility: visible;"><param name="allowScriptAccess" value="always"></object>
+ <script type="text/javascript">
+ var params = { allowScriptAccess: "always" };
+ var atts = { id: "ytapiplayer" };
+ swfobject.embedSWF("//www.youtube.com/v/L4LvKOTkZ7Q?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1",
+ "ytapiplayer", "440", "246", "8", null, null, params, atts);
+
+ // Callback used to pause/resume carousel based on video state
+ function onytplayerStateChange(newState) {
+ var isPaused = $("#pauseButton").hasClass("paused");
+ if ((newState == 1) || (newState == 3)) {
+ // if playing or buffering, pause the carousel
+ if (!isPaused) {
+ $("#pauseButton").click();
+ }
+ } else {
+ // otherwise, make sure carousel is running
+ if (isPaused) {
+ $("#pauseButton").click();
+ }
+ }
+ }
+
+ // Callback received when YouTube player loads to setup callback (above)
+ function onYouTubePlayerReady(playerId) {
+ var ytplayer = document.getElementById("ytapiplayer");
+ ytplayer.addEventListener("onStateChange", "onytplayerStateChange");
+ }
+
+ </script>
+ </div>
+</div>
+
<p>To create a stack, call <a
href="{@docRoot}reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setGroup(java.lang.String, int)">
<code>setGroup()</code></a> for each notification you want in the stack, passing the same
-group key. For example:</p>
+group key.</p>
-<pre style="clear:right">
-final static String GROUP_KEY_EMAILS = "group_key_emails";
-
-NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
- .setContentTitle("New mail from " + sender)
- .setContentText(subject)
- .setSmallIcon(R.drawable.new_mail);
-
-Notification notif = new WearableNotifications.Builder(builder)
- .setGroup(GROUP_KEY_EMAILS)
- .build();
-</pre>
<p>By default, notifications appear in the order in which you added them, with the most recent
notification visible at the top. You can define a specific position in the group
@@ -52,21 +78,82 @@
<code>setGroup()</code></a>.</p>
-<h2 id="AddSummary">Add a Summary Notification</h2>
-
-<p>It's important that you still provide a summary notification that appears on handheld devices.
+<p>It's also important that you still provide a summary notification that appears on handheld devices.
So in addition to adding each unique notification to the same stack group, also add a summary
notification, but set its order position to be <a
href="{@docRoot}reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.</p>
-<pre>
-Notification summaryNotification = new WearableNotifications.Builder(builder)
- .setGroup(GROUP_KEY_EMAILS, WearableNotifications.GROUP_ORDER_SUMMARY)
- .build();
-</pre>
-
<p>This notification will not appear in your stack of notifications on the wearable, but
appears as the only notification on the handheld device.
+</p>
+
+
+<p>Here's an example that creates a stack notification for a wearable and
+a summary notification for a handset device:</p>
+
+<pre style="clear:right">
+public void onClick_button_groups () {
+ Bitmap bitmapMila = BitmapFactory.decodeResource(getResources(),
+ R.drawable.mila128);
+
+ // Nuke all previous notifications and generate unique ids
+ NotificationManagerCompat.from(this).cancelAll();
+ int notificationId = 0;
+
+ // String to represent the group all the notifications will be a part of
+ final String GROUP_KEY_EMAILS = "group_key_messages";
+
+ // Group notification that will be visible on the phone
+ NotificationCompat.Builder builderG = new NotificationCompat.Builder(this)
+ .setContentTitle("2 Pet Notifications")
+ .setContentText("Mila and Dylan both sent messages")
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setLargeIcon(bitmapMila);
+ Notification summaryNotification = new WearableNotifications
+ .Builder(builderG)
+ .setGroup(GROUP_KEY_EMAILS,
+ WearableNotifications.GROUP_ORDER_SUMMARY)
+ .build();
+
+ // Separate notifications that will be visible on the watch
+ Intent viewIntent1 = new Intent(this, MainActivity.class);
+ PendingIntent viewPendingIntent1 =
+ PendingIntent.getActivity(this, notificationId+1, viewIntent1, 0);
+ NotificationCompat.Builder builder1 = new NotificationCompat.Builder(this)
+ .addAction(R.drawable.ic_action_done, "Treat Fed",
+ viewPendingIntent1)
+ .setContentTitle("Message from Mila")
+ .setContentText("What's for dinner? "
+ + "Can we have steak?")
+ .setSmallIcon(R.drawable.ic_launcher);
+ Notification notification1 = new WearableNotifications.Builder(builder1)
+ .setGroup(GROUP_KEY_EMAILS)
+ .build();
+
+ Intent viewIntent2 = new Intent(this, MainActivity.class);
+ PendingIntent viewPendingIntent2 =
+ PendingIntent.getActivity(this, notificationId+2, viewIntent2, 0);
+ NotificationCompat.Builder builder2 = new NotificationCompat.Builder(this)
+ .addAction(R.drawable.ic_action_done, "Water Filled",
+ viewPendingIntent2)
+ .setContentTitle("Message from Dylan")
+ .setContentText("Can you refill our water bowl?")
+ .setSmallIcon(R.drawable.ic_launcher);
+ Notification notification2 = new WearableNotifications.Builder(builder2)
+ .setGroup(GROUP_KEY_EMAILS)
+ .build();
+
+ // Issue the group notification
+ NotificationManagerCompat notificationManager =
+ NotificationManagerCompat.from(this);
+ notificationManager.notify(notificationId+0, summaryNotification);
+
+ // Issue the separate wear notifications
+ notificationManager.notify(notificationId+2, notification2);
+ notificationManager.notify(notificationId+1, notification1);
+}
+</pre>
+
</body>
</html>
diff --git a/drm/java/android/drm/DrmOutputStream.java b/drm/java/android/drm/DrmOutputStream.java
index 22e7ac2..ba1c56f 100644
--- a/drm/java/android/drm/DrmOutputStream.java
+++ b/drm/java/android/drm/DrmOutputStream.java
@@ -18,14 +18,14 @@
import static android.drm.DrmConvertedStatus.STATUS_OK;
import static android.drm.DrmManagerClient.INVALID_SESSION;
-import static libcore.io.OsConstants.SEEK_SET;
+import static android.system.OsConstants.SEEK_SET;
import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
import android.util.Log;
-import libcore.io.ErrnoException;
import libcore.io.IoBridge;
-import libcore.io.Libcore;
import libcore.io.Streams;
import java.io.FileDescriptor;
@@ -69,7 +69,7 @@
final DrmConvertedStatus status = mClient.closeConvertSession(mSessionId);
if (status.statusCode == STATUS_OK) {
try {
- Libcore.os.lseek(mFd, status.offset, SEEK_SET);
+ Os.lseek(mFd, status.offset, SEEK_SET);
} catch (ErrnoException e) {
e.rethrowAsIOException();
}
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 4ea7ec7..b5c0801 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -16,18 +16,21 @@
package android.graphics;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.drawable.Drawable;
import android.view.View;
/**
- * Defines an area of content.
+ * Defines a simple shape, used for bounding graphical regions.
*
- * Can be used with a View or Drawable to drive the shape of shadows cast by a
- * View, and allowing Views to clip inner content.
+ * Can be used with a View, or computed by a Drawable, to drive the shape of shadows cast by a
+ * View.
*
* @see View#setOutline(Outline)
- * @see View#setClipToOutline(boolean)
+ * @see Drawable#getOutline(Outline)
*/
-public class Outline {
+public final class Outline {
/** @hide */
public Rect mRect;
@@ -44,46 +47,74 @@
public Outline() {}
/**
+ * Constructs an Outline with a copy of the data in src.
+ */
+ public Outline(@Nullable Outline src) {
+ set(src);
+ }
+
+ /** @hide */
+ public void markInvalid() {
+ mRadius = 0;
+ mRect = null;
+ mPath = null;
+ }
+
+ /**
* Returns whether the Outline is valid for use with a View.
* <p>
* Outlines are invalid when constructed until a setter method is called.
*/
- public final boolean isValid() {
+ public boolean isValid() {
return mRect != null || mPath != null;
}
/**
- * @hide
- */
- public final boolean canClip() {
- return mPath == null;
- }
-
- /**
* Replace the contents of this Outline with the contents of src.
+ *
+ * @param src Source outline to copy from.
*/
- public void set(Outline src) {
- if (src.mPath != null) {
- if (mPath == null) {
- mPath = new Path();
- }
- mPath.set(src.mPath);
+ public void set(@Nullable Outline src) {
+ if (src == null) {
+ mRadius = 0;
mRect = null;
- }
- if (src.mRect != null) {
- if (mRect == null) {
- mRect = new Rect();
+ mPath = null;
+ } else {
+ if (src.mPath != null) {
+ if (mPath == null) {
+ mPath = new Path();
+ }
+ mPath.set(src.mPath);
+ mRect = null;
}
- mRect.set(src.mRect);
+ if (src.mRect != null) {
+ if (mRect == null) {
+ mRect = new Rect();
+ }
+ mRect.set(src.mRect);
+ }
+ mRadius = src.mRadius;
}
- mRadius = src.mRadius;
}
/**
* Sets the Outline to the rounded rect defined by the input rect, and corner radius.
- * <p>
- * Outlines produced by this method support
- * {@link View#setClipToOutline(boolean) View clipping.}
+ */
+ public void setRect(int left, int top, int right, int bottom) {
+ setRoundRect(left, top, right, bottom, 0.0f);
+ }
+
+ /**
+ * Convenience for {@link #setRect(int, int, int, int)}
+ */
+ public void setRect(@NonNull Rect rect) {
+ setRect(rect.left, rect.top, rect.right, rect.bottom);
+ }
+
+ /**
+ * Sets the Outline to the rounded rect defined by the input rect, and corner radius.
+ *
+ * Passing a zero radius is equivalent to calling {@link #setRect(int, int, int, int)}
*/
public void setRoundRect(int left, int top, int right, int bottom, float radius) {
if (mRect == null) mRect = new Rect();
@@ -93,11 +124,33 @@
}
/**
- * Sets the Constructs an Outline from a {@link android.graphics.Path#isConvex() convex path}.
- *
- * @hide
+ * Convenience for {@link #setRoundRect(int, int, int, int, float)}
*/
- public void setConvexPath(Path convexPath) {
+ public void setRoundRect(@NonNull Rect rect, float radius) {
+ setRoundRect(rect.left, rect.top, rect.right, rect.bottom, radius);
+ }
+
+ /**
+ * Sets the outline to the oval defined by input rect.
+ */
+ public void setOval(int left, int top, int right, int bottom) {
+ mRect = null;
+ if (mPath == null) mPath = new Path();
+ mPath.reset();
+ mPath.addOval(left, top, right, bottom, Path.Direction.CW);
+ }
+
+ /**
+ * Convenience for {@link #setOval(int, int, int, int)}
+ */
+ public void setOval(@NonNull Rect rect) {
+ setOval(rect.left, rect.top, rect.right, rect.bottom);
+ }
+
+ /**
+ * Sets the Constructs an Outline from a {@link android.graphics.Path#isConvex() convex path}.
+ */
+ public void setConvexPath(@NonNull Path convexPath) {
if (!convexPath.isConvex()) {
throw new IllegalArgumentException("path must be convex");
}
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index c07a6da..c600f47 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -499,11 +499,7 @@
* @param dir The direction to wind the rectangle's contour
*/
public void addRect(RectF rect, Direction dir) {
- if (rect == null) {
- throw new NullPointerException("need rect parameter");
- }
- detectSimplePath(rect.left, rect.top, rect.right, rect.bottom, dir);
- native_addRect(mNativePath, rect, dir.nativeInt);
+ addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
}
/**
@@ -527,11 +523,17 @@
* @param dir The direction to wind the oval's contour
*/
public void addOval(RectF oval, Direction dir) {
- if (oval == null) {
- throw new NullPointerException("need oval parameter");
- }
+ addOval(oval.left, oval.top, oval.right, oval.bottom, dir);
+ }
+
+ /**
+ * Add a closed oval contour to the path
+ *
+ * @param dir The direction to wind the oval's contour
+ */
+ public void addOval(float left, float top, float right, float bottom, Direction dir) {
isSimplePath = false;
- native_addOval(mNativePath, oval, dir.nativeInt);
+ native_addOval(mNativePath, left, top, right, bottom, dir.nativeInt);
}
/**
@@ -756,10 +758,10 @@
private static native void native_arcTo(long nPath, RectF oval,
float startAngle, float sweepAngle, boolean forceMoveTo);
private static native void native_close(long nPath);
- private static native void native_addRect(long nPath, RectF rect, int dir);
private static native void native_addRect(long nPath, float left, float top,
float right, float bottom, int dir);
- private static native void native_addOval(long nPath, RectF oval, int dir);
+ private static native void native_addOval(long nPath, float left, float top,
+ float right, float bottom, int dir);
private static native void native_addCircle(long nPath, float x, float y, float radius, int dir);
private static native void native_addArc(long nPath, RectF oval,
float startAngle, float sweepAngle);
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 74d1219..b9d5e19 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -864,22 +864,21 @@
}
/**
- * Returns the outline for this drawable if defined, null if not.
+ * Called to get the drawable to populate the Outline.
* <p>
- * This method will be called by a View on its background Drawable after
- * bounds change, if the View's Outline isn't set explicitly. This allows
- * the background Drawable to provide the shape of the shadow casting
- * portion of the View. It can also serve to clip the area of the View if
- * if {@link View#setClipToOutline(boolean)} is set on the View.
- * <p>
- * The Outline queried by the View will not be modified, and is treated as
- * a static shape that only needs to be requeried when the drawable's bounds
- * change.
+ * This method will be called by a View on its background Drawable after bounds change, or its
+ * Drawable is invalidated, if the View's Outline isn't set explicitly. This allows the
+ * background Drawable to define the shape of the shadow cast by the View.
*
- * @see View#setOutline(android.view.Outline)
- * @see View#setClipToOutline(boolean)
+ * The default behavior defines the outline to be the bounding rectangle. Subclasses that wish
+ * to convey a different shape must override this method.
+ *
+ * @see View#setOutline(android.graphics.Outline)
*/
- public Outline getOutline() { return null; }
+ public boolean getOutline(Outline outline) {
+ outline.setRect(getBounds());
+ return true;
+ }
/**
* Make this drawable mutable. This operation cannot be reversed. A mutable
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 1b5cefe..dc06350 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -139,8 +139,7 @@
private final Path mPath = new Path();
private final RectF mRect = new RectF();
- private Outline mOutline;
-
+
private Paint mLayerPaint; // internal, used if we use saveLayer()
private boolean mRectIsDirty; // internal state
private boolean mMutated;
@@ -573,15 +572,11 @@
mStrokePaint.setColorFilter(mColorFilter);
}
}
-
+
switch (st.mShape) {
case RECTANGLE:
if (st.mRadiusArray != null) {
- if (mPathIsDirty || mRectIsDirty) {
- mPath.reset();
- mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
- mPathIsDirty = mRectIsDirty = false;
- }
+ buildPathIfDirty();
canvas.drawPath(mPath, mFillPaint);
if (haveStroke) {
canvas.drawPath(mPath, mStrokePaint);
@@ -638,7 +633,16 @@
}
}
}
-
+
+ private void buildPathIfDirty() {
+ final GradientState st = mGradientState;
+ if (mPathIsDirty || mRectIsDirty) {
+ mPath.reset();
+ mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
+ mPathIsDirty = mRectIsDirty = false;
+ }
+ }
+
private Path buildRing(GradientState st) {
if (mRingPath != null && (!st.mUseLevelForShape || !mPathIsDirty)) return mRingPath;
mPathIsDirty = false;
@@ -862,7 +866,7 @@
float x0, x1, y0, y1;
if (st.mGradient == LINEAR_GRADIENT) {
- final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
+ final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
switch (st.mOrientation) {
case TOP_BOTTOM:
x0 = r.left; y0 = r.top;
@@ -1428,42 +1432,40 @@
}
@Override
- public Outline getOutline() {
+ public boolean getOutline(Outline outline) {
final GradientState st = mGradientState;
final Rect bounds = getBounds();
switch (st.mShape) {
case RECTANGLE:
if (st.mRadiusArray != null) {
- return null;
+ buildPathIfDirty();
+ outline.setConvexPath(mPath);
+ return true;
}
+
float rad = 0;
if (st.mRadius > 0.0f) {
// clamp the radius based on width & height, matching behavior in draw()
rad = Math.min(st.mRadius,
Math.min(bounds.width(), bounds.height()) * 0.5f);
}
- if (mOutline == null) {
- mOutline = new Outline();
- }
- mOutline.setRoundRect(bounds.left, bounds.top,
- bounds.right, bounds.bottom, rad);
- return mOutline;
- case LINE: {
+ outline.setRoundRect(bounds, rad);
+ return true;
+ case OVAL:
+ outline.setOval(bounds);
+ return true;
+ case LINE:
float halfStrokeWidth = mStrokePaint.getStrokeWidth() * 0.5f;
float centerY = bounds.centerY();
int top = (int) Math.floor(centerY - halfStrokeWidth);
int bottom = (int) Math.ceil(centerY + halfStrokeWidth);
- if (mOutline == null) {
- mOutline = new Outline();
- }
- mOutline.setRoundRect(bounds.left, top, bounds.right, bottom, 0);
- return mOutline;
- }
+ outline.setRect(bounds.left, top, bounds.right, bottom);
+ return true;
default:
// TODO: investigate
- return null;
+ return false;
}
}
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 96309f9..61b1b85 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -21,6 +21,7 @@
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
+import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff.Mode;
@@ -496,6 +497,16 @@
}
@Override
+ public boolean getOutline(Outline outline) {
+ if (mShapeState.mShape == null) {
+ // don't publish outline without a shape
+ return false;
+ }
+
+ return mShapeState.mShape.getOutline(outline);
+ }
+
+ @Override
public ConstantState getConstantState() {
mShapeState.mChangingConfigurations = getChangingConfigurations();
return mShapeState;
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index 3b9d513..0e8831f 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -48,6 +48,7 @@
public class TouchFeedbackDrawable extends LayerDrawable {
private static final String LOG_TAG = TouchFeedbackDrawable.class.getSimpleName();
private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
+ private static final PorterDuffXfermode SRC_OVER = new PorterDuffXfermode(Mode.SRC_OVER);
/** The maximum number of ripples supported. */
private static final int MAX_RIPPLES = 10;
@@ -105,6 +106,26 @@
protected boolean onStateChange(int[] stateSet) {
super.onStateChange(stateSet);
+ // TODO: Implicitly tie states to ripple IDs. For now, just clear
+ // focused and pressed if they aren't in the state set.
+ boolean hasFocused = false;
+ boolean hasPressed = false;
+ for (int i = 0; i < stateSet.length; i++) {
+ if (stateSet[i] == R.attr.state_pressed) {
+ hasPressed = true;
+ } else if (stateSet[i] == R.attr.state_focused) {
+ hasFocused = true;
+ }
+ }
+
+ if (!hasPressed) {
+ removeHotspot(R.attr.state_pressed);
+ }
+
+ if (!hasFocused) {
+ removeHotspot(R.attr.state_focused);
+ }
+
if (mRipplePaint != null && mState.mTint != null) {
final ColorStateList stateList = mState.mTint;
final int newColor = stateList.getColorForState(stateSet, 0);
@@ -122,7 +143,7 @@
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
-
+
if (!mOverrideBounds) {
mHotspotBounds.set(bounds);
}
@@ -217,6 +238,27 @@
super.inflate(r, parser, attrs, theme);
setTargetDensity(r.getDisplayMetrics());
+
+ // Find the mask
+ final int N = getNumberOfLayers();
+ for (int i = 0; i < N; i++) {
+ if (mLayerState.mChildren[i].mId == R.id.mask) {
+ mState.mMask = mLayerState.mChildren[i].mDrawable;
+ }
+ }
+ }
+
+ @Override
+ public boolean setDrawableByLayerId(int id, Drawable drawable) {
+ if (super.setDrawableByLayerId(id, drawable)) {
+ if (id == R.id.mask) {
+ mState.mMask = drawable;
+ }
+
+ return true;
+ }
+
+ return false;
}
/**
@@ -310,7 +352,7 @@
mTouchedRipples = new SparseArray<Ripple>();
mActiveRipples = new Ripple[MAX_RIPPLES];
}
-
+
if (mActiveRipplesCount >= MAX_RIPPLES) {
Log.e(LOG_TAG, "Max ripple count exceeded", new RuntimeException());
return;
@@ -415,99 +457,144 @@
@Override
public void draw(Canvas canvas) {
- final boolean projected = getNumberOfLayers() == 0;
- final Ripple[] activeRipples = mActiveRipples;
- final int ripplesCount = mActiveRipplesCount;
+ final int N = mLayerState.mNum;
final Rect bounds = getBounds();
+ final ChildDrawable[] array = mLayerState.mChildren;
+ final boolean maskOnly = mState.mMask != null && N == 1;
+
+ int restoreToCount = drawRippleLayer(canvas, bounds, maskOnly);
+
+ if (restoreToCount >= 0) {
+ // We have a ripple layer that contains ripples. If we also have an
+ // explicit mask drawable, apply it now using DST_IN blending.
+ if (mState.mMask != null) {
+ canvas.saveLayer(bounds.left, bounds.top, bounds.right,
+ bounds.bottom, getMaskingPaint(DST_IN));
+ mState.mMask.draw(canvas);
+ canvas.restoreToCount(restoreToCount);
+ restoreToCount = -1;
+ }
+
+ // If there's more content, we need an extra masking layer to merge
+ // the ripples over the content.
+ if (!maskOnly) {
+ final PorterDuffXfermode xfermode = mState.getTintXfermodeInverse();
+ final int count = canvas.saveLayer(bounds.left, bounds.top,
+ bounds.right, bounds.bottom, getMaskingPaint(xfermode));
+ if (restoreToCount < 0) {
+ restoreToCount = count;
+ }
+ }
+ }
+
+ // Draw everything except the mask.
+ for (int i = 0; i < N; i++) {
+ if (array[i].mId != R.id.mask) {
+ array[i].mDrawable.draw(canvas);
+ }
+ }
+
+ // Composite the layers if needed.
+ if (restoreToCount >= 0) {
+ canvas.restoreToCount(restoreToCount);
+ }
+ }
+
+ private int drawRippleLayer(Canvas canvas, Rect bounds, boolean maskOnly) {
+ final int ripplesCount = mActiveRipplesCount;
+ if (ripplesCount == 0) {
+ return -1;
+ }
+
+ final Ripple[] activeRipples = mActiveRipples;
+ final boolean projected = isProjected();
+ final Rect layerBounds = projected ? getDirtyBounds() : bounds;
+
+ // Separate the ripple color and alpha channel. The alpha will be
+ // applied when we merge the ripples down to the canvas.
+ final int rippleColor;
+ if (mState.mTint != null) {
+ rippleColor = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
+ } else {
+ rippleColor = Color.TRANSPARENT;
+ }
+ final int rippleAlpha = Color.alpha(rippleColor);
+
+ if (mRipplePaint == null) {
+ mRipplePaint = new Paint();
+ mRipplePaint.setAntiAlias(true);
+ }
+ final Paint ripplePaint = mRipplePaint;
+ ripplePaint.setColor(rippleColor);
+
+ boolean drewRipples = false;
+ int restoreToCount = -1;
+ int activeRipplesCount = 0;
// Draw ripples.
- boolean drewRipples = false;
- int rippleRestoreCount = -1;
- int activeRipplesCount = 0;
for (int i = 0; i < ripplesCount; i++) {
final Ripple ripple = activeRipples[i];
final RippleAnimator animator = ripple.animate();
animator.update();
+
+ // Mark and skip inactive ripples.
if (!animator.isRunning()) {
activeRipples[i] = null;
- } else {
- // If we're masking the ripple layer, make sure we have a layer
- // first. This will merge SRC_OVER (directly) onto the canvas.
- if (!projected && rippleRestoreCount < 0) {
- rippleRestoreCount = canvas.saveLayer(bounds.left, bounds.top,
- bounds.right, bounds.bottom, null);
+ continue;
+ }
+
+ // If we're masking the ripple layer, make sure we have a layer
+ // first. This will merge SRC_OVER (directly) onto the canvas.
+ if (restoreToCount < 0) {
+ // If we're projecting or we only have a mask, we want to treat the
+ // underlying canvas as our content and merge the ripple layer down
+ // using the tint xfermode.
+ final PorterDuffXfermode xfermode;
+ if (projected || maskOnly) {
+ xfermode = mState.getTintXfermode();
+ } else {
+ xfermode = SRC_OVER;
}
- drewRipples |= ripple.draw(canvas, getRipplePaint());
-
- activeRipples[activeRipplesCount] = activeRipples[i];
- activeRipplesCount++;
+ final Paint layerPaint = getMaskingPaint(xfermode);
+ layerPaint.setAlpha(rippleAlpha);
+ restoreToCount = canvas.saveLayer(layerBounds.left, layerBounds.top,
+ layerBounds.right, layerBounds.bottom, layerPaint);
+ layerPaint.setAlpha(255);
}
+
+ drewRipples |= ripple.draw(canvas, ripplePaint);
+
+ activeRipples[activeRipplesCount] = activeRipples[i];
+ activeRipplesCount++;
}
+
mActiveRipplesCount = activeRipplesCount;
- // TODO: Use the masking layer first, if there is one.
-
- // If we have ripples and content, we need a masking layer. This will
- // merge DST_ATOP onto (effectively under) the ripple layer.
- if (drewRipples && !projected && rippleRestoreCount >= 0) {
- final PorterDuffXfermode xfermode = mState.getTintXfermode();
- canvas.saveLayer(bounds.left, bounds.top,
- bounds.right, bounds.bottom, getMaskingPaint(xfermode));
+ // If we created a layer with no content, merge it immediately.
+ if (restoreToCount >= 0 && !drewRipples) {
+ canvas.restoreToCount(restoreToCount);
+ restoreToCount = -1;
}
- Drawable mask = null;
- final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
- for (int i = 0; i < N; i++) {
- if (array[i].mId != R.id.mask) {
- array[i].mDrawable.draw(canvas);
- } else {
- mask = array[i].mDrawable;
- }
- }
-
- // If we have ripples, mask them.
- if (mask != null && drewRipples) {
- // TODO: This will also mask the lower layer, which is bad.
- canvas.saveLayer(bounds.left, bounds.top, bounds.right,
- bounds.bottom, getMaskingPaint(DST_IN));
- mask.draw(canvas);
- }
-
- // Composite the layers if needed.
- if (rippleRestoreCount >= 0) {
- canvas.restoreToCount(rippleRestoreCount);
- }
+ return restoreToCount;
}
- private Paint getRipplePaint() {
- if (mRipplePaint == null) {
- mRipplePaint = new Paint();
- mRipplePaint.setAntiAlias(true);
-
- if (mState.mTint != null) {
- final int color = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
- mRipplePaint.setColor(color);
- }
- }
- return mRipplePaint;
- }
-
- private Paint getMaskingPaint(PorterDuffXfermode mode) {
+ private Paint getMaskingPaint(PorterDuffXfermode xfermode) {
if (mMaskingPaint == null) {
mMaskingPaint = new Paint();
}
- mMaskingPaint.setXfermode(mode);
+ mMaskingPaint.setXfermode(xfermode);
return mMaskingPaint;
}
@Override
public Rect getDirtyBounds() {
- final Rect dirtyBounds = mDirtyBounds;
final Rect drawingBounds = mDrawingBounds;
+ final Rect dirtyBounds = mDirtyBounds;
dirtyBounds.set(drawingBounds);
drawingBounds.setEmpty();
+
final Rect rippleBounds = mTempRect;
final Ripple[] activeRipples = mActiveRipples;
final int N = mActiveRipplesCount;
@@ -530,6 +617,8 @@
int[] mTouchThemeAttrs;
ColorStateList mTint;
PorterDuffXfermode mTintXfermode;
+ PorterDuffXfermode mTintXfermodeInverse;
+ Drawable mMask;
boolean mPinned;
public TouchFeedbackState(
@@ -540,19 +629,26 @@
mTouchThemeAttrs = orig.mTouchThemeAttrs;
mTint = orig.mTint;
mTintXfermode = orig.mTintXfermode;
+ mTintXfermodeInverse = orig.mTintXfermodeInverse;
mPinned = orig.mPinned;
+ mMask = orig.mMask;
}
}
-
+
public void setTintMode(Mode mode) {
final Mode invertedMode = TouchFeedbackState.invertPorterDuffMode(mode);
- mTintXfermode = new PorterDuffXfermode(invertedMode);
+ mTintXfermodeInverse = new PorterDuffXfermode(invertedMode);
+ mTintXfermode = new PorterDuffXfermode(mode);
}
-
+
public PorterDuffXfermode getTintXfermode() {
return mTintXfermode;
}
+ public PorterDuffXfermode getTintXfermodeInverse() {
+ return mTintXfermodeInverse;
+ }
+
@Override
public boolean canApplyTheme() {
return mTouchThemeAttrs != null || super.canApplyTheme();
diff --git a/graphics/java/android/graphics/drawable/shapes/OvalShape.java b/graphics/java/android/graphics/drawable/shapes/OvalShape.java
index c914999..198dcc1 100644
--- a/graphics/java/android/graphics/drawable/shapes/OvalShape.java
+++ b/graphics/java/android/graphics/drawable/shapes/OvalShape.java
@@ -17,7 +17,9 @@
package android.graphics.drawable.shapes;
import android.graphics.Canvas;
+import android.graphics.Outline;
import android.graphics.Paint;
+import android.graphics.RectF;
/**
* Defines an oval shape.
@@ -36,5 +38,13 @@
public void draw(Canvas canvas, Paint paint) {
canvas.drawOval(rect(), paint);
}
+
+ @Override
+ public boolean getOutline(Outline outline) {
+ final RectF rect = rect();
+ outline.setOval((int) Math.ceil(rect.left), (int) Math.ceil(rect.top),
+ (int) Math.floor(rect.right), (int) Math.floor(rect.bottom));
+ return true;
+ }
}
diff --git a/graphics/java/android/graphics/drawable/shapes/RectShape.java b/graphics/java/android/graphics/drawable/shapes/RectShape.java
index a3d2654..2a0256c 100644
--- a/graphics/java/android/graphics/drawable/shapes/RectShape.java
+++ b/graphics/java/android/graphics/drawable/shapes/RectShape.java
@@ -17,6 +17,7 @@
package android.graphics.drawable.shapes;
import android.graphics.Canvas;
+import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.RectF;
@@ -40,10 +41,18 @@
}
@Override
+ public boolean getOutline(Outline outline) {
+ final RectF rect = rect();
+ outline.setRect((int) Math.ceil(rect.left), (int) Math.ceil(rect.top),
+ (int) Math.floor(rect.right), (int) Math.floor(rect.bottom));
+ return true;
+ }
+
+ @Override
protected void onResize(float width, float height) {
mRect.set(0, 0, width, height);
}
-
+
/**
* Returns the RectF that defines this rectangle's bounds.
*/
diff --git a/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java
index b469d2a..a6bb1bb 100644
--- a/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java
+++ b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java
@@ -17,6 +17,7 @@
package android.graphics.drawable.shapes;
import android.graphics.Canvas;
+import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
@@ -77,11 +78,34 @@
public void draw(Canvas canvas, Paint paint) {
canvas.drawPath(mPath, paint);
}
-
+
+ @Override
+ public boolean getOutline(Outline outline) {
+ if (mInnerRect != null) return false; // have a hole, can't produce valid outline
+
+ float radius = 0;
+ if (mOuterRadii != null) {
+ radius = mOuterRadii[0];
+ for (int i = 1; i < 8; i++) {
+ if (mOuterRadii[i] != radius) {
+ // can't call simple constructors, use path
+ outline.setConvexPath(mPath);
+ return true;
+ }
+ }
+ }
+
+ final RectF rect = rect();
+ outline.setRoundRect((int) Math.ceil(rect.left), (int) Math.ceil(rect.top),
+ (int) Math.floor(rect.right), (int) Math.floor(rect.bottom),
+ radius);
+ return true;
+ }
+
@Override
protected void onResize(float w, float h) {
super.onResize(w, h);
-
+
RectF r = rect();
mPath.reset();
diff --git a/graphics/java/android/graphics/drawable/shapes/Shape.java b/graphics/java/android/graphics/drawable/shapes/Shape.java
index 4e192f95..1a20e8b 100644
--- a/graphics/java/android/graphics/drawable/shapes/Shape.java
+++ b/graphics/java/android/graphics/drawable/shapes/Shape.java
@@ -17,6 +17,7 @@
package android.graphics.drawable.shapes;
import android.graphics.Canvas;
+import android.graphics.Outline;
import android.graphics.Paint;
/**
@@ -43,7 +44,6 @@
return mHeight;
}
-
/**
* Draw this shape into the provided Canvas, with the provided Paint.
* Before calling this, you must call {@link #resize(float,float)}.
@@ -52,7 +52,6 @@
* @param paint the Paint object that defines this shape's characteristics
*/
public abstract void draw(Canvas canvas, Paint paint);
-
/**
* Resizes the dimensions of this shape.
@@ -93,8 +92,20 @@
*/
protected void onResize(float width, float height) {}
+ /**
+ * Compute the Outline of the shape.
+ *
+ * The default implementation does not supply an outline.
+ *
+ * @return True if a valid outline has been computed, false otherwise.
+ */
+ public boolean getOutline(Outline outline) {
+ return false;
+ }
+
@Override
public Shape clone() throws CloneNotSupportedException {
return (Shape) super.clone();
}
+
}
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index 904ec8c..c1af5f5 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -147,6 +147,8 @@
/**
* Generate an array of rays' direction vectors.
+ * To make sure the vertices generated are clockwise, the directions are from PI
+ * to -PI.
*
* @param rays The number of rays shooting out from the centroid.
* @param vertices Vertices of the polygon.
@@ -160,8 +162,8 @@
if (vertexCount * 2 > rays) {
float deltaAngle = 2 * M_PI / rays;
for (int i = 0; i < rays; i++) {
- dir[i].x = sinf(deltaAngle * i);
- dir[i].y = cosf(deltaAngle * i);
+ dir[i].x = cosf(M_PI - deltaAngle * i);
+ dir[i].y = sinf(M_PI - deltaAngle * i);
}
return;
}
@@ -178,50 +180,52 @@
// Since the incoming polygon is clockwise, we can find the dip to identify
// the minimal theta.
float polyThetas[vertexCount];
- int minimalPolyThetaIndex = 0;
+ int maxPolyThetaIndex = 0;
for (int i = 0; i < vertexCount; i++) {
polyThetas[i] = atan2(vertices[i].y - centroid3d.y,
vertices[i].x - centroid3d.x);
- if (i > 0 && polyThetas[i] < polyThetas[i - 1]) {
- minimalPolyThetaIndex = i;
+ if (i > 0 && polyThetas[i] > polyThetas[i - 1]) {
+ maxPolyThetaIndex = i;
}
}
- int polyThetaIndex = minimalPolyThetaIndex;
- float polyTheta = polyThetas[minimalPolyThetaIndex];
+ // Both poly's thetas and uniform thetas are in decrease order(clockwise)
+ // from PI to -PI.
+ int polyThetaIndex = maxPolyThetaIndex;
+ float polyTheta = polyThetas[maxPolyThetaIndex];
int uniformThetaIndex = 0;
- float uniformTheta = - M_PI;
+ float uniformTheta = M_PI;
for (int i = 0; i < rays; i++) {
// Compare both thetas and pick the smaller one and move on.
bool hasThetaCollision = abs(polyTheta - uniformTheta) < MINIMAL_DELTA_THETA;
- if (polyTheta < uniformTheta || hasThetaCollision) {
+ if (polyTheta > uniformTheta || hasThetaCollision) {
if (hasThetaCollision) {
// Shift the uniformTheta to middle way between current polyTheta
// and next uniform theta. The next uniform theta can wrap around
// to exactly PI safely here.
// Note that neither polyTheta nor uniformTheta can be FLT_MAX
// due to the hasThetaCollision is true.
- uniformTheta = (polyTheta + deltaAngle * (uniformThetaIndex + 1) - M_PI) / 2;
+ uniformTheta = (polyTheta + M_PI - deltaAngle * (uniformThetaIndex + 1)) / 2;
#if DEBUG_SHADOW
ALOGD("Shifted uniformTheta to %f", uniformTheta);
#endif
}
rayThetas[i] = polyTheta;
polyThetaIndex = (polyThetaIndex + 1) % vertexCount;
- if (polyThetaIndex != minimalPolyThetaIndex) {
+ if (polyThetaIndex != maxPolyThetaIndex) {
polyTheta = polyThetas[polyThetaIndex];
} else {
// out of poly points.
- polyTheta = FLT_MAX;
+ polyTheta = - FLT_MAX;
}
} else {
rayThetas[i] = uniformTheta;
uniformThetaIndex++;
if (uniformThetaIndex < uniformRayCount) {
- uniformTheta = deltaAngle * uniformThetaIndex - M_PI;
+ uniformTheta = M_PI - deltaAngle * uniformThetaIndex;
} else {
// out of uniform points.
- uniformTheta = FLT_MAX;
+ uniformTheta = - FLT_MAX;
}
}
}
@@ -232,8 +236,8 @@
#endif
// TODO: Fix the intersection precision problem and remvoe the delta added
// here.
- dir[i].x = sinf(rayThetas[i] + MINIMAL_DELTA_THETA);
- dir[i].y = cosf(rayThetas[i] + MINIMAL_DELTA_THETA);
+ dir[i].x = cosf(rayThetas[i] + MINIMAL_DELTA_THETA);
+ dir[i].y = sinf(rayThetas[i] + MINIMAL_DELTA_THETA);
}
}
@@ -308,13 +312,12 @@
Vector2 p1 = dir[preIndex] * rayDist[preIndex];
Vector2 p2 = dir[postIndex] * rayDist[postIndex];
- // Now the V (deltaX, deltaY) is the vector going CW around the poly.
+ // Now the rays are going CW around the poly.
Vector2 delta = p2 - p1;
if (delta.length() != 0) {
delta.normalize();
- // Calculate the normal , which is CCW 90 rotate to the V.
- // 90 degrees CCW about z-axis: (x, y, z) -> (-y, x, z)
- normal.x = -delta.y;
+ // Calculate the normal , which is CCW 90 rotate to the delta.
+ normal.x = - delta.y;
normal.y = delta.x;
}
}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 52be531..d324439 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -11,6 +11,7 @@
font/CacheTexture.cpp \
font/Font.cpp \
AmbientShadow.cpp \
+ Animator.cpp \
AssetAtlas.cpp \
FontRenderer.cpp \
GammaFontRenderer.cpp \
@@ -25,6 +26,7 @@
FboCache.cpp \
GradientCache.cpp \
Image.cpp \
+ Interpolator.cpp \
Layer.cpp \
LayerCache.cpp \
LayerRenderer.cpp \
@@ -66,6 +68,8 @@
$(LOCAL_PATH)/../../include/utils \
external/skia/src/core
+ include external/stlport/libstlport.mk
+
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
new file mode 100644
index 0000000..ee16586
--- /dev/null
+++ b/libs/hwui/Animator.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RT-Animator"
+
+#include "Animator.h"
+
+#include <set>
+
+#include "RenderProperties.h"
+
+namespace android {
+namespace uirenderer {
+
+/************************************************************
+ * Private header
+ ************************************************************/
+
+typedef void (RenderProperties::*SetFloatProperty)(float value);
+typedef float (RenderProperties::*GetFloatProperty)() const;
+
+struct PropertyAccessors {
+ GetFloatProperty getter;
+ SetFloatProperty setter;
+};
+
+// Maps RenderProperty enum to accessors
+static const PropertyAccessors PROPERTY_ACCESSOR_LUT[] = {
+ {&RenderProperties::getTranslationX, &RenderProperties::setTranslationX },
+ {&RenderProperties::getTranslationY, &RenderProperties::setTranslationY },
+ {&RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ },
+ {&RenderProperties::getScaleX, &RenderProperties::setScaleX },
+ {&RenderProperties::getScaleY, &RenderProperties::setScaleY },
+ {&RenderProperties::getRotation, &RenderProperties::setRotation },
+ {&RenderProperties::getRotationX, &RenderProperties::setRotationX },
+ {&RenderProperties::getRotationY, &RenderProperties::setRotationY },
+ {&RenderProperties::getX, &RenderProperties::setX },
+ {&RenderProperties::getY, &RenderProperties::setY },
+ {&RenderProperties::getZ, &RenderProperties::setZ },
+ {&RenderProperties::getAlpha, &RenderProperties::setAlpha },
+};
+
+// Helper class to contain generic animator helpers
+class BaseAnimator {
+public:
+ BaseAnimator();
+ virtual ~BaseAnimator();
+
+ void setInterpolator(Interpolator* interpolator);
+ void setDuration(nsecs_t durationInMs);
+
+ bool isFinished() { return mPlayState == FINISHED; }
+
+protected:
+ // This is the main animation entrypoint that subclasses should call
+ // to generate the onAnimation* lifecycle events
+ // Returns true if the animation has finished, false otherwise
+ bool animateFrame(nsecs_t frameTime);
+
+ // Called when PlayState switches from PENDING to RUNNING
+ virtual void onAnimationStarted() {}
+ virtual void onAnimationUpdated(float fraction) = 0;
+ virtual void onAnimationFinished() {}
+
+private:
+ enum PlayState {
+ PENDING,
+ RUNNING,
+ FINISHED,
+ };
+
+ Interpolator* mInterpolator;
+ PlayState mPlayState;
+ long mStartTime;
+ long mDuration;
+};
+
+// Hide the base classes & private bits from the exported RenderPropertyAnimator
+// in this Impl class so that subclasses of RenderPropertyAnimator don't require
+// knowledge of the inner guts but only the public virtual methods.
+// Animates a single property
+class RenderPropertyAnimatorImpl : public BaseAnimator {
+public:
+ RenderPropertyAnimatorImpl(GetFloatProperty getter, SetFloatProperty setter,
+ RenderPropertyAnimator::DeltaValueType deltaType, float delta);
+ ~RenderPropertyAnimatorImpl();
+
+ bool animate(RenderProperties* target, TreeInfo& info);
+
+protected:
+ virtual void onAnimationStarted();
+ virtual void onAnimationUpdated(float fraction);
+
+private:
+ // mTarget is only valid inside animate()
+ RenderProperties* mTarget;
+ GetFloatProperty mGetter;
+ SetFloatProperty mSetter;
+
+ RenderPropertyAnimator::DeltaValueType mDeltaValueType;
+ float mDeltaValue;
+ float mFromValue;
+};
+
+RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property,
+ DeltaValueType deltaType, float deltaValue) {
+ PropertyAccessors pa = PROPERTY_ACCESSOR_LUT[property];
+ mImpl = new RenderPropertyAnimatorImpl(pa.getter, pa.setter, deltaType, deltaValue);
+}
+
+RenderPropertyAnimator::~RenderPropertyAnimator() {
+ delete mImpl;
+ mImpl = NULL;
+}
+
+void RenderPropertyAnimator::setInterpolator(Interpolator* interpolator) {
+ mImpl->setInterpolator(interpolator);
+}
+
+void RenderPropertyAnimator::setDuration(nsecs_t durationInMs) {
+ mImpl->setDuration(durationInMs);
+}
+
+bool RenderPropertyAnimator::isFinished() {
+ return mImpl->isFinished();
+}
+
+bool RenderPropertyAnimator::animate(RenderProperties* target, TreeInfo& info) {
+ return mImpl->animate(target, info);
+}
+
+
+/************************************************************
+ * Base animator
+ ************************************************************/
+
+BaseAnimator::BaseAnimator()
+ : mInterpolator(0)
+ , mPlayState(PENDING)
+ , mStartTime(0)
+ , mDuration(300) {
+
+}
+
+BaseAnimator::~BaseAnimator() {
+ setInterpolator(NULL);
+}
+
+void BaseAnimator::setInterpolator(Interpolator* interpolator) {
+ delete mInterpolator;
+ mInterpolator = interpolator;
+}
+
+void BaseAnimator::setDuration(nsecs_t duration) {
+ mDuration = duration;
+}
+
+bool BaseAnimator::animateFrame(nsecs_t frameTime) {
+ if (mPlayState == PENDING) {
+ mPlayState = RUNNING;
+ mStartTime = frameTime;
+ // No interpolator was set, use the default
+ if (!mInterpolator) {
+ setInterpolator(Interpolator::createDefaultInterpolator());
+ }
+ onAnimationStarted();
+ }
+
+ float fraction = 1.0f;
+ if (mPlayState == RUNNING) {
+ fraction = mDuration > 0 ? (float)(frameTime - mStartTime) / mDuration : 1.0f;
+ if (fraction >= 1.0f) {
+ fraction = 1.0f;
+ mPlayState = FINISHED;
+ }
+ }
+ fraction = mInterpolator->interpolate(fraction);
+ onAnimationUpdated(fraction);
+
+ if (mPlayState == FINISHED) {
+ onAnimationFinished();
+ return true;
+ }
+ return false;
+}
+
+/************************************************************
+ * RenderPropertyAnimator
+ ************************************************************/
+
+RenderPropertyAnimatorImpl::RenderPropertyAnimatorImpl(
+ GetFloatProperty getter, SetFloatProperty setter,
+ RenderPropertyAnimator::DeltaValueType deltaType, float delta)
+ : mTarget(0)
+ , mGetter(getter)
+ , mSetter(setter)
+ , mDeltaValueType(deltaType)
+ , mDeltaValue(delta)
+ , mFromValue(-1) {
+}
+
+RenderPropertyAnimatorImpl::~RenderPropertyAnimatorImpl() {
+}
+
+bool RenderPropertyAnimatorImpl::animate(RenderProperties* target, TreeInfo& info) {
+ mTarget = target;
+ bool finished = animateFrame(info.frameTimeMs);
+ mTarget = NULL;
+ return finished;
+}
+
+void RenderPropertyAnimatorImpl::onAnimationStarted() {
+ mFromValue = (mTarget->*mGetter)();
+
+ if (mDeltaValueType == RenderPropertyAnimator::ABSOLUTE) {
+ mDeltaValue = (mDeltaValue - mFromValue);
+ mDeltaValueType = RenderPropertyAnimator::DELTA;
+ }
+}
+
+void RenderPropertyAnimatorImpl::onAnimationUpdated(float fraction) {
+ float value = mFromValue + (mDeltaValue * fraction);
+ (mTarget->*mSetter)(value);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
new file mode 100644
index 0000000..1c8361b
--- /dev/null
+++ b/libs/hwui/Animator.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANIMATOR_H
+#define ANIMATOR_H
+
+#include <cutils/compiler.h>
+
+#include "Interpolator.h"
+#include "TreeInfo.h"
+#include "utils/VirtualLightRefBase.h"
+
+namespace android {
+namespace uirenderer {
+
+class RenderProperties;
+class RenderPropertyAnimatorImpl;
+
+class RenderPropertyAnimator : public VirtualLightRefBase {
+public:
+ // Since the UI thread doesn't necessarily know what the current values
+ // actually are and thus can't do the calculations, this is used to inform
+ // the animator how to lazy-resolve the input value
+ enum DeltaValueType {
+ // The delta value represents an absolute value endpoint
+ // mDeltaValue needs to be recalculated to be mDelta = (mDelta - fromValue)
+ // in onAnimationStarted()
+ ABSOLUTE = 0,
+ // The final value represents an offset from the current value
+ // No recalculation is needed
+ DELTA,
+ };
+
+ enum RenderProperty {
+ TRANSLATION_X = 0,
+ TRANSLATION_Y,
+ TRANSLATION_Z,
+ SCALE_X,
+ SCALE_Y,
+ ROTATION,
+ ROTATION_X,
+ ROTATION_Y,
+ X,
+ Y,
+ Z,
+ ALPHA,
+ };
+
+ ANDROID_API void setInterpolator(Interpolator* interpolator);
+ ANDROID_API void setDuration(nsecs_t durationInMs);
+ ANDROID_API bool isFinished();
+
+ bool animate(RenderProperties* target, TreeInfo& info);
+
+protected:
+ ANDROID_API RenderPropertyAnimator(RenderProperty property, DeltaValueType deltaType,
+ float deltaValue);
+ ANDROID_API virtual ~RenderPropertyAnimator();
+
+private:
+ RenderPropertyAnimatorImpl* mImpl;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* ANIMATOR_H */
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index df2123b..43223ec 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -691,9 +691,10 @@
propertyAmbientShadowStrength = 25;
propertySpotShadowStrength = 25;
- propertyLightPosXScale = 0.5f;
- propertyLightPosYScale = 0.0f;
- propertyLightPosZScale = 1.0f;
+ propertyLightDiameter = -1.0f;
+ propertyLightPosY = -1.0f;
+ propertyLightPosZ = -1.0f;
+ propertyAmbientRatio = -1.0f;
}
void Caches::setTempProperty(const char* name, const char* value) {
@@ -706,17 +707,21 @@
propertySpotShadowStrength = atoi(value);
ALOGD("spot shadow strength = 0x%x out of 0xff", propertySpotShadowStrength);
return;
- } else if (!strcmp(name, "lightPosXScale")) {
- propertyLightPosXScale = fmin(fmax(atof(value), 0.0), 1.0);
- ALOGD("lightPos X Scale = %.2f", propertyLightPosXScale);
+ } else if (!strcmp(name, "ambientRatio")) {
+ propertyAmbientRatio = fmin(fmax(atof(value), 0.0), 10.0);
+ ALOGD("ambientRatio = %.2f", propertyAmbientRatio);
return;
- } else if (!strcmp(name, "lightPosYScale")) {
- propertyLightPosYScale = fmin(fmax(atof(value), 0.0), 1.0);
- ALOGD("lightPos Y Scale = %.2f", propertyLightPosXScale);
+ } else if (!strcmp(name, "lightDiameter")) {
+ propertyLightDiameter = fmin(fmax(atof(value), 0.0), 3000.0);
+ ALOGD("lightDiameter = %.2f", propertyLightDiameter);
return;
- } else if (!strcmp(name, "lightPosZScale")) {
- propertyLightPosZScale = fmin(fmax(atof(value), 0.0), 1.0);
- ALOGD("lightPos Z Scale = %.2f", propertyLightPosXScale);
+ } else if (!strcmp(name, "lightPosY")) {
+ propertyLightPosY = fmin(fmax(atof(value), 0.0), 3000.0);
+ ALOGD("lightPos Y = %.2f", propertyLightPosY);
+ return;
+ } else if (!strcmp(name, "lightPosZ")) {
+ propertyLightPosZ = fmin(fmax(atof(value), 0.0), 3000.0);
+ ALOGD("lightPos Z = %.2f", propertyLightPosZ);
return;
}
ALOGD(" failed");
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index ba3ccaf..2e2ee15 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -352,13 +352,10 @@
void initTempProperties();
void setTempProperty(const char* name, const char* value);
- // These scaling factors range from 0 to 1, to scale the light position
- // within the bound of (screenwidth, screenheight, max(screenwidth, screenheight));
- // The default scale is (0.5, 0, 1) which put the light at
- // (screenwidth / 2, 0, max(screenwidth, screenheight)).
- float propertyLightPosXScale;
- float propertyLightPosYScale;
- float propertyLightPosZScale;
+ float propertyLightDiameter;
+ float propertyLightPosY;
+ float propertyLightPosZ;
+ float propertyAmbientRatio;
int propertyAmbientShadowStrength;
int propertySpotShadowStrength;
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
new file mode 100644
index 0000000..004ddf5
--- /dev/null
+++ b/libs/hwui/Interpolator.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "Interpolator.h"
+
+#include <math.h>
+
+namespace android {
+namespace uirenderer {
+
+Interpolator* Interpolator::createDefaultInterpolator() {
+ return new AccelerateDecelerateInterpolator();
+}
+
+float AccelerateDecelerateInterpolator::interpolate(float input) {
+ return (float)(cosf((input + 1) * M_PI) / 2.0f) + 0.5f;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h
new file mode 100644
index 0000000..2cfb60c
--- /dev/null
+++ b/libs/hwui/Interpolator.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef INTERPOLATOR_H
+#define INTERPOLATOR_H
+
+namespace android {
+namespace uirenderer {
+
+class Interpolator {
+public:
+ virtual ~Interpolator() {}
+
+ virtual float interpolate(float input) = 0;
+
+ static Interpolator* createDefaultInterpolator();
+
+protected:
+ Interpolator() {}
+};
+
+class AccelerateDecelerateInterpolator : public Interpolator {
+public:
+ AccelerateDecelerateInterpolator() {}
+ virtual ~AccelerateDecelerateInterpolator() {}
+
+ virtual float interpolate(float input);
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* INTERPOLATOR_H */
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 1f5389c..95fdb04 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -3230,6 +3230,10 @@
const float casterRefinementThresholdSquared = 20.0f; // TODO: experiment with this value
PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
casterRefinementThresholdSquared, casterVertices2d);
+ if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) {
+ ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(),
+ casterVertices2d.size());
+ }
if (casterVertices2d.size() == 0) {
// empty caster polygon computed from path
@@ -3287,10 +3291,8 @@
if (mCaches.propertySpotShadowStrength > 0) {
paint.setARGB(casterAlpha * mCaches.propertySpotShadowStrength, 0, 0, 0);
VertexBuffer spotShadowVertexBuffer;
- Vector3 lightPosScale(mCaches.propertyLightPosXScale,
- mCaches.propertyLightPosYScale, mCaches.propertyLightPosZScale);
VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateSpotShadow(
- isCasterOpaque, casterPolygon, casterVertexCount, lightPosScale,
+ isCasterOpaque, casterPolygon, casterVertexCount,
*currentTransform(), getWidth(), getHeight(), casterBounds, localClip,
spotShadowVertexBuffer);
drawVertexBuffer(vertexBufferMode, spotShadowVertexBuffer, &paint);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 2008f02..7a9c181 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -18,6 +18,8 @@
#include "RenderNode.h"
+#include <algorithm>
+
#include <SkCanvas.h>
#include <algorithm>
@@ -54,7 +56,8 @@
: mNeedsPropertiesSync(false)
, mNeedsDisplayListDataSync(false)
, mDisplayListData(0)
- , mStagingDisplayListData(0) {
+ , mStagingDisplayListData(0)
+ , mNeedsAnimatorsSync(false) {
}
RenderNode::~RenderNode() {
@@ -97,15 +100,32 @@
}
void RenderNode::prepareTreeImpl(TreeInfo& info) {
- pushStagingChanges(info);
+ if (info.performStagingPush) {
+ pushStagingChanges(info);
+ }
+ if (info.evaluateAnimations) {
+ evaluateAnimations(info);
+ }
prepareSubTree(info, mDisplayListData);
}
+static bool is_finished(const sp<RenderPropertyAnimator>& animator) {
+ return animator->isFinished();
+}
+
void RenderNode::pushStagingChanges(TreeInfo& info) {
if (mNeedsPropertiesSync) {
mNeedsPropertiesSync = false;
mProperties = mStagingProperties;
}
+ if (mNeedsAnimatorsSync) {
+ mAnimators.resize(mStagingAnimators.size());
+ std::vector< sp<RenderPropertyAnimator> >::iterator it;
+ // hint: this means copy_if_not()
+ it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(),
+ mAnimators.begin(), is_finished);
+ mAnimators.resize(std::distance(mAnimators.begin(), it));
+ }
if (mNeedsDisplayListDataSync) {
mNeedsDisplayListDataSync = false;
// Do a push pass on the old tree to handle freeing DisplayListData
@@ -119,6 +139,34 @@
}
}
+class AnimateFunctor {
+public:
+ AnimateFunctor(RenderProperties* target, TreeInfo& info)
+ : mTarget(target), mInfo(info) {}
+
+ bool operator() (sp<RenderPropertyAnimator>& animator) {
+ bool finished = animator->animate(mTarget, mInfo);
+ if (finished && mInfo.animationListener) {
+ mInfo.animationListener->onAnimationFinished(animator);
+ }
+ return finished;
+ }
+private:
+ RenderProperties* mTarget;
+ TreeInfo& mInfo;
+};
+
+void RenderNode::evaluateAnimations(TreeInfo& info) {
+ if (!mAnimators.size()) return;
+
+ AnimateFunctor functor(&mProperties, info);
+ std::vector< sp<RenderPropertyAnimator> >::iterator newEnd;
+ newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
+ mAnimators.erase(newEnd, mAnimators.end());
+ mProperties.updateMatrix();
+ info.hasAnimations |= mAnimators.size();
+}
+
void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
if (subtree) {
TextureCache& cache = Caches::getInstance().textureCache;
@@ -219,11 +267,11 @@
matrix.multiply(anim);
}
- bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getTranslationZ());
+ bool applyTranslationZ = true3dTransform && !MathUtils::isZero(properties().getZ());
if (properties().hasTransformMatrix() || applyTranslationZ) {
if (properties().isTransformTranslateOnly()) {
matrix.translate(properties().getTranslationX(), properties().getTranslationY(),
- true3dTransform ? properties().getTranslationZ() : 0.0f);
+ true3dTransform ? properties().getZ() : 0.0f);
} else {
if (!true3dTransform) {
matrix.multiply(*properties().getTransformMatrix());
@@ -232,7 +280,7 @@
true3dMat.loadTranslate(
properties().getPivotX() + properties().getTranslationX(),
properties().getPivotY() + properties().getTranslationY(),
- properties().getTranslationZ());
+ properties().getZ());
true3dMat.rotate(properties().getRotationX(), 1, 0, 0);
true3dMat.rotate(properties().getRotationY(), 0, 1, 0);
true3dMat.rotate(properties().getRotation(), 0, 0, 1);
@@ -344,7 +392,9 @@
void RenderNode::deferNodeTree(DeferStateStruct& deferStruct) {
DeferOperationHandler handler(deferStruct, 0);
- if (properties().getTranslationZ() > 0.0f) issueDrawShadowOperation(Matrix4::identity(), handler);
+ if (MathUtils::isPositive(properties().getZ())) {
+ issueDrawShadowOperation(Matrix4::identity(), handler);
+ }
issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
}
@@ -380,7 +430,9 @@
void RenderNode::replayNodeTree(ReplayStateStruct& replayStruct) {
ReplayOperationHandler handler(replayStruct, 0);
- if (properties().getTranslationZ() > 0.0f) issueDrawShadowOperation(Matrix4::identity(), handler);
+ if (MathUtils::isPositive(properties().getZ())) {
+ issueDrawShadowOperation(Matrix4::identity(), handler);
+ }
issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
}
@@ -395,7 +447,7 @@
for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
DrawDisplayListOp* childOp = mDisplayListData->children()[i];
RenderNode* child = childOp->mDisplayList;
- float childZ = child->properties().getTranslationZ();
+ float childZ = child->properties().getZ();
if (!MathUtils::isZero(childZ)) {
zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index b9edbe5..294f436 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -20,6 +20,9 @@
#define LOG_TAG "OpenGLRenderer"
#endif
+#include <set>
+#include <vector>
+
#include <SkCamera.h>
#include <SkMatrix.h>
@@ -41,6 +44,7 @@
#include "DeferredDisplayList.h"
#include "DisplayList.h"
#include "RenderProperties.h"
+#include "TreeInfo.h"
#include "utils/VirtualLightRefBase.h"
class SkBitmap;
@@ -65,17 +69,6 @@
class RestoreToCountOp;
class DrawDisplayListOp;
-struct TreeInfo {
- TreeInfo()
- : hasFunctors(false)
- , prepareTextures(false)
- {}
-
- bool hasFunctors;
- bool prepareTextures;
- // TODO: Damage calculations? Flag to skip staging pushes for RT animations?
-};
-
/**
* Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
*
@@ -91,7 +84,7 @@
class RenderNode : public VirtualLightRefBase {
public:
ANDROID_API RenderNode();
- ANDROID_API ~RenderNode();
+ ANDROID_API virtual ~RenderNode();
// See flags defined in DisplayList.java
enum ReplayFlag {
@@ -152,7 +145,19 @@
return properties().getHeight();
}
- ANDROID_API void prepareTree(TreeInfo& info);
+ ANDROID_API virtual void prepareTree(TreeInfo& info);
+
+ // UI thread only!
+ ANDROID_API void addAnimator(const sp<RenderPropertyAnimator>& animator) {
+ mStagingAnimators.insert(animator);
+ mNeedsAnimatorsSync = true;
+ }
+
+ // UI thread only!
+ ANDROID_API void removeAnimator(const sp<RenderPropertyAnimator>& animator) {
+ mStagingAnimators.erase(animator);
+ mNeedsAnimatorsSync = true;
+ }
private:
typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
@@ -214,6 +219,7 @@
void prepareTreeImpl(TreeInfo& info);
void pushStagingChanges(TreeInfo& info);
+ void evaluateAnimations(TreeInfo& info);
void prepareSubTree(TreeInfo& info, DisplayListData* subtree);
String8 mName;
@@ -226,6 +232,10 @@
DisplayListData* mDisplayListData;
DisplayListData* mStagingDisplayListData;
+ bool mNeedsAnimatorsSync;
+ std::set< sp<RenderPropertyAnimator> > mStagingAnimators;
+ std::vector< sp<RenderPropertyAnimator> > mAnimators;
+
/**
* Draw time state - these properties are only set and used during rendering
*/
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index a922db8..99de1fc 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -37,6 +37,7 @@
, mProjectionReceiver(false)
, mAlpha(1)
, mHasOverlappingRendering(true)
+ , mElevation(0)
, mTranslationX(0), mTranslationY(0), mTranslationZ(0)
, mRotation(0), mRotationX(0), mRotationY(0)
, mScaleX(1), mScaleY(1)
@@ -50,7 +51,8 @@
RenderProperties::ComputedFields::ComputedFields()
: mTransformMatrix(NULL)
- , mClipPath(NULL) {
+ , mClipPath(NULL)
+ , mClipPathOp(SkRegion::kIntersect_Op) {
}
RenderProperties::ComputedFields::~ComputedFields() {
@@ -100,7 +102,7 @@
if (hasTransformMatrix()) {
if (isTransformTranslateOnly()) {
ALOGD("%*sTranslate %.2f, %.2f, %.2f",
- level * 2, "", mPrimitiveFields.mTranslationX, mPrimitiveFields.mTranslationY, mPrimitiveFields.mTranslationZ);
+ level * 2, "", getTranslationX(), getTranslationY(), getZ());
} else {
ALOGD("%*sConcatMatrix %p: " SK_MATRIX_STRING,
level * 2, "", mComputedFields.mTransformMatrix, SK_MATRIX_ARGS(mComputedFields.mTransformMatrix));
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 4270da2..6fc8bce 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -16,7 +16,9 @@
#ifndef RENDERNODEPROPERTIES_H
#define RENDERNODEPROPERTIES_H
+#include <algorithm>
#include <stddef.h>
+#include <vector>
#include <cutils/compiler.h>
#include <androidfw/ResourceTypes.h>
@@ -24,6 +26,7 @@
#include <SkMatrix.h>
#include <SkRegion.h>
+#include "Animator.h"
#include "Rect.h"
#include "RevealClip.h"
#include "Outline.h"
@@ -105,6 +108,17 @@
return mPrimitiveFields.mHasOverlappingRendering;
}
+ void setElevation(float elevation) {
+ if (elevation != mPrimitiveFields.mElevation) {
+ mPrimitiveFields.mElevation = elevation;
+ // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
+ }
+ }
+
+ float getElevation() const {
+ return mPrimitiveFields.mElevation;
+ }
+
void setTranslationX(float translationX) {
if (translationX != mPrimitiveFields.mTranslationX) {
mPrimitiveFields.mTranslationX = translationX;
@@ -130,7 +144,7 @@
void setTranslationZ(float translationZ) {
if (translationZ != mPrimitiveFields.mTranslationZ) {
mPrimitiveFields.mTranslationZ = translationZ;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
+ // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
}
}
@@ -138,6 +152,35 @@
return mPrimitiveFields.mTranslationZ;
}
+ // Animation helper
+ void setX(float value) {
+ setTranslationX(value - getLeft());
+ }
+
+ // Animation helper
+ float getX() const {
+ return getLeft() + getTranslationX();
+ }
+
+ // Animation helper
+ void setY(float value) {
+ setTranslationY(value - getTop());
+ }
+
+ // Animation helper
+ float getY() const {
+ return getTop() + getTranslationY();
+ }
+
+ // Animation helper
+ void setZ(float value) {
+ setTranslationZ(value - getElevation());
+ }
+
+ float getZ() const {
+ return getElevation() + getTranslationZ();
+ }
+
void setRotation(float rotation) {
if (rotation != mPrimitiveFields.mRotation) {
mPrimitiveFields.mRotation = rotation;
@@ -302,7 +345,8 @@
}
void setLeftTopRightBottom(int left, int top, int right, int bottom) {
- if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop || right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) {
+ if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop
+ || right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) {
mPrimitiveFields.mLeft = left;
mPrimitiveFields.mTop = top;
mPrimitiveFields.mRight = right;
@@ -429,6 +473,7 @@
bool mProjectionReceiver;
float mAlpha;
bool mHasOverlappingRendering;
+ float mElevation;
float mTranslationX, mTranslationY, mTranslationZ;
float mRotation, mRotationX, mRotationY;
float mScaleX, mScaleY;
@@ -440,7 +485,6 @@
bool mCaching;
} mPrimitiveFields;
- // mCameraDistance isn't in mPrimitiveFields as it has a complex setter
SkMatrix* mStaticMatrix;
SkMatrix* mAnimationMatrix;
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 4d0edfb..be49aed 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -22,6 +22,7 @@
#include <utils/Trace.h>
#include "AmbientShadow.h"
+#include "Caches.h"
#include "ShadowTessellator.h"
#include "SpotShadow.h"
@@ -41,9 +42,14 @@
// A bunch of parameters to tweak the shadow.
// TODO: Allow some of these changable by debug settings or APIs.
- const float heightFactor = 1.0f / 128;
+ float heightFactor = 1.0f / 128;
const float geomFactor = 64;
+ Caches& caches = Caches::getInstance();
+ if (CC_UNLIKELY(caches.propertyAmbientRatio > 0.0f)) {
+ heightFactor *= caches.propertyAmbientRatio;
+ }
+
Rect ambientShadowBounds(casterBounds);
ambientShadowBounds.outset(maxZ * geomFactor * heightFactor);
@@ -62,16 +68,26 @@
VertexBufferMode ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
- const Vector3& lightPosScale, const mat4& receiverTransform,
+ const mat4& receiverTransform,
int screenWidth, int screenHeight, const Rect& casterBounds,
const Rect& localClip, VertexBuffer& shadowVertexBuffer) {
ATRACE_CALL();
+ Caches& caches = Caches::getInstance();
+
// A bunch of parameters to tweak the shadow.
// TODO: Allow some of these changable by debug settings or APIs.
int maximal = max(screenWidth, screenHeight);
- Vector3 lightCenter(screenWidth * lightPosScale.x, screenHeight * lightPosScale.y,
- maximal * lightPosScale.z);
+ Vector3 lightCenter(screenWidth * 0.5f, 0, maximal);
+
+ if (CC_UNLIKELY(caches.propertyLightPosY > 0)) {
+ lightCenter.y = - caches.propertyLightPosY; // negated since this shifts up
+ }
+ if (CC_UNLIKELY(caches.propertyLightPosZ > 0)) {
+ lightCenter.z = caches.propertyLightPosZ;
+ }
+
+
#if DEBUG_SHADOW
ALOGD("light center %f %f %f", lightCenter.x, lightCenter.y, lightCenter.z);
#endif
@@ -82,9 +98,13 @@
reverseReceiverTransform.loadInverse(receiverTransform);
reverseReceiverTransform.mapPoint3d(lightCenter);
- const float lightSize = maximal / 4;
+ float lightSize = maximal / 4;
const int lightVertexCount = 8;
+ if (CC_UNLIKELY(caches.propertyLightDiameter > 0)) {
+ lightSize = caches.propertyLightDiameter;
+ }
+
// Now light and caster are both in local space, we will check whether
// the shadow is within the clip area.
Rect lightRect = Rect(lightCenter.x - lightSize, lightCenter.y - lightSize,
@@ -169,5 +189,67 @@
return centroid;
}
+/**
+ * Test whether the polygon is order in clockwise.
+ *
+ * @param polygon the polygon as a Vector2 array
+ * @param len the number of points of the polygon
+ */
+bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) {
+ double sum = 0;
+ double p1x = polygon[len - 1].x;
+ double p1y = polygon[len - 1].y;
+ for (int i = 0; i < len; i++) {
+
+ double p2x = polygon[i].x;
+ double p2y = polygon[i].y;
+ sum += p1x * p2y - p2x * p1y;
+ p1x = p2x;
+ p1y = p2y;
+ }
+ return sum < 0;
+}
+
+bool ShadowTessellator::isClockwisePath(const SkPath& path) {
+ SkPath::Iter iter(path, false);
+ SkPoint pts[4];
+ SkPath::Verb v;
+
+ Vector<Vector2> arrayForDirection;
+ while (SkPath::kDone_Verb != (v = iter.next(pts))) {
+ switch (v) {
+ case SkPath::kMove_Verb:
+ arrayForDirection.add(Vector2(pts[0].x(), pts[0].y()));
+ break;
+ case SkPath::kLine_Verb:
+ arrayForDirection.add(Vector2(pts[1].x(), pts[1].y()));
+ break;
+ case SkPath::kQuad_Verb:
+ arrayForDirection.add(Vector2(pts[1].x(), pts[1].y()));
+ arrayForDirection.add(Vector2(pts[2].x(), pts[2].y()));
+ break;
+ case SkPath::kCubic_Verb:
+ arrayForDirection.add(Vector2(pts[1].x(), pts[1].y()));
+ arrayForDirection.add(Vector2(pts[2].x(), pts[2].y()));
+ arrayForDirection.add(Vector2(pts[3].x(), pts[3].y()));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return isClockwise(arrayForDirection.array(), arrayForDirection.size());
+}
+
+void ShadowTessellator::reverseVertexArray(Vertex* polygon, int len) {
+ int n = len / 2;
+ for (int i = 0; i < n; i++) {
+ Vertex tmp = polygon[i];
+ int k = len - 1 - i;
+ polygon[i] = polygon[k];
+ polygon[k] = tmp;
+ }
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index 05370dd..e5a3da1 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -73,13 +73,34 @@
static VertexBufferMode tessellateSpotShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
- const Vector3& lightPosScale, const mat4& receiverTransform,
+ const mat4& receiverTransform,
int screenWidth, int screenHeight, const Rect& casterBounds,
const Rect& localClip, VertexBuffer& shadowVertexBuffer);
static void generateShadowIndices(uint16_t* shadowIndices);
static Vector2 centroid2d(const Vector2* poly, int polyLength);
+
+ static bool isClockwise(const Vector2* polygon, int len);
+
+ /**
+ * Determine whether the path is clockwise, using the control points.
+ *
+ * TODO: Given the skia is using inverted Y coordinate, shadow system needs
+ * to convert to the same coordinate to avoid the extra reverse.
+ *
+ * @param path The path to be examined.
+ */
+ static bool isClockwisePath(const SkPath &path);
+
+ /**
+ * Reverse the vertex array.
+ *
+ * @param polygon The vertex array to be reversed.
+ * @param len The length of the vertex array.
+ */
+ static void reverseVertexArray(Vertex* polygon, int len);
+
}; // ShadowTessellator
}; // namespace uirenderer
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 5fa0ba5..3ebe7b4 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -174,10 +174,10 @@
int SpotShadow::intersection(const Vector2* poly1, int poly1Length,
Vector2* poly2, int poly2Length) {
#if DEBUG_SHADOW
- if (!isClockwise(poly1, poly1Length)) {
+ if (!ShadowTessellator::isClockwise(poly1, poly1Length)) {
ALOGW("Poly1 is not clockwise! Intersection is wrong!");
}
- if (!isClockwise(poly2, poly2Length)) {
+ if (!ShadowTessellator::isClockwise(poly2, poly2Length)) {
ALOGW("Poly2 is not clockwise! Intersection is wrong!");
}
#endif
@@ -407,33 +407,12 @@
if (polygon == 0 || len == 0) {
return;
}
- if (!isClockwise(polygon, len)) {
+ if (!ShadowTessellator::isClockwise(polygon, len)) {
reverse(polygon, len);
}
}
/**
- * Test whether the polygon is order in clockwise.
- *
- * @param polygon the polygon as a Vector2 array
- * @param len the number of points of the polygon
- */
-bool SpotShadow::isClockwise(const Vector2* polygon, int len) {
- double sum = 0;
- double p1x = polygon[len - 1].x;
- double p1y = polygon[len - 1].y;
- for (int i = 0; i < len; i++) {
-
- double p2x = polygon[i].x;
- double p2y = polygon[i].y;
- sum += p1x * p2y - p2x * p1y;
- p1x = p2x;
- p1y = p2y;
- }
- return sum < 0;
-}
-
-/**
* Reverse the polygon
*
* @param polygon the polygon as a Vector2 array
diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h
index 599d37e..fb3e6d5 100644
--- a/libs/hwui/SpotShadow.h
+++ b/libs/hwui/SpotShadow.h
@@ -56,7 +56,6 @@
static bool testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len);
static void makeClockwise(Vector2* polygon, int len);
- static bool isClockwise(const Vector2* polygon, int len);
static void reverse(Vector2* polygon, int len);
static inline bool lineIntersection(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4, Vector2& ret);
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
new file mode 100644
index 0000000..8957607
--- /dev/null
+++ b/libs/hwui/TreeInfo.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef TREEINFO_H
+#define TREEINFO_H
+
+#include <cutils/compiler.h>
+#include <utils/Timers.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace uirenderer {
+
+class RenderPropertyAnimator;
+
+class AnimationListener {
+public:
+ ANDROID_API virtual void onAnimationFinished(const sp<RenderPropertyAnimator>&) = 0;
+protected:
+ ANDROID_API virtual ~AnimationListener() {}
+};
+
+struct TreeInfo {
+ // The defaults here should be safe for everyone but DrawFrameTask to use as-is.
+ TreeInfo()
+ : hasFunctors(false)
+ , prepareTextures(false)
+ , performStagingPush(true)
+ , frameTimeMs(0)
+ , evaluateAnimations(false)
+ , hasAnimations(false)
+ , animationListener(0)
+ {}
+
+ bool hasFunctors;
+ bool prepareTextures;
+ bool performStagingPush;
+
+ // Animations
+ nsecs_t frameTimeMs;
+ bool evaluateAnimations;
+ // This is only updated if evaluateAnimations is true
+ bool hasAnimations;
+ AnimationListener* animationListener;
+
+ // TODO: Damage calculations
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TREEINFO_H */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 5ce7ba6..63f4b95 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -308,18 +308,20 @@
return value == EGL_BUFFER_PRESERVED;
}
-CanvasContext::CanvasContext(bool translucent)
+CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode)
: mRenderThread(RenderThread::getInstance())
, mEglSurface(EGL_NO_SURFACE)
, mDirtyRegionsEnabled(false)
, mOpaque(!translucent)
, mCanvas(0)
- , mHaveNewSurface(false) {
+ , mHaveNewSurface(false)
+ , mRootRenderNode(rootRenderNode) {
mGlobalContext = GlobalContext::get();
}
CanvasContext::~CanvasContext() {
destroyCanvasAndSurface();
+ mRenderThread.removeFrameCallback(this);
}
void CanvasContext::destroyCanvasAndSurface() {
@@ -403,7 +405,16 @@
}
}
-void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) {
+void CanvasContext::prepareTree(TreeInfo& info) {
+ mRootRenderNode->prepareTree(info);
+
+ if (info.hasAnimations && !info.hasFunctors) {
+ // TODO: Functors
+ mRenderThread.postFrameCallback(this);
+ }
+}
+
+void CanvasContext::draw(Rect* dirty) {
LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
"drawDisplayList called on a context with no canvas or surface!");
@@ -417,7 +428,7 @@
}
status_t status;
- if (dirty) {
+ if (dirty && !dirty->isEmpty()) {
status = mCanvas->prepareDirty(dirty->left, dirty->top,
dirty->right, dirty->bottom, mOpaque);
} else {
@@ -425,7 +436,7 @@
}
Rect outBounds;
- status |= mCanvas->drawDisplayList(displayList, outBounds);
+ status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds);
// TODO: Draw debug info
// TODO: Performance tracking
@@ -437,6 +448,20 @@
}
}
+// Called by choreographer to do an RT-driven animation
+void CanvasContext::doFrame(nsecs_t frameTimeNanos) {
+ ATRACE_CALL();
+
+ TreeInfo info;
+ info.evaluateAnimations = true;
+ info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNanos);
+ info.performStagingPush = false;
+ info.prepareTextures = false;
+
+ prepareTree(info);
+ draw(NULL);
+}
+
void CanvasContext::invokeFunctor(Functor* functor) {
ATRACE_CALL();
DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index a3fe591..0873ad4 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -25,6 +25,7 @@
#include "../RenderNode.h"
#include "RenderTask.h"
+#include "RenderThread.h"
#define FUNCTOR_PROCESS_DELAY 4
@@ -39,15 +40,13 @@
namespace renderthread {
class GlobalContext;
-class CanvasContext;
-class RenderThread;
// This per-renderer class manages the bridge between the global EGL context
// and the render surface.
-class CanvasContext {
+class CanvasContext : public IFrameCallback {
public:
- CanvasContext(bool translucent);
- ~CanvasContext();
+ CanvasContext(bool translucent, RenderNode* rootRenderNode);
+ virtual ~CanvasContext();
bool initialize(EGLNativeWindowType window);
void updateSurface(EGLNativeWindowType window);
@@ -55,9 +54,13 @@
void setup(int width, int height);
void makeCurrent();
void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
- void drawDisplayList(RenderNode* displayList, Rect* dirty);
+ void prepareTree(TreeInfo& info);
+ void draw(Rect* dirty);
void destroyCanvasAndSurface();
+ // IFrameCallback, Chroreographer-driven frame callback entry point
+ virtual void doFrame(nsecs_t frameTimeNanos);
+
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
void invokeFunctor(Functor* functor);
@@ -82,6 +85,8 @@
bool mOpaque;
OpenGLRenderer* mCanvas;
bool mHaveNewSurface;
+
+ const sp<RenderNode> mRootRenderNode;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index f542d43..ff4be71 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -30,7 +30,7 @@
namespace uirenderer {
namespace renderthread {
-DrawFrameTask::DrawFrameTask() : mContext(0), mRenderNode(0) {
+DrawFrameTask::DrawFrameTask() : mContext(0) {
}
DrawFrameTask::~DrawFrameTask() {
@@ -55,25 +55,17 @@
}
}
-void DrawFrameTask::setRenderNode(RenderNode* renderNode) {
- LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to setRenderNode with!");
-
- mRenderNode = renderNode;
-}
-
void DrawFrameTask::setDirty(int left, int top, int right, int bottom) {
mDirty.set(left, top, right, bottom);
}
void DrawFrameTask::drawFrame(RenderThread* renderThread) {
- LOG_ALWAYS_FATAL_IF(!mRenderNode.get(), "Cannot drawFrame with no render node!");
LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
postAndWait(renderThread);
// Reset the single-frame data
mDirty.setEmpty();
- mRenderNode = 0;
}
void DrawFrameTask::postAndWait(RenderThread* renderThread) {
@@ -88,8 +80,7 @@
bool canUnblockUiThread = syncFrameState();
// Grab a copy of everything we need
- Rect dirtyCopy(mDirty);
- sp<RenderNode> renderNode = mRenderNode;
+ Rect dirty(mDirty);
CanvasContext* context = mContext;
// From this point on anything in "this" is *UNSAFE TO ACCESS*
@@ -97,15 +88,20 @@
unblockUiThread();
}
- drawRenderNode(context, renderNode.get(), &dirtyCopy);
+ context->draw(&dirty);
if (!canUnblockUiThread) {
unblockUiThread();
}
}
-static void prepareTreeInfo(TreeInfo& info) {
+static void initTreeInfo(TreeInfo& info) {
info.prepareTextures = true;
+ info.performStagingPush = true;
+ info.evaluateAnimations = true;
+ // TODO: Get this from Choreographer
+ nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
+ info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNs);
}
bool DrawFrameTask::syncFrameState() {
@@ -113,9 +109,9 @@
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse();
TreeInfo info;
- prepareTreeInfo(info);
+ initTreeInfo(info);
mContext->processLayerUpdates(&mLayers, info);
- mRenderNode->prepareTree(info);
+ mContext->prepareTree(info);
// If prepareTextures is false, we ran out of texture cache space
return !info.hasFunctors && info.prepareTextures;
}
@@ -125,16 +121,6 @@
mSignal.signal();
}
-void DrawFrameTask::drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty) {
- ATRACE_CALL();
-
- if (dirty->bottom == -1 && dirty->left == -1
- && dirty->top == -1 && dirty->right == -1) {
- dirty = 0;
- }
- context->drawDisplayList(renderNode, dirty);
-}
-
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 055d4cf..c280868 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -53,7 +53,6 @@
void addLayer(DeferredLayerUpdater* layer);
void removeLayer(DeferredLayerUpdater* layer);
- void setRenderNode(RenderNode* renderNode);
void setDirty(int left, int top, int right, int bottom);
void drawFrame(RenderThread* renderThread);
@@ -63,7 +62,6 @@
void postAndWait(RenderThread* renderThread);
bool syncFrameState();
void unblockUiThread();
- static void drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty);
Mutex mLock;
Condition mSignal;
@@ -73,7 +71,6 @@
/*********************************************
* Single frame data
*********************************************/
- sp<RenderNode> mRenderNode;
Rect mDirty;
/*********************************************
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ce490f1..87886e6 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -51,15 +51,16 @@
MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
ARGS(method) *args = (ARGS(method) *) task->payload()
-CREATE_BRIDGE1(createContext, bool translucent) {
- return new CanvasContext(args->translucent);
+CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) {
+ return new CanvasContext(args->translucent, args->rootRenderNode);
}
-RenderProxy::RenderProxy(bool translucent)
+RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode)
: mRenderThread(RenderThread::getInstance())
, mContext(0) {
SETUP_TASK(createContext);
args->translucent = translucent;
+ args->rootRenderNode = rootRenderNode;
mContext = (CanvasContext*) postAndWait(task);
mDrawFrameTask.setContext(mContext);
}
@@ -133,9 +134,8 @@
post(task);
}
-void RenderProxy::drawDisplayList(RenderNode* displayList,
+void RenderProxy::syncAndDrawFrame(
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
- mDrawFrameTask.setRenderNode(displayList);
mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
mDrawFrameTask.drawFrame(&mRenderThread);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index a112493..eab1395 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -56,14 +56,14 @@
*/
class ANDROID_API RenderProxy {
public:
- ANDROID_API RenderProxy(bool translucent);
+ ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode);
ANDROID_API virtual ~RenderProxy();
ANDROID_API bool initialize(const sp<ANativeWindow>& window);
ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
ANDROID_API void setup(int width, int height);
- ANDROID_API void drawDisplayList(RenderNode* displayList,
+ ANDROID_API void syncAndDrawFrame(
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
ANDROID_API void destroyCanvasAndSurface();
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 212f475..e95707a 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -18,9 +18,11 @@
#include "RenderThread.h"
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+
#include "CanvasContext.h"
#include "RenderProxy.h"
-#include <utils/Log.h>
namespace android {
using namespace uirenderer::renderthread;
@@ -29,6 +31,14 @@
namespace uirenderer {
namespace renderthread {
+// Number of events to read at a time from the DisplayEventReceiver pipe.
+// The value should be large enough that we can quickly drain the pipe
+// using just a few large reads.
+static const size_t EVENT_BUFFER_SIZE = 100;
+
+// Slight delay to give the UI time to push us a new frame before we replay
+static const int DISPATCH_FRAME_CALLBACKS_DELAY = 0;
+
TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
RenderTask* TaskQueue::next() {
@@ -103,8 +113,25 @@
}
}
+class DispatchFrameCallbacks : public RenderTask {
+private:
+ RenderThread* mRenderThread;
+public:
+ DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {}
+
+ virtual void run() {
+ mRenderThread->dispatchFrameCallbacks();
+ }
+};
+
RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
- , mNextWakeup(LLONG_MAX) {
+ , mNextWakeup(LLONG_MAX)
+ , mDisplayEventReceiver(0)
+ , mVsyncRequested(false)
+ , mFrameCallbackTaskPending(false)
+ , mFrameCallbackTask(0)
+ , mFrameTime(0) {
+ mFrameCallbackTask = new DispatchFrameCallbacks(this);
mLooper = new Looper(false);
run("RenderThread");
}
@@ -112,10 +139,86 @@
RenderThread::~RenderThread() {
}
+void RenderThread::initializeDisplayEventReceiver() {
+ LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?");
+ mDisplayEventReceiver = new DisplayEventReceiver();
+ status_t status = mDisplayEventReceiver->initCheck();
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver "
+ "failed with status: %d", status);
+
+ // Register the FD
+ mLooper->addFd(mDisplayEventReceiver->getFd(), 0,
+ Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);
+}
+
+int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
+ if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+ ALOGE("Display event receiver pipe was closed or an error occurred. "
+ "events=0x%x", events);
+ return 0; // remove the callback
+ }
+
+ if (!(events & Looper::EVENT_INPUT)) {
+ ALOGW("Received spurious callback for unhandled poll event. "
+ "events=0x%x", events);
+ return 1; // keep the callback
+ }
+
+ reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue();
+
+ return 1; // keep the callback
+}
+
+static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) {
+ DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+ nsecs_t latest = 0;
+ ssize_t n;
+ while ((n = receiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ for (ssize_t i = 0; i < n; i++) {
+ const DisplayEventReceiver::Event& ev = buf[i];
+ switch (ev.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ latest = ev.header.timestamp;
+ break;
+ }
+ }
+ }
+ if (n < 0) {
+ ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
+ }
+ return latest;
+}
+
+void RenderThread::drainDisplayEventQueue() {
+ nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
+ if (vsyncEvent > 0) {
+ mVsyncRequested = false;
+ mFrameTime = vsyncEvent;
+ if (!mFrameCallbackTaskPending) {
+ mFrameCallbackTaskPending = true;
+ //queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
+ queue(mFrameCallbackTask);
+ }
+ }
+}
+
+void RenderThread::dispatchFrameCallbacks() {
+ mFrameCallbackTaskPending = false;
+
+ std::set<IFrameCallback*> callbacks;
+ mFrameCallbacks.swap(callbacks);
+
+ for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) {
+ (*it)->doFrame(mFrameTime);
+ }
+}
+
bool RenderThread::threadLoop() {
+ initializeDisplayEventReceiver();
+
int timeoutMillis = -1;
for (;;) {
- int result = mLooper->pollAll(timeoutMillis);
+ int result = mLooper->pollOnce(timeoutMillis);
LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,
"RenderThread Looper POLL_ERROR!");
@@ -159,6 +262,20 @@
mQueue.remove(task);
}
+void RenderThread::postFrameCallback(IFrameCallback* callback) {
+ mFrameCallbacks.insert(callback);
+ if (!mVsyncRequested) {
+ mVsyncRequested = true;
+ status_t status = mDisplayEventReceiver->requestNextVsync();
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "requestNextVsync failed with status: %d", status);
+ }
+}
+
+void RenderThread::removeFrameCallback(IFrameCallback* callback) {
+ mFrameCallbacks.erase(callback);
+}
+
RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
AutoMutex _lock(mLock);
RenderTask* next = mQueue.peek();
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index e444aa0..b93dfd6 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -18,6 +18,10 @@
#define RENDERTHREAD_H_
#include "RenderTask.h"
+
+#include <memory>
+#include <set>
+
#include <cutils/compiler.h>
#include <utils/Looper.h>
#include <utils/Mutex.h>
@@ -25,9 +29,13 @@
#include <utils/Thread.h>
namespace android {
+class DisplayEventReceiver;
+
namespace uirenderer {
namespace renderthread {
+class DispatchFrameCallbacks;
+
class TaskQueue {
public:
TaskQueue();
@@ -42,6 +50,15 @@
RenderTask* mTail;
};
+// Mimics android.view.Choreographer.FrameCallback
+class IFrameCallback {
+public:
+ virtual void doFrame(nsecs_t frameTimeNanos) = 0;
+
+protected:
+ ~IFrameCallback() {}
+};
+
class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> {
public:
// RenderThread takes complete ownership of tasks that are queued
@@ -50,15 +67,25 @@
void queueDelayed(RenderTask* task, int delayMs);
void remove(RenderTask* task);
+ // Mimics android.view.Choreographer
+ void postFrameCallback(IFrameCallback* callback);
+ void removeFrameCallback(IFrameCallback* callback);
+
protected:
virtual bool threadLoop();
private:
friend class Singleton<RenderThread>;
+ friend class DispatchFrameCallbacks;
RenderThread();
virtual ~RenderThread();
+ void initializeDisplayEventReceiver();
+ static int displayEventReceiverCallback(int fd, int events, void* data);
+ void drainDisplayEventQueue();
+ void dispatchFrameCallbacks();
+
// Returns the next task to be run. If this returns NULL nextWakeup is set
// to the time to requery for the nextTask to run. mNextWakeup is also
// set to this time
@@ -69,6 +96,13 @@
nsecs_t mNextWakeup;
TaskQueue mQueue;
+
+ DisplayEventReceiver* mDisplayEventReceiver;
+ bool mVsyncRequested;
+ std::set<IFrameCallback*> mFrameCallbacks;
+ bool mFrameCallbackTaskPending;
+ DispatchFrameCallbacks* mFrameCallbackTask;
+ nsecs_t mFrameTime;
};
} /* namespace renderthread */
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 57ba8fa..7deabe9 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -29,6 +29,10 @@
inline static bool isZero(float value) {
return (value >= -gNonZeroEpsilon) && (value <= gNonZeroEpsilon);
}
+
+ inline static bool isPositive(float value) {
+ return value >= gNonZeroEpsilon;
+ }
}; // class MathUtils
} /* namespace uirenderer */
diff --git a/location/java/android/location/FusedBatchOptions.java b/location/java/android/location/FusedBatchOptions.java
index 623d707..5600aeb 100644
--- a/location/java/android/location/FusedBatchOptions.java
+++ b/location/java/android/location/FusedBatchOptions.java
@@ -95,8 +95,9 @@
}
public static final class BatchFlags {
- public static int WAKEUP_ON_FIFO_FULL = 1<<0;
- public static int CALLBACK_ON_LOCATION_FIX = 1<<1;
+ // follow the definitions to the letter in fused_location.h
+ public static int WAKEUP_ON_FIFO_FULL = 0x0000001;
+ public static int CALLBACK_ON_LOCATION_FIX =0x0000002;
}
/*
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 04c6e97..ad0d459 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -88,12 +88,22 @@
public static final int CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT;
public static final int CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT);
+ // aka QUAD_BACK
public static final int CHANNEL_OUT_QUAD = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT);
+ /** @hide */
+ public static final int CHANNEL_OUT_QUAD_SIDE = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_SIDE_LEFT | CHANNEL_OUT_SIDE_RIGHT);
public static final int CHANNEL_OUT_SURROUND = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_CENTER);
+ // aka 5POINT1_BACK
public static final int CHANNEL_OUT_5POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT);
+ /** @hide */
+ public static final int CHANNEL_OUT_5POINT1_SIDE = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY |
+ CHANNEL_OUT_SIDE_LEFT | CHANNEL_OUT_SIDE_RIGHT);
+ // TODO does this need an @deprecated ?
// different from AUDIO_CHANNEL_OUT_7POINT1
public static final int CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index 2dea509..1c73c05 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -1250,8 +1250,6 @@
}
if (prse.hasMatchingMediaButtonIntent(mediaIntent)) {
inStackIndex = index;
- // found it, ok to stop here
- break;
}
}
@@ -1272,7 +1270,11 @@
mPRStack.push(prse);
} else {
// and put it after the ones with active playback
- mPRStack.add(lastPlayingIndex, prse);
+ if (inStackIndex > lastPlayingIndex) {
+ mPRStack.add(lastPlayingIndex, prse);
+ } else {
+ mPRStack.add(lastPlayingIndex - 1, prse);
+ }
}
}
}
@@ -2151,13 +2153,13 @@
// of this RemoteControlClient (note that it may not be in the stack)
for (int index = mPRStack.size()-1; index >= 0; index--) {
prse = mPRStack.elementAt(index);
- if (prse.isPlaybackActive()) {
- lastPlayingIndex = index;
- }
if (prse.getRccId() == rccId) {
inStackIndex = index;
prse.mPlaybackState = newState;
}
+ if (prse.isPlaybackActive()) {
+ lastPlayingIndex = index;
+ }
}
if (inStackIndex != -1) {
@@ -2177,7 +2179,11 @@
mPRStack.push(prse);
} else {
// and put it after the ones with active playback
- mPRStack.add(lastPlayingIndex, prse);
+ if (inStackIndex > lastPlayingIndex) {
+ mPRStack.add(lastPlayingIndex, prse);
+ } else {
+ mPRStack.add(lastPlayingIndex - 1, prse);
+ }
}
}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 72f3e1a..9516bf8 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -44,6 +44,8 @@
import android.sax.Element;
import android.sax.ElementListener;
import android.sax.RootElement;
+import android.system.ErrnoException;
+import android.system.Os;
import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
@@ -60,9 +62,6 @@
import java.util.Iterator;
import java.util.Locale;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-
/**
* Internal service helper that no-one should use directly.
*
@@ -1130,7 +1129,7 @@
if (path != null && path.startsWith("/")) {
boolean exists = false;
try {
- exists = Libcore.os.access(path, libcore.io.OsConstants.F_OK);
+ exists = Os.access(path, android.system.OsConstants.F_OK);
} catch (ErrnoException e1) {
}
if (!exists && !MtpConstants.isAbstractObject(format)) {
@@ -1281,6 +1280,14 @@
mMediaProvider = null;
}
+ private void releaseResources() {
+ // release the DrmManagerClient resources
+ if (mDrmManagerClient != null) {
+ mDrmManagerClient.release();
+ mDrmManagerClient = null;
+ }
+ }
+
private void initialize(String volumeName) {
mMediaProvider = mContext.getContentResolver().acquireProvider("media");
@@ -1341,6 +1348,8 @@
Log.e(TAG, "UnsupportedOperationException in MediaScanner.scan()", e);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.scan()", e);
+ } finally {
+ releaseResources();
}
}
@@ -1364,6 +1373,8 @@
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
return null;
+ } finally {
+ releaseResources();
}
}
@@ -1511,6 +1522,7 @@
if (fileList != null) {
fileList.close();
}
+ releaseResources();
}
}
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 716418c..7a86811 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -274,17 +274,11 @@
fid = env->GetStaticFieldID(imageFormatClazz, "JPEG", "I");
jpegFormat = env->GetStaticIntField(imageFormatClazz, fid);
- fid = env->GetStaticFieldID(imageFormatClazz, "RAW_SENSOR", "I");
- rawSensorFormat = env->GetStaticIntField(imageFormatClazz, fid);
- // Translate the JPEG to BLOB for camera purpose, an add more if more mismatch is found.
+ // Translate the JPEG to BLOB for camera purpose.
if (format == jpegFormat) {
format = HAL_PIXEL_FORMAT_BLOB;
}
- // Same thing for RAW_SENSOR format
- if (format == rawSensorFormat) {
- format = HAL_PIXEL_FORMAT_RAW_SENSOR;
- }
return format;
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index d04b1f8..a710c03 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -255,7 +255,7 @@
env, env->FindClass("android/media/MediaCodec$BufferInfo"));
jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V");
- env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags);
+ env->CallVoidMethod(bufferInfo, method, (jint)offset, (jint)size, timeUs, flags);
return OK;
}
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 4e42ae3..157dd49 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -273,6 +273,13 @@
width,
height,
config);
+ if (jBitmap == NULL) {
+ if (env->ExceptionCheck()) {
+ env->ExceptionClear();
+ }
+ ALOGE("getFrameAtTime: create Bitmap failed!");
+ return NULL;
+ }
SkBitmap *bitmap =
(SkBitmap *) env->GetLongField(jBitmap, fields.nativeBitmap);
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 48ef9db..36c1d5c 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -41,6 +41,9 @@
import android.os.ServiceManager;
import android.os.StatFs;
import android.provider.Settings;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStatVfs;
import android.util.DisplayMetrics;
import android.util.Slog;
@@ -66,11 +69,8 @@
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
-import libcore.io.Libcore;
import libcore.io.Streams;
-import libcore.io.StructStatVfs;
/*
* This service copies a downloaded apk to a file passed in as
@@ -245,7 +245,7 @@
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
try {
- final StructStatVfs stat = Libcore.os.statvfs(path);
+ final StructStatVfs stat = Os.statvfs(path);
final long totalSize = stat.f_blocks * stat.f_bsize;
final long availSize = stat.f_bavail * stat.f_bsize;
return new long[] { totalSize, availSize };
@@ -379,7 +379,7 @@
}
try {
- Libcore.os.chmod(resFile.getAbsolutePath(), 0640);
+ Os.chmod(resFile.getAbsolutePath(), 0640);
} catch (ErrnoException e) {
Slog.e(TAG, "Could not chown APK: " + e.getMessage());
PackageHelper.destroySdDir(newCid);
@@ -401,7 +401,7 @@
}
try {
- Libcore.os.chmod(publicZipFile.getAbsolutePath(), 0644);
+ Os.chmod(publicZipFile.getAbsolutePath(), 0644);
} catch (ErrnoException e) {
Slog.e(TAG, "Could not chown public resource file: " + e.getMessage());
PackageHelper.destroySdDir(newCid);
diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml
index 65dbe18..ca76a7d 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Stoor"</string>
<string name="menu_share" msgid="3075149983979628146">"Deel"</string>
<string name="menu_delete" msgid="8138799623850614177">"Vee uit"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Kies \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> gekies"</string>
<string name="sort_name" msgid="9183560467917256779">"Volgens naam"</string>
<string name="sort_date" msgid="586080032956151448">"Volgens datum gewysig"</string>
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index 1003c0a..84879d5 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"አስቀምጥ"</string>
<string name="menu_share" msgid="3075149983979628146">"አጋራ"</string>
<string name="menu_delete" msgid="8138799623850614177">"ሰርዝ"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"«<xliff:g id="DIRECTORY">^1</xliff:g>»ን ይምረጡ"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> ተመርጠዋል"</string>
<string name="sort_name" msgid="9183560467917256779">"በስም"</string>
<string name="sort_date" msgid="586080032956151448">"በተለወጠበት ቀን"</string>
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index e939b3e..5c5d863 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"حفظ"</string>
<string name="menu_share" msgid="3075149983979628146">"مشاركة"</string>
<string name="menu_delete" msgid="8138799623850614177">"حذف"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"تحديد \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"تم تحديد <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"بحسب الاسم"</string>
<string name="sort_date" msgid="586080032956151448">"بحسب تاريخ التعديل"</string>
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index 18cdb9c..d1da879 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Запазване"</string>
<string name="menu_share" msgid="3075149983979628146">"Споделяне"</string>
<string name="menu_delete" msgid="8138799623850614177">"Изтриване"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Избиране на „<xliff:g id="DIRECTORY">^1</xliff:g>“"</string>
<string name="mode_selected_count" msgid="459111894725594625">"Избрахте <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"По име"</string>
<string name="sort_date" msgid="586080032956151448">"По дата на промяната"</string>
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index b883869..23e7284 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Desa"</string>
<string name="menu_share" msgid="3075149983979628146">"Comparteix"</string>
<string name="menu_delete" msgid="8138799623850614177">"Suprimeix"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Selecciona \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"Seleccionats: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Per nom"</string>
<string name="sort_date" msgid="586080032956151448">"Per data de modificació"</string>
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index 3827b94..ad8897a 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Uložit"</string>
<string name="menu_share" msgid="3075149983979628146">"Sdílet"</string>
<string name="menu_delete" msgid="8138799623850614177">"Smazat"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Vyberte adresář <xliff:g id="DIRECTORY">^1</xliff:g>"</string>
<string name="mode_selected_count" msgid="459111894725594625">"Vybráno: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Podle názvu"</string>
<string name="sort_date" msgid="586080032956151448">"Podle data úpravy"</string>
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index 25c74e3..7ae5d1e 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Gem"</string>
<string name="menu_share" msgid="3075149983979628146">"Del"</string>
<string name="menu_delete" msgid="8138799623850614177">"Slet"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Vælg \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> er valgt"</string>
<string name="sort_name" msgid="9183560467917256779">"Efter navn"</string>
<string name="sort_date" msgid="586080032956151448">"Efter ændringsdato"</string>
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index 93fad32..98c1787 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Speichern"</string>
<string name="menu_share" msgid="3075149983979628146">"Teilen"</string>
<string name="menu_delete" msgid="8138799623850614177">"Löschen"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" auswählen"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> ausgewählt"</string>
<string name="sort_name" msgid="9183560467917256779">"Nach Name"</string>
<string name="sort_date" msgid="586080032956151448">"Nach Änderungsdatum"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index db83aea..f0f7e10 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Αποθήκευση"</string>
<string name="menu_share" msgid="3075149983979628146">"Κοινοποίηση"</string>
<string name="menu_delete" msgid="8138799623850614177">"Διαγραφή"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Επιλογή \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"Επιλέχθηκαν <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Κατά όνομα"</string>
<string name="sort_date" msgid="586080032956151448">"Κατά ημερομηνία τροποποίησης"</string>
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index a0fe3b7..d2af473 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Save"</string>
<string name="menu_share" msgid="3075149983979628146">"Share"</string>
<string name="menu_delete" msgid="8138799623850614177">"Delete"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Select \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> selected"</string>
<string name="sort_name" msgid="9183560467917256779">"By name"</string>
<string name="sort_date" msgid="586080032956151448">"By date modified"</string>
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index a0fe3b7..d2af473 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Save"</string>
<string name="menu_share" msgid="3075149983979628146">"Share"</string>
<string name="menu_delete" msgid="8138799623850614177">"Delete"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Select \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> selected"</string>
<string name="sort_name" msgid="9183560467917256779">"By name"</string>
<string name="sort_date" msgid="586080032956151448">"By date modified"</string>
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index 3319011..daf18cf 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Guardar"</string>
<string name="menu_share" msgid="3075149983979628146">"Compartir"</string>
<string name="menu_delete" msgid="8138799623850614177">"Eliminar"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Seleccionar \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> seleccionado(s)"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nombre"</string>
<string name="sort_date" msgid="586080032956151448">"Por fecha de modificación"</string>
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 7189f1d..573ee41 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Guardar"</string>
<string name="menu_share" msgid="3075149983979628146">"Compartir"</string>
<string name="menu_delete" msgid="8138799623850614177">"Eliminar"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Selecciona \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"Seleccionado: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nombre"</string>
<string name="sort_date" msgid="586080032956151448">"Por fecha de modificación"</string>
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index a12b695..dae965a0 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Salvesta"</string>
<string name="menu_share" msgid="3075149983979628146">"Jaga"</string>
<string name="menu_delete" msgid="8138799623850614177">"Kustuta"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Kataloogi „<xliff:g id="DIRECTORY">^1</xliff:g>” valimine"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> on valitud"</string>
<string name="sort_name" msgid="9183560467917256779">"Nime järgi"</string>
<string name="sort_date" msgid="586080032956151448">"Muutmiskuupäeva järgi"</string>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index ba04522..a646eda 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"ذخیره"</string>
<string name="menu_share" msgid="3075149983979628146">"اشتراکگذاری"</string>
<string name="menu_delete" msgid="8138799623850614177">"حذف"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"انتخاب «<xliff:g id="DIRECTORY">^1</xliff:g>»"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> انتخاب شد"</string>
<string name="sort_name" msgid="9183560467917256779">"بر اساس نام"</string>
<string name="sort_date" msgid="586080032956151448">"بر اساس تاریخ اصلاح"</string>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index 8ea10be..aa118ed 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Tallenna"</string>
<string name="menu_share" msgid="3075149983979628146">"Jaa"</string>
<string name="menu_delete" msgid="8138799623850614177">"Poista"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Valitse <xliff:g id="DIRECTORY">^1</xliff:g>"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> valittua"</string>
<string name="sort_name" msgid="9183560467917256779">"Nimen mukaan"</string>
<string name="sort_date" msgid="586080032956151448">"Muokkauspäivän mukaan"</string>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index 0549707..b370a1e 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Enregistrer"</string>
<string name="menu_share" msgid="3075149983979628146">"Partager"</string>
<string name="menu_delete" msgid="8138799623850614177">"Supprimer"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Sélectionner « <xliff:g id="DIRECTORY">^1</xliff:g> »"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> sélectionné(s)"</string>
<string name="sort_name" msgid="9183560467917256779">"Par nom"</string>
<string name="sort_date" msgid="586080032956151448">"Par date de modification"</string>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 292ec86..070b130 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Enregistrer"</string>
<string name="menu_share" msgid="3075149983979628146">"Partager"</string>
<string name="menu_delete" msgid="8138799623850614177">"Supprimer"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Sélectionner \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> élément(s) sélectionné(s)"</string>
<string name="sort_name" msgid="9183560467917256779">"Par nom"</string>
<string name="sort_date" msgid="586080032956151448">"Par date de modification"</string>
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index 579bd37..66c707e 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"सहेजें"</string>
<string name="menu_share" msgid="3075149983979628146">"साझा करें"</string>
<string name="menu_delete" msgid="8138799623850614177">"हटाएं"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" चुनें"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> चयनित"</string>
<string name="sort_name" msgid="9183560467917256779">"नाम के अनुसार"</string>
<string name="sort_date" msgid="586080032956151448">"बदलाव के दिनांक के अनुसार"</string>
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 6baae95..3438e73 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Spremi"</string>
<string name="menu_share" msgid="3075149983979628146">"Dijeli"</string>
<string name="menu_delete" msgid="8138799623850614177">"Izbriši"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Odaberi \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"Odabrano: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Po korisniku"</string>
<string name="sort_date" msgid="586080032956151448">"Po datumu izmjene"</string>
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index b8ef2d4..2af559b 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Mentés"</string>
<string name="menu_share" msgid="3075149983979628146">"Megosztás"</string>
<string name="menu_delete" msgid="8138799623850614177">"Törlés"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"A(z) „<xliff:g id="DIRECTORY">^1</xliff:g>” mappa kiválasztása"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> kiválasztva"</string>
<string name="sort_name" msgid="9183560467917256779">"Név szerint"</string>
<string name="sort_date" msgid="586080032956151448">"Módosítás dátuma szerint"</string>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index 69fdfb6..67a1f7e 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Պահել"</string>
<string name="menu_share" msgid="3075149983979628146">"Համօգտագործել"</string>
<string name="menu_delete" msgid="8138799623850614177">"Ջնջել"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Ընտրել «<xliff:g id="DIRECTORY">^1</xliff:g>»"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> ընտրված"</string>
<string name="sort_name" msgid="9183560467917256779">"Ըստ անվան"</string>
<string name="sort_date" msgid="586080032956151448">"Ըստ փոփոխման ամսաթվի"</string>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index d5c69bf..62057c7 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Simpan"</string>
<string name="menu_share" msgid="3075149983979628146">"Bagikan"</string>
<string name="menu_delete" msgid="8138799623850614177">"Hapus"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Pilih \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> dipilih"</string>
<string name="sort_name" msgid="9183560467917256779">"Menurut nama"</string>
<string name="sort_date" msgid="586080032956151448">"Menurut tanggal diubah"</string>
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index 9d1fc94..bec4d00 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Salva"</string>
<string name="menu_share" msgid="3075149983979628146">"Condividi"</string>
<string name="menu_delete" msgid="8138799623850614177">"Elimina"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Seleziona \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> selezionati"</string>
<string name="sort_name" msgid="9183560467917256779">"Per nome"</string>
<string name="sort_date" msgid="586080032956151448">"Per data di modifica"</string>
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index af47c6c..c8a3fb9 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"שמור"</string>
<string name="menu_share" msgid="3075149983979628146">"שתף"</string>
<string name="menu_delete" msgid="8138799623850614177">"מחק"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"בחר ב-\"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> נבחרו"</string>
<string name="sort_name" msgid="9183560467917256779">"לפי שם"</string>
<string name="sort_date" msgid="586080032956151448">"לפי תאריך שינוי"</string>
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index ca64914..1475005 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"保存"</string>
<string name="menu_share" msgid="3075149983979628146">"共有"</string>
<string name="menu_delete" msgid="8138799623850614177">"削除"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"「<xliff:g id="DIRECTORY">^1</xliff:g>」を選択"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g>件選択済み"</string>
<string name="sort_name" msgid="9183560467917256779">"名前順"</string>
<string name="sort_date" msgid="586080032956151448">"更新日順"</string>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index 3e3d509..c90768f 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"შენახვა"</string>
<string name="menu_share" msgid="3075149983979628146">"გაზიარება"</string>
<string name="menu_delete" msgid="8138799623850614177">"წაშლა"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"„<xliff:g id="DIRECTORY">^1</xliff:g>“-ის არჩევა"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> მონიშნული"</string>
<string name="sort_name" msgid="9183560467917256779">"სახელით"</string>
<string name="sort_date" msgid="586080032956151448">"ცვლილების თარიღით"</string>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 2ecbba7..e8944ec 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"រក្សាទុក"</string>
<string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string>
<string name="menu_delete" msgid="8138799623850614177">"លុប"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"ជ្រើស \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"បានជ្រើស <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"តាមឈ្មោះ"</string>
<string name="sort_date" msgid="586080032956151448">"តាមកាលបរិច្ឆេទបានកែប្រែ"</string>
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index 0ece450..5996e66 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"저장"</string>
<string name="menu_share" msgid="3075149983979628146">"공유"</string>
<string name="menu_delete" msgid="8138799623850614177">"삭제"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"\'<xliff:g id="DIRECTORY">^1</xliff:g>\' 선택"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g>개 선택됨"</string>
<string name="sort_name" msgid="9183560467917256779">"이름순"</string>
<string name="sort_date" msgid="586080032956151448">"수정된 날짜순"</string>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index 0bc0b8b..8452ae1 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"ບັນທຶກ"</string>
<string name="menu_share" msgid="3075149983979628146">"ແບ່ງປັນ"</string>
<string name="menu_delete" msgid="8138799623850614177">"ລຶບ"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"ເລືອກ \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"ເລືອກແລ້ວ <xliff:g id="COUNT">%1$d</xliff:g> ລາຍການ"</string>
<string name="sort_name" msgid="9183560467917256779">"ຕາມຊື່"</string>
<string name="sort_date" msgid="586080032956151448">"ຕາມວັນທີທີ່ແກ້ໄຂ"</string>
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index 04825f9..8ec3e0b 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Išsaugoti"</string>
<string name="menu_share" msgid="3075149983979628146">"Bendrinti"</string>
<string name="menu_delete" msgid="8138799623850614177">"Ištrinti"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Pasirinkti katalogą „<xliff:g id="DIRECTORY">^1</xliff:g>“"</string>
<string name="mode_selected_count" msgid="459111894725594625">"Pasirinkta: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Pagal pavadinimą"</string>
<string name="sort_date" msgid="586080032956151448">"Pagal keitimo datą"</string>
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index 5157327..caaf8ec 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Saglabāt"</string>
<string name="menu_share" msgid="3075149983979628146">"Kopīgot"</string>
<string name="menu_delete" msgid="8138799623850614177">"Dzēst"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Atlasīt “<xliff:g id="DIRECTORY">^1</xliff:g>”"</string>
<string name="mode_selected_count" msgid="459111894725594625">"Atlasīts: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Pēc nosaukuma"</string>
<string name="sort_date" msgid="586080032956151448">"Pēc pārveidošanas datuma"</string>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index 8fd8f52..3d90cc1 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Хадгалах"</string>
<string name="menu_share" msgid="3075149983979628146">"Хуваалцах"</string>
<string name="menu_delete" msgid="8138799623850614177">"Устгах"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\"-г сонгох"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> сонгогдсон"</string>
<string name="sort_name" msgid="9183560467917256779">"Нэрээр"</string>
<string name="sort_date" msgid="586080032956151448">"Өөрчлөгдсөн огноогоор"</string>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index 48e30305..9ea7119 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Simpan"</string>
<string name="menu_share" msgid="3075149983979628146">"Kongsi"</string>
<string name="menu_delete" msgid="8138799623850614177">"Padam"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Pilih \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> dipilih"</string>
<string name="sort_name" msgid="9183560467917256779">"Mengikut nama"</string>
<string name="sort_date" msgid="586080032956151448">"Diubah suai mengikut tarikh"</string>
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index 09a11d5..492fde7 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Lagre"</string>
<string name="menu_share" msgid="3075149983979628146">"Del"</string>
<string name="menu_delete" msgid="8138799623850614177">"Slett"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Velg <xliff:g id="DIRECTORY">^1</xliff:g>"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> valgt"</string>
<string name="sort_name" msgid="9183560467917256779">"Etter navn"</string>
<string name="sort_date" msgid="586080032956151448">"«Etter dato» endret"</string>
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index ca626a86..a8cf114 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Opslaan"</string>
<string name="menu_share" msgid="3075149983979628146">"Delen"</string>
<string name="menu_delete" msgid="8138799623850614177">"Verwijderen"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"<xliff:g id="DIRECTORY">^1</xliff:g> selecteren"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> geselecteerd"</string>
<string name="sort_name" msgid="9183560467917256779">"Op naam"</string>
<string name="sort_date" msgid="586080032956151448">"Op aanpassingsdatum"</string>
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index 7a7978b..ead40e9 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Zapisz"</string>
<string name="menu_share" msgid="3075149983979628146">"Udostępnij"</string>
<string name="menu_delete" msgid="8138799623850614177">"Usuń"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Zaznacz „<xliff:g id="DIRECTORY">^1</xliff:g>”"</string>
<string name="mode_selected_count" msgid="459111894725594625">"Wybrano: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Według nazwy"</string>
<string name="sort_date" msgid="586080032956151448">"Według daty edycji"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index 0443c35..0003c05 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Guardar"</string>
<string name="menu_share" msgid="3075149983979628146">"Partilhar"</string>
<string name="menu_delete" msgid="8138799623850614177">"Eliminar"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Selecionar \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> selecionado(s)"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
<string name="sort_date" msgid="586080032956151448">"Por data de modificação"</string>
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index f9038d1..4a5c72a 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Salvar"</string>
<string name="menu_share" msgid="3075149983979628146">"Compartilhar"</string>
<string name="menu_delete" msgid="8138799623850614177">"Excluir"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Selecionar \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> selecionados"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
<string name="sort_date" msgid="586080032956151448">"Por data de modificação"</string>
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index 26cd77f..0dfa11d4 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Salvați"</string>
<string name="menu_share" msgid="3075149983979628146">"Distribuiți"</string>
<string name="menu_delete" msgid="8138799623850614177">"Ștergeți"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Selectați „<xliff:g id="DIRECTORY">^1</xliff:g>”"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> selectate"</string>
<string name="sort_name" msgid="9183560467917256779">"După nume"</string>
<string name="sort_date" msgid="586080032956151448">"După data modificării"</string>
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index f8e1f1b..f86a4af 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Сохранить"</string>
<string name="menu_share" msgid="3075149983979628146">"Поделиться"</string>
<string name="menu_delete" msgid="8138799623850614177">"Удалить"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Выбрать папку \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"Выбрано: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"По названию"</string>
<string name="sort_date" msgid="586080032956151448">"По дате изменения"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index cb4b718..5d03df6 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Uložiť"</string>
<string name="menu_share" msgid="3075149983979628146">"Zdieľať"</string>
<string name="menu_delete" msgid="8138799623850614177">"Odstrániť"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Vyberte adresár <xliff:g id="DIRECTORY">^1</xliff:g>"</string>
<string name="mode_selected_count" msgid="459111894725594625">"Vybraté: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Podľa názvu"</string>
<string name="sort_date" msgid="586080032956151448">"Podľa dátumu zmeny"</string>
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index a6dc20b..b3e52dd43 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Shrani"</string>
<string name="menu_share" msgid="3075149983979628146">"Skupna raba"</string>
<string name="menu_delete" msgid="8138799623850614177">"Izbriši"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Izbira mape »<xliff:g id="DIRECTORY">^1</xliff:g>«"</string>
<string name="mode_selected_count" msgid="459111894725594625">"Št. izbranih: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Po imenu"</string>
<string name="sort_date" msgid="586080032956151448">"Po datumu spremembe"</string>
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index 06e0d08..892dbce 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Сачувај"</string>
<string name="menu_share" msgid="3075149983979628146">"Дели"</string>
<string name="menu_delete" msgid="8138799623850614177">"Избриши"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Изабери „<xliff:g id="DIRECTORY">^1</xliff:g>“"</string>
<string name="mode_selected_count" msgid="459111894725594625">"Изабрано је <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Према имену"</string>
<string name="sort_date" msgid="586080032956151448">"Према датуму измене"</string>
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index da8432e..fd6457d 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Spara"</string>
<string name="menu_share" msgid="3075149983979628146">"Dela"</string>
<string name="menu_delete" msgid="8138799623850614177">"Ta bort"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Välj <xliff:g id="DIRECTORY">^1</xliff:g>"</string>
<string name="mode_selected_count" msgid="459111894725594625">"Har valt <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Efter namn"</string>
<string name="sort_date" msgid="586080032956151448">"Efter ändringsdatum"</string>
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index c769a59..0948c71 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Hifadhi"</string>
<string name="menu_share" msgid="3075149983979628146">"Shiriki"</string>
<string name="menu_delete" msgid="8138799623850614177">"Futa"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Chagua \" <xliff:g id="DIRECTORY">^1</xliff:g> \""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> zimechaguliwa"</string>
<string name="sort_name" msgid="9183560467917256779">"Kwa jina"</string>
<string name="sort_date" msgid="586080032956151448">"Kwa tarehe viliporekebishwa"</string>
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index bf92893..4bf3e4f 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"บันทึก"</string>
<string name="menu_share" msgid="3075149983979628146">"แชร์"</string>
<string name="menu_delete" msgid="8138799623850614177">"ลบ"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"เลือก \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"เลือกไว้ <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"ตามชื่อ"</string>
<string name="sort_date" msgid="586080032956151448">"ตามวันที่ที่ปรับเปลี่ยน"</string>
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index bc363e7..8ef8aa5 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"I-save"</string>
<string name="menu_share" msgid="3075149983979628146">"Ibahagi"</string>
<string name="menu_delete" msgid="8138799623850614177">"Tanggalin"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Piliin ang \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> ang pinili"</string>
<string name="sort_name" msgid="9183560467917256779">"Ayon sa pangalan"</string>
<string name="sort_date" msgid="586080032956151448">"Ayon sa petsa ng pagbago"</string>
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index 5d7a2be..93586d0 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Kaydet"</string>
<string name="menu_share" msgid="3075149983979628146">"Paylaş"</string>
<string name="menu_delete" msgid="8138799623850614177">"Sil"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" dizinini seç"</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> tane seçildi"</string>
<string name="sort_name" msgid="9183560467917256779">"Ada göre"</string>
<string name="sort_date" msgid="586080032956151448">"Değişiklik tarihine göre"</string>
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index dbd44b7..8f8865b 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Зберегти"</string>
<string name="menu_share" msgid="3075149983979628146">"Поділитися"</string>
<string name="menu_delete" msgid="8138799623850614177">"Видалити"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Вибрати каталог \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"Вибрано <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"За назвою"</string>
<string name="sort_date" msgid="586080032956151448">"За датою змінення"</string>
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index 919708f..8b8ff1f 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Lưu"</string>
<string name="menu_share" msgid="3075149983979628146">"Chia sẻ"</string>
<string name="menu_delete" msgid="8138799623850614177">"Xóa"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Chọn \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"Đã chọn <xliff:g id="COUNT">%1$d</xliff:g>"</string>
<string name="sort_name" msgid="9183560467917256779">"Theo tên"</string>
<string name="sort_date" msgid="586080032956151448">"Theo ngày sửa đổi"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index a916b87..68ab5f8 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"保存"</string>
<string name="menu_share" msgid="3075149983979628146">"分享"</string>
<string name="menu_delete" msgid="8138799623850614177">"删除"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"选择“<xliff:g id="DIRECTORY">^1</xliff:g>”"</string>
<string name="mode_selected_count" msgid="459111894725594625">"已选择<xliff:g id="COUNT">%1$d</xliff:g>项"</string>
<string name="sort_name" msgid="9183560467917256779">"按名称"</string>
<string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index 7cae910..afd8b63 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"儲存"</string>
<string name="menu_share" msgid="3075149983979628146">"分享"</string>
<string name="menu_delete" msgid="8138799623850614177">"刪除"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"選取「<xliff:g id="DIRECTORY">^1</xliff:g>」"</string>
<string name="mode_selected_count" msgid="459111894725594625">"已選取 <xliff:g id="COUNT">%1$d</xliff:g> 個"</string>
<string name="sort_name" msgid="9183560467917256779">"按名稱"</string>
<string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index 387fdba..2e77f21 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"儲存"</string>
<string name="menu_share" msgid="3075149983979628146">"共用"</string>
<string name="menu_delete" msgid="8138799623850614177">"刪除"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"選取「<xliff:g id="DIRECTORY">^1</xliff:g>」"</string>
<string name="mode_selected_count" msgid="459111894725594625">"已選取 <xliff:g id="COUNT">%1$d</xliff:g> 個項目"</string>
<string name="sort_name" msgid="9183560467917256779">"依名稱"</string>
<string name="sort_date" msgid="586080032956151448">"依修改日期"</string>
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index 564b2d7..55e2c75 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -29,8 +29,7 @@
<string name="menu_save" msgid="2394743337684426338">"Londoloza"</string>
<string name="menu_share" msgid="3075149983979628146">"Yabelana"</string>
<string name="menu_delete" msgid="8138799623850614177">"Susa"</string>
- <!-- no translation found for menu_select (8711270657353563424) -->
- <skip />
+ <string name="menu_select" msgid="8711270657353563424">"Khetha i-\"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"<xliff:g id="COUNT">%1$d</xliff:g> okukhethiwe"</string>
<string name="sort_name" msgid="9183560467917256779">"Ngegama"</string>
<string name="sort_date" msgid="586080032956151448">"Ngedethi yokuguqula"</string>
diff --git a/packages/InputDevices/res/raw/keyboard_layout_czech.kcm b/packages/InputDevices/res/raw/keyboard_layout_czech.kcm
index f710a8e..dc614db 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_czech.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_czech.kcm
@@ -13,7 +13,7 @@
# limitations under the License.
#
-# Czech keyboard layout.
+# Czech (EU - qwerty) keyboard layout.
#
type OVERLAY
@@ -26,6 +26,8 @@
label: ';'
base: ';'
shift: '\u00b0'
+ ralt: '\u0060'
+ shift+ralt: '\u007e'
}
key 1 {
@@ -38,6 +40,7 @@
key 2 {
label: '2'
base: '\u011b'
+ capslock: '\u011a'
shift: '2'
ralt: '@'
}
@@ -45,6 +48,7 @@
key 3 {
label: '3'
base: '\u0161'
+ capslock: '\u0160'
shift: '3'
ralt: '#'
}
@@ -52,6 +56,7 @@
key 4 {
label: '4'
base: '\u010d'
+ capslock: '\u010c'
shift: '4'
ralt: '$'
}
@@ -59,6 +64,7 @@
key 5 {
label: '5'
base: '\u0159'
+ capslock: '\u0158'
shift: '5'
ralt: '%'
}
@@ -66,6 +72,7 @@
key 6 {
label: '6'
base: '\u017e'
+ capslock: '\u017d'
shift: '6'
ralt: '^'
}
@@ -73,6 +80,7 @@
key 7 {
label: '7'
base: '\u00fd'
+ capslock: '\u00dd'
shift: '7'
ralt: '&'
}
@@ -80,6 +88,7 @@
key 8 {
label: '8'
base: '\u00e1'
+ capslock: '\u00c1'
shift: '8'
ralt: '*'
}
@@ -87,6 +96,7 @@
key 9 {
label: '9'
base: '\u00ed'
+ capslock: '\u00cd'
shift: '9'
ralt: '('
}
@@ -94,6 +104,7 @@
key 0 {
label: '0'
base: '\u00e9'
+ capslock: '\u00c9'
shift: '0'
ralt: ')'
}
@@ -180,6 +191,7 @@
key LEFT_BRACKET {
label: '\u00fa'
base: '\u00fa'
+ capslock: '\u00da'
shift: '/'
ralt: '['
ralt+shift: '{'
@@ -252,6 +264,7 @@
key SEMICOLON {
label: '\u016f'
base: '\u016f'
+ capslock: '\u016e'
shift: '"'
ralt: ';'
ralt+shift: ':'
@@ -261,8 +274,8 @@
label: '\u00a7'
base: '\u00a7'
shift: '!'
- ralt: '\''
- ralt+shift: '"'
+ ralt: '\u00a4'
+ ralt+shift: '\u005e'
}
key BACKSLASH {
@@ -279,6 +292,8 @@
label: '\\'
base: '\\'
shift: '|'
+ ralt: '\u00df'
+ shift+ralt: '\u02dd'
}
key Z {
@@ -330,6 +345,7 @@
base: ','
shift: '?'
ralt: '<'
+ shift+ralt: '\u00d7'
}
key PERIOD {
@@ -337,6 +353,7 @@
base: '.'
shift: ':'
ralt: '>'
+ shift+ralt: '\u00f7'
}
key SLASH {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_english_us_intl.kcm b/packages/InputDevices/res/raw/keyboard_layout_english_us_intl.kcm
index 0fabf02..66c1c98 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_english_us_intl.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_english_us_intl.kcm
@@ -122,7 +122,7 @@
base: 'q'
shift, capslock: 'Q'
ralt: '\u00e4'
- shift+ralt: '\u00c4'
+ shift+ralt, capslock+ralt: '\u00c4'
}
key W {
@@ -130,7 +130,7 @@
base: 'w'
shift, capslock: 'W'
ralt: '\u00e5'
- shift+ralt: '\u00c5'
+ shift+ralt, capslock+ralt: '\u00c5'
}
key E {
@@ -138,7 +138,7 @@
base: 'e'
shift, capslock: 'E'
ralt: '\u00e9'
- shift+ralt: '\u00c9'
+ shift+ralt, capslock+ralt: '\u00c9'
}
key R {
@@ -153,7 +153,7 @@
base: 't'
shift, capslock: 'T'
ralt: '\u00fe'
- shift+ralt: '\u00de'
+ shift+ralt, capslock+ralt: '\u00de'
}
key Y {
@@ -161,7 +161,7 @@
base: 'y'
shift, capslock: 'Y'
ralt: '\u00fc'
- shift+ralt: '\u00dc'
+ shift+ralt, capslock+ralt: '\u00dc'
}
key U {
@@ -169,7 +169,7 @@
base: 'u'
shift, capslock: 'U'
ralt: '\u00fa'
- shift+ralt: '\u00da'
+ shift+ralt, capslock+ralt: '\u00da'
}
key I {
@@ -177,7 +177,7 @@
base: 'i'
shift, capslock: 'I'
ralt: '\u00ed'
- shift+ralt: '\u00cd'
+ shift+ralt, capslock+ralt: '\u00cd'
}
key O {
@@ -185,7 +185,7 @@
base: 'o'
shift, capslock: 'O'
ralt: '\u00f3'
- shift+ralt: '\u00d3'
+ shift+ralt, capslock+ralt: '\u00d3'
}
key P {
@@ -193,7 +193,7 @@
base: 'p'
shift, capslock: 'P'
ralt: '\u00f6'
- shift+ralt: '\u00d6'
+ shift+ralt, capslock+ralt: '\u00d6'
}
key LEFT_BRACKET {
@@ -225,7 +225,7 @@
base: 'a'
shift, capslock: 'A'
ralt: '\u00e1'
- shift+ralt: '\u00c1'
+ shift+ralt, ralt+capslock: '\u00c1'
}
key S {
@@ -241,7 +241,7 @@
base: 'd'
shift, capslock: 'D'
ralt: '\u00f0'
- shift+ralt: '\u00d0'
+ shift+ralt, capslock+ralt: '\u00d0'
}
key F {
@@ -279,7 +279,7 @@
base: 'l'
shift, capslock: 'L'
ralt: '\u00f8'
- shift+ralt: '\u00d8'
+ shift+ralt, capslock+ralt: '\u00d8'
}
key SEMICOLON {
@@ -313,7 +313,7 @@
base: 'z'
shift, capslock: 'Z'
ralt: '\u00e6'
- shift+ralt: '\u00c6'
+ shift+ralt, capslock+ralt: '\u00c6'
}
key X {
@@ -347,7 +347,7 @@
base: 'n'
shift, capslock: 'N'
ralt: '\u00f1'
- shift+ralt: '\u00d1'
+ shift+ralt, capslock+ralt: '\u00d1'
}
key M {
@@ -362,7 +362,7 @@
base: ','
shift: '<'
ralt: '\u00e7'
- shift+ralt: '\u00c7'
+ shift+ralt, capslock+ralt: '\u00c7'
}
key PERIOD {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_slovak.kcm b/packages/InputDevices/res/raw/keyboard_layout_slovak.kcm
index 70c1fa4..2eb0f63 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_slovak.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_slovak.kcm
@@ -13,7 +13,7 @@
# limitations under the License.
#
-# Slovak keyboard layout.
+# Slovak (EU - qwerty) keyboard layout.
#
type OVERLAY
@@ -26,94 +26,90 @@
label: ';'
base: ';'
shift: '\u00b0'
- ralt: '`'
- ralt+shift: '~'
}
key 1 {
label: '1'
base: '+'
shift: '1'
- ralt: '!'
+ ralt: '~'
}
key 2 {
label: '2'
base: '\u013e'
shift: '2'
- ralt: '@'
+ ralt: '\u02c7'
}
key 3 {
label: '3'
base: '\u0161'
shift: '3'
- ralt: '#'
+ ralt: '\u0302'
}
key 4 {
label: '4'
base: '\u010d'
shift: '4'
- ralt: '$'
+ ralt: '\u02d8'
}
key 5 {
label: '5'
base: '\u0165'
shift: '5'
- ralt: '%'
+ ralt: '\u00b0'
}
key 6 {
label: '6'
base: '\u017e'
shift: '6'
- ralt: '^'
+ ralt: '\u02db'
}
key 7 {
label: '7'
base: '\u00fd'
shift: '7'
- ralt: '&'
+ ralt: '\u0300'
}
key 8 {
label: '8'
base: '\u00e1'
shift: '8'
- ralt: '*'
+ ralt: '\u02d9'
}
key 9 {
label: '9'
base: '\u00ed'
shift: '9'
- ralt: '('
+ ralt: '\u0301'
}
key 0 {
label: '0'
base: '\u00e9'
shift: '0'
- ralt: ')'
+ ralt: '\u02dd'
}
key MINUS {
label: '='
base: '='
shift: '%'
- ralt: '-'
- ralt+shift: '_'
+ ralt: '\u0308'
}
key EQUALS {
label: '\u00b4'
base: '\u0301'
shift: '\u030c'
- ralt: '='
- ralt+shift: '+'
+ ralt: '\u00b8'
}
### ROW 2
@@ -179,22 +175,21 @@
label: 'P'
base: 'p'
shift, capslock: 'P'
+ ralt: '\''
}
key LEFT_BRACKET {
label: '\u00fa'
base: '\u00fa'
shift: '/'
- ralt: '['
- ralt+shift: '{'
+ ralt: '\u00f7'
}
key RIGHT_BRACKET {
label: '\u00e4'
base: '\u00e4'
shift: '('
- ralt: ']'
- ralt+shift: '}'
+ ralt: '\u00d7'
}
### ROW 3
@@ -209,24 +204,28 @@
label: 'S'
base: 's'
shift, capslock: 'S'
+ ralt: '\u0111'
}
key D {
label: 'D'
base: 'd'
shift, capslock: 'D'
+ ralt: '\u0110'
}
key F {
label: 'F'
base: 'f'
shift, capslock: 'F'
+ ralt: '['
}
key G {
label: 'G'
base: 'g'
shift, capslock: 'G'
+ ralt: ']'
}
key H {
@@ -245,64 +244,65 @@
label: 'K'
base: 'k'
shift, capslock: 'K'
+ ralt: '\u0142'
}
key L {
label: 'L'
base: 'l'
shift, capslock: 'L'
+ ralt: '\u0141'
}
key SEMICOLON {
label: '\u00f4'
base: '\u00f4'
shift: '"'
- ralt: ';'
- ralt+shift: ':'
+ ralt: '$'
}
key APOSTROPHE {
label: '\u00a7'
base: '\u00a7'
shift: '!'
- ralt: '\''
- ralt+shift: '"'
+ ralt: '\u00df'
}
key BACKSLASH {
label: '\u0148'
base: '\u0148'
shift: ')'
- ralt: '\\'
- ralt+shift: '|'
+ ralt: '\u00a4'
}
### ROW 4
key PLUS {
- label: '\\'
- base: '\\'
- shift: '|'
- ralt: '&'
- ralt+shift: '*'
+ label: '&'
+ base: '&'
+ shift: '*'
+ ralt: '<'
}
key Z {
label: 'Z'
base: 'z'
shift, capslock: 'Z'
+ ralt: '>'
}
key X {
label: 'X'
base: 'x'
shift, capslock: 'X'
+ ralt: '#'
}
key C {
label: 'C'
base: 'c'
shift, capslock: 'C'
+ ralt: '&'
}
key V {
@@ -316,12 +316,14 @@
label: 'B'
base: 'b'
shift, capslock: 'B'
+ ralt: '{'
}
key N {
label: 'N'
base: 'n'
shift, capslock: 'N'
+ ralt: '}'
}
key M {
@@ -348,6 +350,5 @@
label: '-'
base: '-'
shift: '_'
- ralt: '/'
- ralt+shift: '?'
+ ralt: '*'
}
diff --git a/packages/InputDevices/res/raw/keyboard_layout_swiss_french.kcm b/packages/InputDevices/res/raw/keyboard_layout_swiss_french.kcm
index a75d154..9e20462 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_swiss_french.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_swiss_french.kcm
@@ -56,12 +56,14 @@
label: '4'
base: '4'
shift: '\u00e7'
+ ralt: '\u00b0'
}
key 5 {
label: '5'
base: '5'
shift: '%'
+ ralt: '\u00a7'
}
key 6 {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_swiss_german.kcm b/packages/InputDevices/res/raw/keyboard_layout_swiss_german.kcm
index ae93f4b..7fbd1a9 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_swiss_german.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_swiss_german.kcm
@@ -56,12 +56,14 @@
label: '4'
base: '4'
shift: '\u00e7'
+ ralt: '\u00b0'
}
key 5 {
label: '5'
base: '5'
shift: '%'
+ ralt: '\u00a7'
}
key 6 {
@@ -178,6 +180,8 @@
label: '\u00fc'
base: '\u00fc'
shift: '\u00e8'
+ capslock: '\u00dc'
+ capslock+shift: '\u00c8'
ralt: '['
}
@@ -248,12 +252,16 @@
label: '\u00f6'
base: '\u00f6'
shift: '\u00e9'
+ capslock: '\u00d6'
+ capslock+shift: '\u00c9'
}
key APOSTROPHE {
label: '\u00e4'
base: '\u00e4'
shift: '\u00e0'
+ capslock: '\u00c4'
+ capslock+shift: '\u00c0'
ralt: '{'
}
diff --git a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
index b4847f0..b2d0219 100644
--- a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
@@ -20,6 +20,7 @@
<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
<com.android.keyguard.EmergencyCarrierArea
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -35,7 +36,8 @@
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary" />
+ android:textColor="?android:attr/textColorSecondary"
+ androidprv:allCaps="@bool/kg_use_all_caps" />
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index 3b3a069..546ddd4 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -24,7 +24,7 @@
android:id="@+id/keyguard_status_view"
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
androidprv:layout_maxHeight="@dimen/keyguard_security_height"
android:gravity="center_horizontal|top"
diff --git a/packages/Keyguard/res/values-sw600dp/dimens.xml b/packages/Keyguard/res/values-sw600dp/dimens.xml
index ea5ef27..25e86e1 100644
--- a/packages/Keyguard/res/values-sw600dp/dimens.xml
+++ b/packages/Keyguard/res/values-sw600dp/dimens.xml
@@ -32,7 +32,7 @@
<!-- Keyguard dimensions -->
<!-- Size of the clock font in keyguard's status view -->
- <dimen name="kg_status_clock_font_size">141dp</dimen>
+ <dimen name="kg_status_clock_font_size">120dp</dimen>
<!-- Size of the generic status lines keyguard's status view -->
<dimen name="kg_status_line_font_size">16sp</dimen>
diff --git a/packages/Keyguard/res/values-sw720dp/dimens.xml b/packages/Keyguard/res/values-sw720dp/dimens.xml
index 4853a7b..30b979c 100644
--- a/packages/Keyguard/res/values-sw720dp/dimens.xml
+++ b/packages/Keyguard/res/values-sw720dp/dimens.xml
@@ -19,7 +19,7 @@
<resources>
<!-- Keyguard dimensions -->
<!-- Size of the clock font in keyguard's status view -->
- <dimen name="kg_status_clock_font_size">188dp</dimen>
+ <dimen name="kg_status_clock_font_size">140dp</dimen>
<!-- Size of the generic status lines keyguard's status view -->
<dimen name="kg_status_line_font_size">19sp</dimen>
diff --git a/packages/Keyguard/res/values/attrs.xml b/packages/Keyguard/res/values/attrs.xml
index e045dd2..2410b6a 100644
--- a/packages/Keyguard/res/values/attrs.xml
+++ b/packages/Keyguard/res/values/attrs.xml
@@ -133,4 +133,8 @@
<attr name="digit" format="integer" />
<attr name="textView" format="reference" />
</declare-styleable>
+
+ <declare-styleable name="CarrierText">
+ <attr name="allCaps" format="boolean" />
+ </declare-styleable>
</resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
index 88558cd..05f2962 100644
--- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java
+++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import android.content.Context;
+import android.content.res.TypedArray;
import android.text.method.SingleLineTransformationMethod;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -81,7 +82,14 @@
public CarrierText(Context context, AttributeSet attrs) {
super(context, attrs);
mLockPatternUtils = new LockPatternUtils(mContext);
- boolean useAllCaps = mContext.getResources().getBoolean(R.bool.kg_use_all_caps);
+ boolean useAllCaps;
+ TypedArray a = context.getTheme().obtainStyledAttributes(
+ attrs, R.styleable.CarrierText, 0, 0);
+ try {
+ useAllCaps = a.getBoolean(R.styleable.CarrierText_allCaps, false);
+ } finally {
+ a.recycle();
+ }
setTransformationMethod(new CarrierTextTransformationMethod(mContext, useAllCaps));
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 73c2840..ba67a82 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -215,8 +215,29 @@
mUserHasTrust.put(userId, enabled);
}
+ private boolean isTrustDisabled(int userId) {
+ final DevicePolicyManager dpm =
+ (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ if (dpm != null) {
+ // TODO once UI is finalized
+ final boolean disabledByGlobalActions = false;
+ final boolean disabledBySettings = false;
+
+ // Don't allow trust agent if device is secured with a SIM PIN. This is here
+ // mainly because there's no other way to prompt the user to enter their SIM PIN
+ // once they get past the keyguard screen.
+ final boolean disabledBySimPin = isSimPinSecure();
+
+ final boolean disabledByDpm = (dpm.getKeyguardDisabledFeatures(null, userId)
+ & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
+ return disabledByDpm || disabledByGlobalActions || disabledBySettings
+ || disabledBySimPin;
+ }
+ return false;
+ }
+
public boolean getUserHasTrust(int userId) {
- return mUserHasTrust.get(userId);
+ return !isTrustDisabled(userId) && mUserHasTrust.get(userId);
}
static class DisplayClientState {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e1c17cb..56f5a3a 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -88,7 +88,7 @@
<uses-permission android:name="android.permission.BLUETOOTH_STACK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN" />
- <uses-permission android:name="android.permission.RENDER_STATS" />
+ <uses-permission android:name="android.permission.FRAME_STATS" />
<application android:label="@string/app_label">
<provider
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 69fbc1b..761ad42 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -45,51 +45,54 @@
android:layout_marginTop="@dimen/status_bar_height"
android:visibility="gone" />
- <LinearLayout
+ <com.android.keyguard.CarrierText
+ android:id="@+id/keyguard_carrier_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:layout_marginLeft="8dp"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <include layout="@layout/status_bar_expanded_header"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_panel_header_height"
+ />
+
+ <include
+ layout="@layout/keyguard_status_view"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/emergency_calls_only"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network.EmergencyOnly"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:padding="4dp"
+ android:gravity="center"
+ android:visibility="gone"
+ />
+
+ <FrameLayout
+ android:id="@+id/notification_container_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/close_handle_underlap"
- android:orientation="vertical"
- android:animateLayoutChanges="false"
>
-
- <include layout="@layout/status_bar_expanded_header"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_panel_header_height"
- />
-
<include
- layout="@layout/keyguard_status_view"
- android:visibility="gone" />
-
- <TextView
- android:id="@+id/emergency_calls_only"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network.EmergencyOnly"
- android:layout_height="wrap_content"
+ layout="@layout/flip_settings"
+ android:layout_marginTop="@dimen/notification_panel_header_height"
android:layout_width="match_parent"
- android:padding="4dp"
- android:gravity="center"
- android:visibility="gone"
+ android:layout_height="wrap_content"
/>
- <FrameLayout
- android:id="@+id/notification_container_parent"
+ <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
+ android:id="@+id/notification_stack_scroller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- >
- <include
- layout="@layout/flip_settings"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
-
- <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
- android:id="@+id/notification_stack_scroller"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
- </FrameLayout>
- </LinearLayout>
+ />
+ </FrameLayout>
<include
layout="@layout/keyguard_bottom_area"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index 5d2f330..2e08bff 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -18,35 +18,27 @@
<com.android.systemui.statusbar.NotificationOverflowContainer
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="32dp"
+ android:focusable="true"
+ android:clickable="true"
>
- <com.android.systemui.statusbar.LatestItemView
- android:id="@+id/container"
+ <TextView
+ android:id="@+id/more_text"
android:layout_width="match_parent"
- android:layout_height="40dp"
- android:layout_marginTop="@dimen/notification_divider_height"
- android:focusable="true"
- android:clickable="true"
- android:background="@*android:drawable/notification_quantum_bg_dim"
- >
- <TextView
- android:id="@+id/more_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:gravity="center_horizontal"
- android:textColor="@color/keyguard_overflow_content_color"
- android:textAllCaps="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- />
- <com.android.systemui.statusbar.NotificationOverflowIconsView
- android:id="@+id/overflow_icons_view"
- android:layout_gravity="end|center_vertical"
- android:gravity="end"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:layout_width="120dp"
- android:layout_height="wrap_content"
- />
- </com.android.systemui.statusbar.LatestItemView>
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:gravity="center_horizontal"
+ android:textColor="@color/keyguard_overflow_content_color"
+ android:textAllCaps="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+ <com.android.systemui.statusbar.NotificationOverflowIconsView
+ android:id="@+id/overflow_icons_view"
+ android:layout_gravity="end|center_vertical"
+ android:gravity="end"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:layout_width="120dp"
+ android:layout_height="wrap_content"
+ />
</com.android.systemui.statusbar.NotificationOverflowContainer>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index d61d8b9..8959a5b 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -2,17 +2,17 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:focusable="true"
+ android:clickable="true"
>
- <View
- android:id="@+id/top_glow"
- android:alpha="0"
- android:visibility="invisible"
+ <com.android.systemui.statusbar.NotificationContentView android:id="@+id/expanded"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <com.android.systemui.statusbar.NotificationContentView android:id="@+id/expandedPublic"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_divider_height"
- android:layout_gravity="top|center_horizontal"
- android:background="@drawable/top_divider_glow"
- />
+ android:layout_height="wrap_content" />
<Button
android:id="@+id/veto"
@@ -25,35 +25,6 @@
android:paddingStart="8dp"
/>
- <com.android.systemui.statusbar.LatestItemView android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_divider_height"
- android:layout_marginTop="@dimen/notification_divider_height"
- android:focusable="true"
- android:clickable="true"
- android:background="@*android:drawable/notification_quantum_bg"
- >
-
- <com.android.internal.widget.SizeAdaptiveLayout android:id="@+id/expanded"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <com.android.internal.widget.SizeAdaptiveLayout android:id="@+id/expandedPublic"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </com.android.systemui.statusbar.LatestItemView>
-
- <View
- android:id="@+id/bottom_glow"
- android:alpha="0"
- android:visibility="invisible"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_divider_height"
- android:layout_gravity="bottom|center_horizontal"
- android:background="@drawable/bottom_divider_glow"
- />
-
<TextView
android:id="@+id/debug_info"
android:visibility="invisible"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 285978b..d8a3114 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d meer"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Tik weer om oop te maak"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Sleep op om te ontsluit"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 430c209..9d1e036 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"%d ማሳወቂያዎች ተደብቀዋል"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"ለማሳየት ነካ ያድርጉ"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"አይረብሹ"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d ተጨማሪ"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"ለመክፈት ዳግም መታ ያድርጉ"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"ለማስከፈት ወደ ላይ ያንሸራትቱ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 3186791..22a032a 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d أخرى"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"انقر مرة أخرى للفتح"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"مرر سريعًا لأعلى لإلغاء القفل"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index b79429c..00ac707 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"%d известия са скрити"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Докоснете за показване"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Не ме безпокойте"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Още %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Докоснете отново, за да отворите"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Прекарайте пръст нагоре, за да отключите"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 1841622..bfad5bc 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -222,4 +222,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d més"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Torna a tocar per obrir-la."</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Fes lliscar el dit cap amunt per desbloquejar el teclat."</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 1c2e122..94ba5c7 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -218,9 +218,10 @@
<item quantity="other" msgid="7388721375827338153">"Skrytá oznámení: %d"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Oznámení zobrazíte kliknutím"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Nerušit"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Další: %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Oznámení otevřete opětovným klepnutím"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Zařízení odemknete přejetím prstem nahoru"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 62ab0c3..df442ab 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d mere"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Tryk igen for at åbne"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Stryg for at låse op"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a73e6d7..3c4e0d3 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -222,4 +222,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d mehr"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Zum Öffnen erneut tippen"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Zum Entsperren nach oben wischen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index caf64f5..6dcffcf 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -222,4 +222,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d ακόμη"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Πατήστε ξανά για να ανοίξετε"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Σύρετε για να ξεκλειδώσετε"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 7eff6ee..91a334a 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"%d notifications hidden"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Touch to show"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Do not disturb"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d more"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Swipe up to unlock"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 7eff6ee..91a334a 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"%d notifications hidden"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Touch to show"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Do not disturb"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d more"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Swipe up to unlock"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index d2a1125..2150cbe 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -222,4 +222,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d más"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Presionar de nuevo para abrir"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Deslizar el dedo hacia arriba para desbloquear"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 822db03..3424166 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d más"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Toca de nuevo para abrir"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Desliza el dedo hacia arriba para desbloquear"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 027de0c..8dbf9af 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -214,9 +214,10 @@
<!-- String.format failed for translation -->
<!-- no translation found for zen_mode_notification_title:other (7388721375827338153) -->
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Puudutage kuvamiseks"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Mitte segada"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Veel %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Avamiseks puudutage uuesti"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Lukustuse tühistamiseks pühkige üles"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 53353ae..b2ac990 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"%d اعلان پنهان شده"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"برای نمایش لمس کنید"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"مزاحم نشوید"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d بیشتر"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"برای باز کردن دوباره ضربه بزنید"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"برای باز کردن قفل سریع به بالا بکشید"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index dad857c..af0df4d 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -206,8 +206,7 @@
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Värinkorjaustila"</string>
<string name="recents_empty_message" msgid="2269156590813544104">"VIIMEISIMMÄT"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Sovellustiedot"</string>
- <!-- no translation found for recents_search_bar_label (8074997400187836677) -->
- <skip />
+ <string name="recents_search_bar_label" msgid="8074997400187836677">"haku"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Verkkoa saatetaan\nvalvoa"</string>
<string name="description_target_search" msgid="3091587249776033139">"Haku"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Liu\'uta ylös ja <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
@@ -217,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"%d ilmoitusta piilotettu"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Näytä koskettamalla"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Älä häiritse"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d muuta"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Avaa napauttamalla uudelleen"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Avaa lukitus pyyhkäisemällä ylös"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index f0eb784..f01c99a 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -222,4 +222,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d autres"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Touchez à nouveau pour ouvrir"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Glissez vers le haut pour déverrouiller"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 84dae3f..5efbd2f 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -218,9 +218,10 @@
<item quantity="other" msgid="7388721375827338153">"%d notifications masquées"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Appuyer pour afficher"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Ne pas déranger"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"+ %d autres"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Appuyer à nouveau pour ouvrir"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Faire glisser pour déverrouiller"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 2cdff20..e6d38ef 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d और"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"खोलने के लिए पुन: टैप करें"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"अनलॉक करने के लिए ऊपर स्वाइप करें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 8b69a4d..b777b8f 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"Broj skrivenih obavijesti: %d"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Dodirnite za prikaz"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Ne ometaj"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Još %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Dodirnite opet za otvaranje"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Prijeđite prstom prema gore za otključavanje"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index e9c7c7b..10732fe 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d további"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Koppintson rá ismét a megnyitáshoz"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Húzza felfelé az ujját a feloldáshoz"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index e2d69a0..f1c4869 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -206,8 +206,7 @@
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Գույների կարգավորման ռեժիմ"</string>
<string name="recents_empty_message" msgid="2269156590813544104">"ՎԵՐՋԻՆՆԵՐԸ"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Հավելվածի մասին"</string>
- <!-- no translation found for recents_search_bar_label (8074997400187836677) -->
- <skip />
+ <string name="recents_search_bar_label" msgid="8074997400187836677">"որոնել"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Ցանցը կարող է\nվերահսկվել"</string>
<string name="description_target_search" msgid="3091587249776033139">"Որոնել"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Սահեցրեք վերև <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>-ի համար:"</string>
@@ -217,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"%d ծանուցում թաքցված է"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Հպեք՝ ցուցադրելու համար"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Չխանգարել"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Եվս %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Կրկին հպեք՝ բացելու համար"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Սահեցրեք վերև` ապակողպելու համար"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index c3ec03b..f0b0713 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d lainnya"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Ketuk lagi untuk membuka"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Gesek ke atas untuk membuka kunci"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 60f9fc8..6a57682 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -222,4 +222,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Altre %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Tocca ancora per aprire"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Scorri verso l\'alto per sbloccare"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index f9dc2e7..3e87b76 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"עוד %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"הקש שוב כדי לפתוח"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"החלק מעלה כדי לבטל את הנעילה"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 73fb622..1edb630 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -218,9 +218,10 @@
<item quantity="other" msgid="7388721375827338153">"%d件の通知が非表示"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"表示するにはタップします"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"通知を非表示"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"他%d件"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"開くにはもう一度タップしてください"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"ロック解除するには上にスワイプしてください"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 0cd7812..9d4344a 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -206,8 +206,7 @@
<string name="quick_settings_color_space_label" msgid="853443689745584770">"ფერთა კორექციის რეჟიმი"</string>
<string name="recents_empty_message" msgid="2269156590813544104">"ბოლო დროის"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"აპლიკაციის შესახებ"</string>
- <!-- no translation found for recents_search_bar_label (8074997400187836677) -->
- <skip />
+ <string name="recents_search_bar_label" msgid="8074997400187836677">"ძიება"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"შესაძლოა ქსელზე\nმონიტორინგი ხორციელდებოდეს"</string>
<string name="description_target_search" msgid="3091587249776033139">"ძიება"</string>
<string name="description_direction_up" msgid="7169032478259485180">"გაასრიალეთ ზემოთ <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>-თვის."</string>
@@ -217,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"%d შეტყობინება დამალულია"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"შეეხეთ საჩვენებლად"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"არ შემაწუხოთ"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d სხვა"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"შეეხეთ ისევ გასახსნელად"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"გაასრიალეთ ზევით განსაბლოკად"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 30eae2e..bb2d3b9 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d ទៀត"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"ប៉ះម្ដងទៀត ដើម្បីបើក"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"អូសឡើងលើ ដើម្បីដោះសោ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index e1ab831..83ffa9f 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"알림 %d개 숨김"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"표시하려면 터치"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"알림 일시중지"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d개 더보기"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"다시 탭하여 열기"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"위로 스와이프하여 잠금 해제"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 3ade551..9a3d483 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"ເຊື່ອງ %d ການແຈ້ງເຕືອນແລ້ວ"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"ແຕະເພື່ອສະແດງ"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"ຫ້າມລົບກວນ"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d ເພີ່ມເຕີມ"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"ແຕະອີກຄັ້ງເພື່ອເປີດ"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"ເລື່ອນຂຶ້ນເພື່ອປົດລັອກ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 3dc6a7a..0e9f5b7 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Dar %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Palieskite dar kartą, kad atidarytumėte"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Perbraukite aukštyn, kad atrakintumėte"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 9405545..ca07095 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"vēl %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Pieskarieties vēlreiz, lai atvērtu"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Velciet uz augšu, lai atbloķētu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 99ecacf..4545301 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"өөр %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Нээхийн тулд дахин товшино уу"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Түгжээг тайлах бол шудрана уу"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 886d644..aa7e6eb 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -216,9 +216,12 @@
<item quantity="other" msgid="7388721375827338153">"%d pemberitahuan disembunyikan"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Sentuh untuk menunjukkan"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Jangan ganggu"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d lagi"</item>
</plurals>
+ <!-- no translation found for notification_tap_again (7590196980943943842) -->
+ <skip />
+ <!-- no translation found for keyguard_unlock (8043466894212841998) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 1892632..001e733 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"%d varsler er skjult"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Trykk for å vise"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Ikke forstyrr"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d til"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Trykk på nytt for å åpne"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Sveip oppover for å låse opp"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index bf4d765..8a6e33b 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Nog %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Tik nogmaals om te openen"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Veeg omhoog om te ontgrendelen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 57605b2..4862d1b 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d więcej"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Kliknij ponownie, by otworzyć"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Przesuń w górę, by odblokować"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index fc71887..012283a 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Mais %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Toque novamente para abrir"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Deslizar rapidamente com o dedo para cima para desbloquear"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index d1ed7ac..0978d18 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -218,9 +218,10 @@
<item quantity="other" msgid="7388721375827338153">"%d notificações ocultas"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Toque para mostrar"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Não perturbe"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Mais %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Toque novamente para abrir"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Deslize para cima para desbloquear"</string>
</resources>
diff --git a/packages/SystemUI/res/values-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml
index 4e0f0f6..25f643f 100644
--- a/packages/SystemUI/res/values-rm/strings.xml
+++ b/packages/SystemUI/res/values-rm/strings.xml
@@ -399,4 +399,8 @@
<!-- no translation found for zen_mode_title (8793432092004749188) -->
<skip />
<!-- no translation found for keyguard_more_overflow_text:other (9180696159506883684) -->
+ <!-- no translation found for notification_tap_again (7590196980943943842) -->
+ <skip />
+ <!-- no translation found for keyguard_unlock (8043466894212841998) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index cef305c..6281110 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -206,8 +206,7 @@
<string name="quick_settings_color_space_label" msgid="853443689745584770">"Mod de corectare a culorilor"</string>
<string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informații despre aplicație"</string>
- <!-- no translation found for recents_search_bar_label (8074997400187836677) -->
- <skip />
+ <string name="recents_search_bar_label" msgid="8074997400187836677">"căutare"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Rețeaua poate\nfi monitorizată"</string>
<string name="description_target_search" msgid="3091587249776033139">"Căutaţi"</string>
<string name="description_direction_up" msgid="7169032478259485180">"Glisaţi în sus pentru <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
@@ -217,9 +216,12 @@
<item quantity="other" msgid="7388721375827338153">"%d de notificări ascunse"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Atingeți pentru a afișa"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Nu deranjaţi"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Încă %d"</item>
</plurals>
+ <!-- no translation found for notification_tap_again (7590196980943943842) -->
+ <skip />
+ <!-- no translation found for keyguard_unlock (8043466894212841998) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index ef4b6ee..976a793 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -218,9 +218,10 @@
<item quantity="other" msgid="7388721375827338153">"Скрыто оповещений: %d"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Нажмите, чтобы открыть"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Не беспокоить"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Ещё %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Нажмите ещё раз, чтобы открыть"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Для разблокировки проведите пальцем по экрану"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 11e8b26..1224c3a 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -218,9 +218,10 @@
<item quantity="other" msgid="7388721375827338153">"Skryté upozornenia: %d"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Upozornenie zobrazíte dotykom"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Nerušiť"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d ďalších"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Upozornenie otvoríte opätovným klepnutím"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Zariadenie odomknete prejdením prstom nahor"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 00d0086..10011bc 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"še %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Znova se dotaknite, da odprete"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Povlecite, da odklenete"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 0f8686c..0c6a939 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"Сакривена обавештења: %d"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Додирните за приказ"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Не узнемиравај"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Још %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Додирните поново да бисте отворили"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Превуците нагоре да бисте откључали"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index a0e1468..d8bd1b1 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d till"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Tryck igen för att öppna"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Dra uppåt om du vill låsa upp"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 2c84b19..d308b518 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -214,9 +214,10 @@
<item quantity="other" msgid="7388721375827338153">"Arifa %d zimefichwa"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Gusa ili zionekane"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Usisumbue"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d zaidi"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Gonga tena ili ufungue"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Telezesha kidole ili ufungue"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 1ebe21c..7929a30 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"อีก %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"แตะอีกครั้งเพื่อเปิด"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"กวาดขึ้นเพื่อปลดล็อก"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index eb8f236..92473a4 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"Nakatago ang %d (na) notification"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Pindutin upang ipakita"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Huwag istorbohin"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d pa"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"I-tap ulit upang buksan"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Mag-swipe pataas upang i-unlock"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 1b57a94..9837ff8 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"%d bildirim gizli"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Görüntülemek için dokunun"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Rahatsız etmeyin"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d adet daha"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Açmak için tekrar hafifçe vurun"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Kilidi açmak için hızlıca yukarı kaydırın"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 60192d7..534f520 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"Ще %d"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Торкніться знову, щоб відкрити"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Проведіть пальцем угору, щоб розблокувати"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 8770854..56fc89c 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -216,9 +216,10 @@
<item quantity="other" msgid="7388721375827338153">"%d thông báo ẩn"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Chạm để hiển thị"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"Không làm phiền"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d thông báo khác"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Nhấn lại để mở"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Vuốt lên để mở khóa"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 89250a5..442fdb7 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -218,9 +218,10 @@
<item quantity="other" msgid="7388721375827338153">"隐藏了%d条通知"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"触摸即可显示"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"勿扰"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"还有%d条"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"再次点按即可打开"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"向上滑动即可解锁"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 6fb723e..54a3b1e 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -222,4 +222,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"還有 %d 個"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"再次輕按即可開啟"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"向上快速滑動即可解鎖"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 9ceb4e9..fc74f53 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -218,9 +218,10 @@
<item quantity="other" msgid="7388721375827338153">"已隱藏 %d 則通知"</item>
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"輕觸即可顯示"</string>
- <!-- no translation found for zen_mode_title (8793432092004749188) -->
- <skip />
+ <string name="zen_mode_title" msgid="8793432092004749188">"請勿打擾"</string>
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"還有 %d 則"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"再次輕按即可開啟"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"向上滑動即可解鎖"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 81a08d9..dfe8838 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -220,4 +220,6 @@
<plurals name="keyguard_more_overflow_text">
<item quantity="other" msgid="9180696159506883684">"%d okuningi"</item>
</plurals>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Thepha futhi ukuze uvule"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Swayiphela phezulu ukuze uvule"</string>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0604817..9837d9b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -82,12 +82,6 @@
<!-- Height of a medium notification in the status bar -->
<dimen name="notification_mid_height">128dp</dimen>
- <!-- Height of a small notification in the status bar plus glow, padding, etc -->
- <dimen name="notification_row_min_height">68dp</dimen>
-
- <!-- Height of a large notification in the status bar plus glow, padding, etc -->
- <dimen name="notification_row_max_height">260dp</dimen>
-
<!-- size at which Notification icons will be drawn in the status bar -->
<dimen name="status_bar_icon_drawing_size">18dip</dimen>
@@ -182,7 +176,7 @@
<dimen name="carrier_label_height">24dp</dimen>
<!-- The distance you can pull a notification before it pops open -->
- <dimen name="one_finger_pop_limit">32dp</dimen>
+ <dimen name="one_finger_pop_limit">0dp</dimen>
<!-- The fixed height of each tile -->
<dimen name="quick_settings_cell_height">110dp</dimen>
@@ -194,7 +188,7 @@
Not used at this screen size. -->
<item type="dimen" name="notification_panel_min_height_frac">0%</item>
- <dimen name="blinds_pop_threshold">32dp</dimen>
+ <dimen name="blinds_pop_threshold">0dp</dimen>
<!-- The size of the gesture span needed to activate the "pull" notification expansion -->
<dimen name="pull_span_min">25dp</dimen>
@@ -261,6 +255,9 @@
<!-- Z distance between notifications if they are in the stack -->
<dimen name="z_distance_between_notifications">2dp</dimen>
+ <!-- The padding between the individual notification cards. -->
+ <dimen name="notification_padding">3dp</dimen>
+
<!-- Width of the zen mode interstitial dialog. -->
<dimen name="zen_mode_dialog_width">320dp</dimen>
@@ -269,4 +266,6 @@
<dimen name="quick_settings_tmp_scrim_stroke_width">8dp</dimen>
<dimen name="quick_settings_tmp_scrim_text_size">30dp</dimen>
+
+ <dimen name="notifications_top_padding">8dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 8dd3f8d..61c268e 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -19,7 +19,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.media.AudioManager;
@@ -32,8 +31,9 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewConfiguration;
-import android.view.ViewGroup;
+import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.ScrollAdapter;
public class ExpandHelper implements Gefingerpoken, OnClickListener {
@@ -81,8 +81,6 @@
private boolean mHasPopped;
private View mEventSource;
private View mCurrView;
- private View mCurrViewTopGlow;
- private View mCurrViewBottomGlow;
private float mOldHeight;
private float mNaturalHeight;
private float mInitialTouchFocusY;
@@ -99,9 +97,6 @@
private ScaleGestureDetector mSGD;
private ViewScaler mScaler;
private ObjectAnimator mScaleAnimation;
- private AnimatorSet mGlowAnimationSet;
- private ObjectAnimator mGlowTopAnimation;
- private ObjectAnimator mGlowBottomAnimation;
private Vibrator mVibrator;
private int mSmallSize;
@@ -121,9 +116,7 @@
float focusY = detector.getFocusY();
final View underFocus = findView(focusX, focusY);
- if (underFocus != null) {
- startExpanding(underFocus, STRETCH);
- }
+ startExpanding(underFocus, STRETCH);
return mExpanding;
}
@@ -139,41 +132,21 @@
};
private class ViewScaler {
- View mView;
+ ExpandableView mView;
public ViewScaler() {}
- public void setView(View v) {
+ public void setView(ExpandableView v) {
mView = v;
}
public void setHeight(float h) {
if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
- ViewGroup.LayoutParams lp = mView.getLayoutParams();
- lp.height = (int)h;
- mView.setLayoutParams(lp);
- mView.requestLayout();
+ mView.setActualHeight((int) h);
}
public float getHeight() {
- int height = mView.getLayoutParams().height;
- if (height < 0) {
- height = mView.getMeasuredHeight();
- }
- return height;
+ return mView.getActualHeight();
}
public int getNaturalHeight(int maximum) {
- ViewGroup.LayoutParams lp = mView.getLayoutParams();
- if (DEBUG_SCALE) Log.v(TAG, "Inspecting a child of type: " +
- mView.getClass().getName());
- int oldHeight = lp.height;
- lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- mView.setLayoutParams(lp);
- mView.measure(
- View.MeasureSpec.makeMeasureSpec(mView.getMeasuredWidth(),
- View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(maximum,
- View.MeasureSpec.AT_MOST));
- lp.height = oldHeight;
- mView.setLayoutParams(lp);
- return mView.getMeasuredHeight();
+ return Math.min(maximum, mView.getMaxHeight());
}
}
@@ -195,12 +168,6 @@
mGravity = Gravity.TOP;
mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f);
mScaleAnimation.setDuration(EXPAND_DURATION);
- mScaleAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mCallback.setUserLockedChild(mCurrView, false);
- }
- });
mPopLimit = mContext.getResources().getDimension(R.dimen.blinds_pop_threshold);
mPopDuration = mContext.getResources().getInteger(R.integer.blinds_pop_duration_ms);
mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min);
@@ -223,14 +190,6 @@
}
};
- mGlowTopAnimation = ObjectAnimator.ofFloat(null, "alpha", 0f);
- mGlowTopAnimation.addListener(glowVisibilityController);
- mGlowBottomAnimation = ObjectAnimator.ofFloat(null, "alpha", 0f);
- mGlowBottomAnimation.addListener(glowVisibilityController);
- mGlowAnimationSet = new AnimatorSet();
- mGlowAnimationSet.play(mGlowTopAnimation).with(mGlowBottomAnimation);
- mGlowAnimationSet.setDuration(GLOW_DURATION);
-
final ViewConfiguration configuration = ViewConfiguration.get(mContext);
mTouchSlop = configuration.getScaledTouchSlop();
@@ -251,7 +210,6 @@
float newHeight = clamp(target);
mScaler.setHeight(newHeight);
- setGlow(calculateGlow(target, newHeight));
mLastFocusY = mSGD.getFocusY();
mLastSpanY = mSGD.getCurrentSpan();
}
@@ -322,37 +280,6 @@
return (GLOW_BASE + strength * (1f - GLOW_BASE));
}
- public void setGlow(float glow) {
- if (!mGlowAnimationSet.isRunning() || glow == 0f) {
- if (mGlowAnimationSet.isRunning()) {
- mGlowAnimationSet.end();
- }
- if (mCurrViewTopGlow != null && mCurrViewBottomGlow != null) {
- if (glow == 0f || mCurrViewTopGlow.getAlpha() == 0f) {
- // animate glow in and out
- mGlowTopAnimation.setTarget(mCurrViewTopGlow);
- mGlowBottomAnimation.setTarget(mCurrViewBottomGlow);
- mGlowTopAnimation.setFloatValues(glow);
- mGlowBottomAnimation.setFloatValues(glow);
- mGlowAnimationSet.setupStartValues();
- mGlowAnimationSet.start();
- } else {
- // set it explicitly in reponse to touches.
- mCurrViewTopGlow.setAlpha(glow);
- mCurrViewBottomGlow.setAlpha(glow);
- handleGlowVisibility();
- }
- }
- }
- }
-
- private void handleGlowVisibility() {
- mCurrViewTopGlow.setVisibility(mCurrViewTopGlow.getAlpha() <= 0.0f ?
- View.INVISIBLE : View.VISIBLE);
- mCurrViewBottomGlow.setVisibility(mCurrViewBottomGlow.getAlpha() <= 0.0f ?
- View.INVISIBLE : View.VISIBLE);
- }
-
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
@@ -387,9 +314,7 @@
if (DEBUG_SCALE) Log.v(TAG, "got pull gesture (xspan=" + xspan + "px)");
final View underFocus = findView(x, y);
- if (underFocus != null) {
- startExpanding(underFocus, PULL);
- }
+ startExpanding(underFocus, PULL);
return true;
}
if (mScrollAdapter != null && !mScrollAdapter.isScrolledToTop()) {
@@ -404,8 +329,7 @@
if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
mLastMotionY = y;
final View underFocus = findView(x, y);
- if (underFocus != null) {
- startExpanding(underFocus, BLINDS);
+ if (startExpanding(underFocus, BLINDS)) {
mInitialTouchY = mLastMotionY;
mHasPopped = false;
}
@@ -466,9 +390,6 @@
if (mHasPopped) {
mScaler.setHeight(newHeight);
- setGlow(GLOW_BASE);
- } else {
- setGlow(calculateGlow(4f * pull, 0f));
}
final int x = (int) mSGD.getFocusX();
@@ -508,17 +429,22 @@
return true;
}
- private void startExpanding(View v, int expandType) {
+ /**
+ * @return True if the view is expandable, false otherwise.
+ */
+ private boolean startExpanding(View v, int expandType) {
+ if (!(v instanceof ExpandableNotificationRow)) {
+ return false;
+ }
mExpansionStyle = expandType;
- if (mExpanding && v == mCurrView) {
- return;
+ if (mExpanding && v == mCurrView) {
+ return true;
}
mExpanding = true;
if (DEBUG) Log.d(TAG, "scale type " + expandType + " beginning on view: " + v);
mCallback.setUserLockedChild(v, true);
setView(v);
- setGlow(GLOW_BASE);
- mScaler.setView(v);
+ mScaler.setView((ExpandableView) v);
mOldHeight = mScaler.getHeight();
if (mCallback.canChildBeExpanded(v)) {
if (DEBUG) Log.d(TAG, "working on an expandable child");
@@ -530,6 +456,7 @@
if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight +
" mNaturalHeight: " + mNaturalHeight);
v.getParent().requestDisallowInterceptTouchEvent(true);
+ return true;
}
private void finishExpanding(boolean force) {
@@ -549,11 +476,18 @@
if (mScaleAnimation.isRunning()) {
mScaleAnimation.cancel();
}
- setGlow(0f);
- mCallback.setUserExpandedChild(mCurrView, h == mNaturalHeight);
+ mCallback.setUserExpandedChild(mCurrView, targetHeight == mNaturalHeight);
if (targetHeight != currentHeight) {
mScaleAnimation.setFloatValues(targetHeight);
mScaleAnimation.setupStartValues();
+ final View scaledView = mCurrView;
+ mScaleAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCallback.setUserLockedChild(scaledView, false);
+ mScaleAnimation.removeListener(this);
+ }
+ });
mScaleAnimation.start();
} else {
mCallback.setUserLockedChild(mCurrView, false);
@@ -571,23 +505,11 @@
private void clearView() {
mCurrView = null;
- mCurrViewTopGlow = null;
- mCurrViewBottomGlow = null;
+
}
private void setView(View v) {
mCurrView = v;
- if (v instanceof ViewGroup) {
- ViewGroup g = (ViewGroup) v;
- mCurrViewTopGlow = g.findViewById(R.id.top_glow);
- mCurrViewBottomGlow = g.findViewById(R.id.bottom_glow);
- if (DEBUG) {
- String debugLog = "Looking for glows: " +
- (mCurrViewTopGlow != null ? "found top " : "didn't find top") +
- (mCurrViewBottomGlow != null ? "found bottom " : "didn't find bottom");
- Log.v(TAG, debugLog);
- }
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 74483cc..4857adc 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -211,6 +211,7 @@
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
+ mBackground = null;
}
void updateSurfaceSize(SurfaceHolder surfaceHolder) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 72d9a52..a996c1a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -26,7 +26,7 @@
public static final boolean Verbose = false;
public static class App {
- public static final boolean EnableTaskFiltering = true;
+ public static final boolean EnableTaskFiltering = false;
public static final boolean EnableTaskStackClipping = false;
public static final boolean EnableInfoPane = true;
public static final boolean EnableSearchButton = false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index a77e61d..0a8e76f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -976,8 +976,6 @@
} else {
mStack.filterTasks(tv.getTask());
}
- } else {
- Console.logError(getContext(), "Task Filtering TBD");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
similarity index 82%
rename from packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 5e90084..0f32dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,7 +11,7 @@
* 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.
+ * limitations under the License
*/
package com.android.systemui.statusbar;
@@ -21,12 +21,15 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import com.android.internal.R;
-public class LatestItemView extends FrameLayout {
+/**
+ * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
+ * to implement dimming/activating on Keyguard for the double-tap gesture
+ */
+public abstract class ActivatableNotificationView extends ExpandableOutlineView {
private static final long DOUBLETAP_TIMEOUT_MS = 1000;
@@ -48,11 +51,13 @@
private OnActivatedListener mOnActivatedListener;
- public LatestItemView(Context context, AttributeSet attrs) {
+ public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ updateBackgroundResource();
}
+
private final Runnable mTapTimeoutRunnable = new Runnable() {
@Override
public void run() {
@@ -66,20 +71,6 @@
}
@Override
- public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
- if (super.onRequestSendAccessibilityEvent(child, event)) {
- // Add a record for the entire layout since its content is somehow small.
- // The event comes from a leaf view that is interacted with.
- AccessibilityEvent record = AccessibilityEvent.obtain();
- onInitializeAccessibilityEvent(record);
- dispatchPopulateAccessibilityEvent(record);
- event.appendRecord(record);
- return true;
- }
- return false;
- }
-
- @Override
public boolean onTouchEvent(MotionEvent event) {
if (mLocked) {
return handleTouchEventLocked(event);
@@ -94,6 +85,9 @@
case MotionEvent.ACTION_DOWN:
mDownX = event.getX();
mDownY = event.getY();
+ if (mDownY > getActualHeight()) {
+ return false;
+ }
// Call the listener tentatively directly, even if we don't know whether the user
// will stay within the touch slop, as the listener is implemented as a scale
@@ -132,7 +126,7 @@
}
private void makeActive(float x, float y) {
- getBackground().setHotspot(0, x, y);
+ mCustomBackground.setHotspot(0, x, y);
mActivated = true;
}
@@ -142,8 +136,8 @@
private void makeInactive() {
if (mActivated) {
// Make sure that we clear the hotspot from the center.
- getBackground().setHotspot(0, getWidth() / 2, getHeight() / 2);
- getBackground().removeHotspot(0);
+ mCustomBackground.setHotspot(0, getWidth() / 2, getActualHeight() / 2);
+ mCustomBackground.removeHotspot(0);
mActivated = false;
}
if (mOnActivatedListener != null) {
@@ -188,7 +182,19 @@
}
private void updateBackgroundResource() {
- setBackgroundResource(mDimmed ? mDimmedBgResId : mBgResId);
+ setCustomBackgroundResource(mDimmed ? mDimmedBgResId : mBgResId);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ setPivotX(getWidth()/2);
+ }
+
+ @Override
+ public void setActualHeight(int actualHeight) {
+ super.setActualHeight(actualHeight);
+ setPivotY(actualHeight/2);
}
public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 2ea5add..9149e2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -71,7 +71,6 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
import com.android.internal.util.LegacyNotificationUtil;
-import com.android.internal.widget.SizeAdaptiveLayout;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SearchPanelView;
@@ -83,7 +82,7 @@
import java.util.Locale;
public abstract class BaseStatusBar extends SystemUI implements
- CommandQueue.Callbacks, LatestItemView.OnActivatedListener {
+ CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener {
public static final String TAG = "StatusBar";
public static final boolean DEBUG = false;
public static final boolean MULTIUSER_DEBUG = false;
@@ -737,8 +736,9 @@
return false;
}
- Log.v(TAG, "publicNotification: "
- + sbn.getNotification().publicVersion);
+ if (DEBUG) {
+ Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
+ }
Notification publicNotification = sbn.getNotification().publicVersion;
@@ -759,20 +759,20 @@
// NB: the large icon is now handled entirely by the template
// bind the click event to the content area
- ViewGroup content = (ViewGroup)row.findViewById(R.id.container);
- SizeAdaptiveLayout expanded = (SizeAdaptiveLayout)row.findViewById(R.id.expanded);
- SizeAdaptiveLayout expandedPublic
- = (SizeAdaptiveLayout)row.findViewById(R.id.expandedPublic);
+ NotificationContentView expanded =
+ (NotificationContentView) row.findViewById(R.id.expanded);
+ NotificationContentView expandedPublic =
+ (NotificationContentView) row.findViewById(R.id.expandedPublic);
- content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
PendingIntent contentIntent = sbn.getNotification().contentIntent;
if (contentIntent != null) {
final View.OnClickListener listener = makeClicker(contentIntent,
sbn.getPackageName(), sbn.getTag(), sbn.getId(), isHeadsUp, sbn.getUserId());
- content.setOnClickListener(listener);
+ row.setOnClickListener(listener);
} else {
- content.setOnClickListener(null);
+ row.setOnClickListener(null);
}
// set up the adaptive layout
@@ -794,19 +794,11 @@
if (contentViewLocal != null) {
contentViewLocal.setIsRootNamespace(true);
- SizeAdaptiveLayout.LayoutParams params =
- new SizeAdaptiveLayout.LayoutParams(contentViewLocal.getLayoutParams());
- params.minHeight = minHeight;
- params.maxHeight = minHeight;
- expanded.addView(contentViewLocal, params);
+ expanded.setContractedChild(contentViewLocal);
}
if (bigContentViewLocal != null) {
bigContentViewLocal.setIsRootNamespace(true);
- SizeAdaptiveLayout.LayoutParams params =
- new SizeAdaptiveLayout.LayoutParams(bigContentViewLocal.getLayoutParams());
- params.minHeight = minHeight+1;
- params.maxHeight = maxHeight;
- expanded.addView(bigContentViewLocal, params);
+ expanded.setExpandedChild(bigContentViewLocal);
}
PackageManager pm = mContext.getPackageManager();
@@ -820,11 +812,7 @@
if (publicViewLocal != null) {
publicViewLocal.setIsRootNamespace(true);
- SizeAdaptiveLayout.LayoutParams params =
- new SizeAdaptiveLayout.LayoutParams(publicViewLocal.getLayoutParams());
- params.minHeight = minHeight;
- params.maxHeight = minHeight;
- expandedPublic.addView(publicViewLocal, params);
+ expandedPublic.setContractedChild(publicViewLocal);
}
}
catch (RuntimeException e) {
@@ -882,7 +870,6 @@
entry.row = row;
entry.row.setHeightRange(mRowMinHeight, mRowMaxHeight);
entry.row.setOnActivatedListener(this);
- entry.content = content;
entry.expanded = contentViewLocal;
entry.expandedPublic = publicViewLocal;
entry.setBigContentView(bigContentViewLocal);
@@ -1073,11 +1060,12 @@
for (int i = n-1; i >= 0; i--) {
NotificationData.Entry entry = mNotificationData.get(i);
if (mOnKeyguard) {
- entry.row.setSystemExpanded(false);
+ entry.row.setExpansionDisabled(true);
} else {
+ entry.row.setExpansionDisabled(false);
if (!entry.row.isUserLocked()) {
boolean top = (i == n-1);
- entry.row.setSystemExpanded(top || entry.row.isUserExpanded());
+ entry.row.setSystemExpanded(top);
}
}
entry.row.setDimmed(mOnKeyguard);
@@ -1090,6 +1078,10 @@
mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
}
} else {
+ if (entry.row.getVisibility() == View.GONE) {
+ // notify the scroller of a child addition
+ mStackScroller.generateAddAnimation(entry.row);
+ }
entry.row.setVisibility(View.VISIBLE);
visibleNotifications++;
}
@@ -1290,8 +1282,8 @@
final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
if (userChangedExpansion) {
boolean userExpanded = oldEntry.row.isUserExpanded();
- newEntry.row.applyExpansionToLayout(userExpanded);
newEntry.row.setUserExpanded(userExpanded);
+ newEntry.row.applyExpansionToLayout();
}
}
@@ -1349,10 +1341,11 @@
final View.OnClickListener listener = makeClicker(contentIntent,
notification.getPackageName(), notification.getTag(), notification.getId(),
isHeadsUp, notification.getUserId());
- entry.content.setOnClickListener(listener);
+ entry.row.setOnClickListener(listener);
} else {
- entry.content.setOnClickListener(null);
+ entry.row.setOnClickListener(null);
}
+ entry.row.notifyContentUpdated();
}
protected void notifyHeadsUpScreenOn(boolean screenOn) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index bb481ec..61aad6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -19,14 +19,11 @@
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
+import android.view.accessibility.AccessibilityEvent;
-import com.android.internal.widget.SizeAdaptiveLayout;
import com.android.systemui.R;
-public class ExpandableNotificationRow extends FrameLayout
- implements LatestItemView.OnActivatedListener {
+public class ExpandableNotificationRow extends ActivatableNotificationView {
private int mRowMinHeight;
private int mRowMaxHeight;
@@ -41,20 +38,21 @@
/** Are we showing the "public" version */
private boolean mShowingPublic;
- private LatestItemView mLatestItemView;
-
/**
* Is this notification expanded by the system. The expansion state can be overridden by the
* user expansion.
*/
private boolean mIsSystemExpanded;
- private SizeAdaptiveLayout mPublicLayout;
- private SizeAdaptiveLayout mPrivateLayout;
+
+ /**
+ * Whether the notification expansion is disabled. This is the case on Keyguard.
+ */
+ private boolean mExpansionDisabled;
+
+ private NotificationContentView mPublicLayout;
+ private NotificationContentView mPrivateLayout;
private int mMaxExpandHeight;
- private boolean mMaxHeightNeedsUpdate;
private NotificationActivator mActivator;
- private LatestItemView.OnActivatedListener mOnActivatedListener;
- private boolean mSelfInitiatedLayout;
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -63,18 +61,29 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mPublicLayout = (SizeAdaptiveLayout) findViewById(R.id.expandedPublic);
- mPrivateLayout = (SizeAdaptiveLayout) findViewById(R.id.expanded);
- mLatestItemView = (LatestItemView) findViewById(R.id.container);
+ mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
+ mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
mActivator = new NotificationActivator(this);
- mLatestItemView.setOnActivatedListener(this);
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+ if (super.onRequestSendAccessibilityEvent(child, event)) {
+ // Add a record for the entire layout since its content is somehow small.
+ // The event comes from a leaf view that is interacted with.
+ AccessibilityEvent record = AccessibilityEvent.obtain();
+ onInitializeAccessibilityEvent(record);
+ dispatchPopulateAccessibilityEvent(record);
+ event.appendRecord(record);
+ return true;
+ }
+ return false;
}
public void setHeightRange(int rowMinHeight, int rowMaxHeight) {
mRowMinHeight = rowMinHeight;
mRowMaxHeight = rowMaxHeight;
- mMaxHeightNeedsUpdate = true;
}
public boolean isExpandable() {
@@ -128,22 +137,27 @@
*/
public void setSystemExpanded(boolean expand) {
mIsSystemExpanded = expand;
- applyExpansionToLayout(expand);
+ applyExpansionToLayout();
+ }
+
+ /**
+ * @param expansionDisabled whether to prevent notification expansion
+ */
+ public void setExpansionDisabled(boolean expansionDisabled) {
+ mExpansionDisabled = expansionDisabled;
+ applyExpansionToLayout();
}
/**
* Apply an expansion state to the layout.
- *
- * @param expand should the layout be in the expanded state
*/
- public void applyExpansionToLayout(boolean expand) {
- ViewGroup.LayoutParams lp = getLayoutParams();
+ public void applyExpansionToLayout() {
+ boolean expand = isExpanded();
if (expand && mExpandable) {
- lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ setActualHeight(mMaxExpandHeight);
} else {
- lp.height = mRowMinHeight;
+ setActualHeight(mRowMinHeight);
}
- setLayoutParams(lp);
}
/**
@@ -153,6 +167,9 @@
* @return the maximum allowed expansion height of this view.
*/
public int getMaximumAllowedExpandHeight() {
+ if (isUserLocked()) {
+ return getActualHeight();
+ }
boolean inExpansionState = isExpanded();
if (!inExpansionState) {
// not expanded, so we return the collapsed size
@@ -162,31 +179,6 @@
return mShowingPublic ? mRowMinHeight : getMaxExpandHeight();
}
- private void updateMaxExpandHeight() {
-
- // We don't want this method to trigger a layout of the whole view hierarchy,
- // as the layout parameters in the end are the same which they were in the beginning.
- // Otherwise a loop may occur if this method is called on the layout of a parent.
- mSelfInitiatedLayout = true;
- ViewGroup.LayoutParams lp = getLayoutParams();
- int oldHeight = lp.height;
- lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- setLayoutParams(lp);
- measure(View.MeasureSpec.makeMeasureSpec(getWidth(), View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(mRowMaxHeight, View.MeasureSpec.AT_MOST));
- lp.height = oldHeight;
- setLayoutParams(lp);
- mMaxExpandHeight = getMeasuredHeight();
- mSelfInitiatedLayout = false;
- }
-
- @Override
- public void requestLayout() {
- if (!mSelfInitiatedLayout) {
- super.requestLayout();
- }
- }
-
/**
* Check whether the view state is currently expanded. This is given by the system in {@link
* #setSystemExpanded(boolean)} and can be overridden by user expansion or
@@ -196,13 +188,18 @@
* @return whether the view state is currently expanded.
*/
private boolean isExpanded() {
- return !hasUserChangedExpansion() && isSystemExpanded() || isUserExpanded();
+ return !mExpansionDisabled
+ && (!hasUserChangedExpansion() && isSystemExpanded() || isUserExpanded());
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- mMaxHeightNeedsUpdate = true;
+ boolean updateExpandHeight = mMaxExpandHeight == 0;
+ mMaxExpandHeight = mPrivateLayout.getMaxHeight();
+ if (updateExpandHeight) {
+ applyExpansionToLayout();
+ }
}
public void setShowingPublic(boolean show) {
@@ -220,62 +217,44 @@
* Sets the notification as dimmed, meaning that it will appear in a more gray variant.
*/
public void setDimmed(boolean dimmed) {
- mLatestItemView.setDimmed(dimmed);
+ super.setDimmed(dimmed);
mActivator.setDimmed(dimmed);
}
public int getMaxExpandHeight() {
- if (mMaxHeightNeedsUpdate) {
- updateMaxExpandHeight();
- mMaxHeightNeedsUpdate = false;
- }
return mMaxExpandHeight;
}
- /**
- * Sets the notification as locked. In the locked state, the first tap will produce a quantum
- * ripple to make the notification brighter and only the second tap will cause a click.
- */
- public void setLocked(boolean locked) {
- mLatestItemView.setLocked(locked);
- }
-
- public void setOnActivatedListener(LatestItemView.OnActivatedListener listener) {
- mOnActivatedListener = listener;
- }
-
public NotificationActivator getActivator() {
return mActivator;
}
- @Override
- public void onActivated(View view) {
- if (mOnActivatedListener != null) {
- mOnActivatedListener.onActivated(this);
- }
- }
-
- @Override
- public void onReset(View view) {
- if (mOnActivatedListener != null) {
- mOnActivatedListener.onReset(this);
- }
- }
-
- /**
- * Sets the resource id for the background of this notification.
- *
- * @param bgResId The background resource to use in normal state.
- * @param dimmedBgResId The background resource to use in dimmed state.
- */
- public void setBackgroundResourceIds(int bgResId, int dimmedBgResId) {
- mLatestItemView.setBackgroundResourceIds(bgResId, dimmedBgResId);
- }
-
/**
* @return the potential height this view could expand in addition.
*/
public int getExpandPotential() {
- return getMaximumAllowedExpandHeight() - getHeight();
+ return getMaximumAllowedExpandHeight() - getActualHeight();
+ }
+
+ @Override
+ public void setActualHeight(int height) {
+ mPrivateLayout.setActualHeight(height);
+ invalidate();
+ super.setActualHeight(height);
+ }
+
+ @Override
+ public int getMaxHeight() {
+ return mPrivateLayout.getMaxHeight();
+ }
+
+ @Override
+ public void setClipTopAmount(int clipTopAmount) {
+ super.setClipTopAmount(clipTopAmount);
+ mPrivateLayout.setClipTopAmount(clipTopAmount);
+ }
+
+ public void notifyContentUpdated() {
+ mPrivateLayout.notifyContentUpdated();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
new file mode 100644
index 0000000..43eb5b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.graphics.Outline;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * Like {@link ExpandableView}, but setting an outline for the height and clipping.
+ */
+public abstract class ExpandableOutlineView extends ExpandableView {
+
+ private final Outline mOutline = new Outline();
+
+ public ExpandableOutlineView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void setActualHeight(int actualHeight) {
+ super.setActualHeight(actualHeight);
+ updateOutline();
+ }
+
+ @Override
+ public void setClipTopAmount(int clipTopAmount) {
+ super.setClipTopAmount(clipTopAmount);
+ updateOutline();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ updateOutline();
+ }
+
+ private void updateOutline() {
+ mOutline.setRect(0,
+ mClipTopAmount,
+ getWidth(),
+ Math.max(mActualHeight, mClipTopAmount));
+ setOutline(mOutline);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
new file mode 100644
index 0000000..35913fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * An abstract view for expandable views.
+ */
+public abstract class ExpandableView extends FrameLayout {
+
+ private OnHeightChangedListener mOnHeightChangedListener;
+ protected int mActualHeight;
+ protected int mClipTopAmount;
+ protected Drawable mCustomBackground;
+ private boolean mActualHeightInitialized;
+
+ public ExpandableView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mCustomBackground != null) {
+ mCustomBackground.setBounds(0, mClipTopAmount, getWidth(), mActualHeight);
+ mCustomBackground.draw(canvas);
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return super.verifyDrawable(who) || who == mCustomBackground;
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ final Drawable d = mCustomBackground;
+ if (d != null && d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (!mActualHeightInitialized && mActualHeight == 0) {
+ mActualHeight = getHeight();
+ }
+ mActualHeightInitialized = true;
+ }
+
+ /**
+ * Sets the actual height of this notification. This is different than the laid out
+ * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding.
+ */
+ public void setActualHeight(int actualHeight) {
+ mActualHeight = actualHeight;
+ invalidate();
+ if (mOnHeightChangedListener != null) {
+ mOnHeightChangedListener.onHeightChanged(this);
+ }
+ }
+
+ /**
+ * See {@link #setActualHeight}.
+ *
+ * @return The actual height of this notification.
+ */
+ public int getActualHeight() {
+ return mActualHeight;
+ }
+
+ /**
+ * @return The maximum height of this notification.
+ */
+ public abstract int getMaxHeight();
+
+ /**
+ * Sets the amount this view should be clipped from the top. This is used when an expanded
+ * notification is scrolling in the top or bottom stack.
+ *
+ * @param clipTopAmount The amount of pixels this view should be clipped from top.
+ */
+ public void setClipTopAmount(int clipTopAmount) {
+ mClipTopAmount = clipTopAmount;
+ invalidate();
+ }
+
+ public void setOnHeightChangedListener(OnHeightChangedListener listener) {
+ mOnHeightChangedListener = listener;
+ }
+
+ /**
+ * Sets a custom background drawable. As we need to change our bounds independently of layout,
+ * we need the notition of a custom background.
+ */
+ public void setCustomBackground(Drawable customBackground) {
+ if (mCustomBackground != null) {
+ mCustomBackground.setCallback(null);
+ unscheduleDrawable(mCustomBackground);
+ }
+ mCustomBackground = customBackground;
+ mCustomBackground.setCallback(this);
+ setWillNotDraw(customBackground == null);
+ invalidate();
+ }
+
+ public void setCustomBackgroundResource(int drawableResId) {
+ setCustomBackground(getResources().getDrawable(drawableResId));
+ }
+
+ /**
+ * A listener notifying when {@link #getActualHeight} changes.
+ */
+ public interface OnHeightChangedListener {
+ void onHeightChanged(ExpandableView view);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index d563968..6401695 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -107,7 +107,7 @@
mBar.updateNotification(mSynKey, sbn);
}
final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey);
- entry.content.setOnClickListener(mSynClickListener);
+ entry.row.setOnClickListener(mSynClickListener);
}
private final View.OnClickListener mSynClickListener = new View.OnClickListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
new file mode 100644
index 0000000..fd0cb08
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+
+import com.android.systemui.R;
+
+/**
+ * A frame layout containing the actual payload of the notification, including the contracted and
+ * expanded layout. This class is responsible for clipping the content and and switching between the
+ * expanded and contracted view depending on its clipped size.
+ */
+public class NotificationContentView extends ExpandableView {
+
+ private final Rect mClipBounds = new Rect();
+
+ private View mContractedChild;
+ private View mExpandedChild;
+
+ private int mSmallHeight;
+
+ public NotificationContentView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
+ mActualHeight = mSmallHeight;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ updateClipping();
+ }
+
+ public void setContractedChild(View child) {
+ if (mContractedChild != null) {
+ removeView(mContractedChild);
+ }
+ sanitizeContractedLayoutParams(child);
+ addView(child);
+ mContractedChild = child;
+ selectLayout();
+ }
+
+ public void setExpandedChild(View child) {
+ if (mExpandedChild != null) {
+ removeView(mExpandedChild);
+ }
+ addView(child);
+ mExpandedChild = child;
+ selectLayout();
+ }
+
+ @Override
+ public void setActualHeight(int actualHeight) {
+ super.setActualHeight(actualHeight);
+ selectLayout();
+ updateClipping();
+ }
+
+ @Override
+ public int getMaxHeight() {
+
+ // The maximum height is just the laid out height.
+ return getHeight();
+ }
+
+ @Override
+ public void setClipTopAmount(int clipTopAmount) {
+ super.setClipTopAmount(clipTopAmount);
+ updateClipping();
+ }
+
+ public int getClipTopAmount() {
+ return mClipTopAmount;
+ }
+
+ private void updateClipping() {
+ mClipBounds.set(0, mClipTopAmount, getWidth(), mActualHeight);
+ setClipBounds(mClipBounds);
+ }
+
+ private void sanitizeContractedLayoutParams(View contractedChild) {
+ LayoutParams lp = (LayoutParams) contractedChild.getLayoutParams();
+ lp.height = mSmallHeight;
+ contractedChild.setLayoutParams(lp);
+ }
+
+ private void selectLayout() {
+ if (mActualHeight <= mSmallHeight || mExpandedChild == null) {
+ if (mContractedChild.getVisibility() != View.VISIBLE) {
+ mContractedChild.setVisibility(View.VISIBLE);
+ }
+ if (mExpandedChild != null && mExpandedChild.getVisibility() != View.INVISIBLE) {
+ mExpandedChild.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ if (mExpandedChild.getVisibility() != View.VISIBLE) {
+ mExpandedChild.setVisibility(View.VISIBLE);
+ }
+ if (mContractedChild.getVisibility() != View.INVISIBLE) {
+ mContractedChild.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ public void notifyContentUpdated() {
+ selectLayout();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index b9a59dd..6b6f55a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -33,7 +33,6 @@
public StatusBarNotification notification;
public StatusBarIconView icon;
public ExpandableNotificationRow row; // the outer expanded view
- public View content; // takes the click events and sends the PendingIntent
public View expanded; // the inflated RemoteViews
public View expandedPublic; // for insecure lockscreens
public ImageView largeIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index be58dad..8ebd50d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -18,8 +18,6 @@
import android.content.Context;
import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.systemui.R;
@@ -27,28 +25,33 @@
/**
* Container view for overflowing notification icons on Keyguard.
*/
-public class NotificationOverflowContainer extends FrameLayout
- implements LatestItemView.OnActivatedListener {
+public class NotificationOverflowContainer extends ActivatableNotificationView {
private NotificationOverflowIconsView mIconsView;
- private LatestItemView.OnActivatedListener mOnActivatedListener;
private NotificationActivator mActivator;
- public NotificationOverflowContainer(Context context) {
- super(context);
- }
-
public NotificationOverflowContainer(Context context, AttributeSet attrs) {
super(context, attrs);
}
- public NotificationOverflowContainer(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
+ @Override
+ public void setActualHeight(int currentHeight) {
+ // noop
}
- public NotificationOverflowContainer(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
+ @Override
+ public int getActualHeight() {
+ return getHeight();
+ }
+
+ @Override
+ public int getMaxHeight() {
+ return getHeight();
+ }
+
+ @Override
+ public void setClipTopAmount(int clipTopAmount) {
+ // noop
}
@Override
@@ -57,35 +60,16 @@
mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
mIconsView.setMoreText((TextView) findViewById(R.id.more_text));
- LatestItemView latestItemView = (LatestItemView) findViewById(R.id.container);
mActivator = new NotificationActivator(this);
mActivator.setDimmed(true);
- latestItemView.setOnActivatedListener(this);
- latestItemView.setLocked(true);
+ setLocked(true);
+ setDimmed(true);
}
public NotificationOverflowIconsView getIconsView() {
return mIconsView;
}
- public void setOnActivatedListener(LatestItemView.OnActivatedListener onActivatedListener) {
- mOnActivatedListener = onActivatedListener;
- }
-
- @Override
- public void onActivated(View view) {
- if (mOnActivatedListener != null) {
- mOnActivatedListener.onActivated(this);
- }
- }
-
- @Override
- public void onReset(View view) {
- if (mOnActivatedListener != null) {
- mOnActivatedListener.onReset(this);
- }
- }
-
public NotificationActivator getActivator() {
return mActivator;
}
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 5d75801..712eec8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -23,10 +23,12 @@
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-public class NotificationPanelView extends PanelView {
+public class NotificationPanelView extends PanelView implements
+ ExpandableView.OnHeightChangedListener {
public static final boolean DEBUG_GESTURES = true;
PhoneStatusBar mStatusBar;
@@ -34,11 +36,8 @@
private View mKeyguardStatusView;
private NotificationStackScrollLayout mNotificationStackScroller;
- private int[] mTempLocation = new int[2];
- private int[] mTempChildLocation = new int[2];
- private View mNotificationParent;
private boolean mTrackingSettings;
- private float mExpandedHeight = -1;
+ private int mNotificationTopPadding;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -67,7 +66,19 @@
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
mNotificationStackScroller = (NotificationStackScrollLayout)
findViewById(R.id.notification_stack_scroller);
- mNotificationParent = findViewById(R.id.notification_container_parent);
+ mNotificationStackScroller.setOnHeightChangedListener(this);
+ mNotificationTopPadding = getResources().getDimensionPixelSize(
+ R.dimen.notifications_top_padding);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ int keyguardBottomMargin =
+ ((MarginLayoutParams) mKeyguardStatusView.getLayoutParams()).bottomMargin;
+ mNotificationStackScroller.setTopPadding(mStatusBar.isOnKeyguard()
+ ? mKeyguardStatusView.getBottom() + keyguardBottomMargin
+ : mHeader.getBottom() + mNotificationTopPadding);
}
@Override
@@ -92,18 +103,6 @@
return super.dispatchPopulateAccessibilityEvent(event);
}
- /**
- * Gets the relative position of a view on the screen in regard to this view.
- *
- * @param requestedView the view we want to find the relative position for
- * @return
- */
- private int getRelativeTop(View requestedView) {
- getLocationOnScreen(mTempLocation);
- requestedView.getLocationOnScreen(mTempChildLocation);
- return mTempChildLocation[1] - mTempLocation[1];
- }
-
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
// intercept for quick settings
@@ -164,42 +163,7 @@
@Override
protected void onHeightUpdated(float expandedHeight) {
- updateNotificationStackHeight(expandedHeight);
- }
-
- /**
- * Update the height of the {@link #mNotificationStackScroller} to the new expanded height.
- * This is much more efficient than doing it over the layout pass.
- *
- * @param expandedHeight the new expanded height
- */
- private void updateNotificationStackHeight(float expandedHeight) {
- if (mExpandedHeight == expandedHeight) return;
- mExpandedHeight = expandedHeight;
- mNotificationStackScroller.setIsExpanded(expandedHeight > 0.0f);
- float childOffset = getRelativeTop(mNotificationStackScroller)
- - mNotificationParent.getTranslationY();
- int newStackHeight = (int) (expandedHeight - childOffset);
- int itemHeight = mNotificationStackScroller.getItemHeight();
- int bottomStackPeekSize = mNotificationStackScroller.getBottomStackPeekSize();
- int minStackHeight = itemHeight + bottomStackPeekSize;
- if (newStackHeight >= minStackHeight) {
- mNotificationParent.setTranslationY(0);
- mNotificationStackScroller.setCurrentStackHeight(newStackHeight);
- } else {
-
- // We did not reach the position yet where we actually start growing,
- // so we translate the stack upwards.
- int translationY = (newStackHeight - minStackHeight);
- // A slight parallax effect is introduced in order for the stack to catch up with
- // the top card.
- float partiallyThere = (float) newStackHeight / minStackHeight;
- partiallyThere = Math.max(0, partiallyThere);
- translationY += (1 - partiallyThere) * bottomStackPeekSize;
- mNotificationParent.setTranslationY(translationY);
- mNotificationStackScroller.setCurrentStackHeight(
- (int) (expandedHeight - (childOffset + translationY)));
- }
+ mNotificationStackScroller.setStackHeight(expandedHeight);
}
@Override
@@ -218,4 +182,9 @@
super.onExpandingFinished();
mNotificationStackScroller.onExpansionStopped();
}
+
+ @Override
+ public void onHeightChanged(ExpandableView view) {
+ requestPanelHeightUpdate();
+ }
}
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 81e6b75..545352c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -247,6 +247,7 @@
private int mCarrierLabelHeight;
private TextView mEmergencyCallLabel;
private int mNotificationHeaderHeight;
+ private View mKeyguardCarrierLabel;
private boolean mShowCarrierInPanel = false;
@@ -375,6 +376,7 @@
private InterceptedNotifications mIntercepted;
private VelocityTracker mSettingsTracker;
private float mSettingsDownY;
+ private boolean mSettingsStarted;
private boolean mSettingsCancelled;
private boolean mSettingsClosing;
private int mNotificationPadding;
@@ -615,6 +617,7 @@
(NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
mKeyguardIconOverflowContainer.setOnActivatedListener(this);
+ mKeyguardCarrierLabel = mStatusBarWindow.findViewById(R.id.keyguard_carrier_text);
mStackScroller.addView(mKeyguardIconOverflowContainer);
mExpandedContents = mStackScroller;
@@ -766,16 +769,16 @@
if (mSettingsTracker != null) {
mSettingsTracker.addMovement(event);
}
-
+ final int slop = ViewConfiguration.get(mContext).getScaledTouchSlop();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mSettingsTracker = VelocityTracker.obtain();
mSettingsDownY = event.getY();
mSettingsCancelled = false;
+ mSettingsStarted = false;
mSettingsClosing = mFlipSettingsView.getVisibility() == View.VISIBLE;
- mFlipSettingsView.setVisibility(View.VISIBLE);
- mStackScroller.setVisibility(View.VISIBLE);
- positionSettings(0);
- if (!mSettingsClosing) {
+ if (mSettingsClosing) {
+ mStackScroller.setVisibility(View.VISIBLE);
+ } else {
mFlipSettingsView.setTranslationY(-mNotificationPanel.getMeasuredHeight());
}
dispatchSettingsEvent(event);
@@ -784,8 +787,7 @@
final float dy = event.getY() - mSettingsDownY;
final FlipperButton flipper = mOnKeyguard ? mKeyguardFlipper : mHeaderFlipper;
final boolean inButton = flipper.inHolderBounds(event);
- final int slop = ViewConfiguration.get(mContext).getScaledTouchSlop();
- final boolean qsTap = mSettingsClosing && Math.abs(dy) < slop;
+ final boolean qsTap = mSettingsClosing && Math.abs(dy) < slop;
if (!qsTap && !inButton) {
mSettingsTracker.computeCurrentVelocity(1000);
final float vy = mSettingsTracker.getYVelocity();
@@ -801,10 +803,9 @@
dispatchSettingsEvent(event);
} else if (mSettingsTracker != null && event.getAction() == MotionEvent.ACTION_MOVE) {
final float dy = event.getY() - mSettingsDownY;
- positionSettings(dy);
if (mSettingsClosing) {
- final boolean qsTap =
- Math.abs(dy) < ViewConfiguration.get(mContext).getScaledTouchSlop();
+ positionSettings(dy);
+ final boolean qsTap = Math.abs(dy) < slop;
if (!mSettingsCancelled && !qsTap) {
MotionEvent cancelEvent = MotionEvent.obtainNoHistory(event);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
@@ -812,6 +813,14 @@
mSettingsCancelled = true;
}
} else {
+ if (!mSettingsStarted && dy > slop) {
+ mSettingsStarted = true;
+ mFlipSettingsView.setVisibility(View.VISIBLE);
+ mStackScroller.setVisibility(View.VISIBLE);
+ }
+ if (mSettingsStarted) {
+ positionSettings(dy);
+ }
dispatchSettingsEvent(event);
}
}
@@ -1337,7 +1346,8 @@
!(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
&& mStackScroller.getHeight() < (mNotificationPanel.getHeight()
- mCarrierLabelHeight - mNotificationHeaderHeight)
- && mStackScroller.getVisibility() == View.VISIBLE;
+ && mStackScroller.getVisibility() == View.VISIBLE
+ && !mOnKeyguard;
if (force || mCarrierLabelVisible != makeVisible) {
mCarrierLabelVisible = makeVisible;
@@ -1615,9 +1625,9 @@
return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0;
}
- void makeExpandedVisible() {
+ void makeExpandedVisible(boolean force) {
if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
- if (mExpandedVisible || !panelsEnabled()) {
+ if (!force && (mExpandedVisible || !panelsEnabled())) {
return;
}
@@ -2708,8 +2718,8 @@
}
mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
- mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_row_min_height);
- mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_row_max_height);
+ mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
+ mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count);
@@ -2949,6 +2959,7 @@
mKeyguardBottomArea.setVisibility(View.VISIBLE);
mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
+ mKeyguardCarrierLabel.setVisibility(View.VISIBLE);
mNotificationPanelHeader.setVisibility(View.GONE);
mKeyguardFlipper.setVisibility(View.VISIBLE);
@@ -2957,6 +2968,7 @@
mKeyguardStatusView.setVisibility(View.GONE);
mKeyguardBottomArea.setVisibility(View.GONE);
mKeyguardIndicationTextView.setVisibility(View.GONE);
+ mKeyguardCarrierLabel.setVisibility(View.GONE);
mNotificationPanelHeader.setVisibility(View.VISIBLE);
mKeyguardFlipper.setVisibility(View.GONE);
@@ -2967,6 +2979,7 @@
updateRowStates();
checkBarModes();
updateNotificationIcons();
+ updateCarrierLabelVisibility(false);
}
public void userActivity() {
@@ -2996,8 +3009,9 @@
private void instantExpandNotificationsPanel() {
- // Make our window larger.
- mStatusBarWindowManager.setStatusBarExpanded(true);
+ // Make our window larger and the panel visible.
+ makeExpandedVisible(true);
+ mNotificationPanel.setVisibility(View.VISIBLE);
// Wait for window manager to pickup the change, so we know the maximum height of the panel
// then.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 8957a77..194774d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -168,6 +168,9 @@
if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
mSimState = IccCardConstants.State.ABSENT;
}
+ else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) {
+ mSimState = IccCardConstants.State.CARD_IO_ERROR;
+ }
else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
mSimState = IccCardConstants.State.READY;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index bf7dd5c..79c63f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -106,7 +106,7 @@
@Override
public void onPanelPeeked() {
super.onPanelPeeked();
- mBar.makeExpandedVisible();
+ mBar.makeExpandedVisible(false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index c5dae85..6b5ef5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -69,8 +69,8 @@
mStackScrollLayout = (NotificationStackScrollLayout) findViewById(
R.id.notification_stack_scroller);
mNotificationPanel = (NotificationPanelView) findViewById(R.id.notification_panel);
- int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
- int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height);
+ int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
+ int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
mExpandHelper = new ExpandHelper(getContext(), mStackScrollLayout,
minHeight, maxHeight);
mExpandHelper.setEventSource(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
index c1662ba..20011ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
@@ -212,6 +212,13 @@
}
}
+ @Override
+ protected void onDetachedFromWindow() {
+ if (mAdapter != null) {
+ mAdapter.dispose();
+ }
+ }
+
public void setAutoActivate(boolean value) {
mAutoActivate = value;
}
@@ -396,6 +403,7 @@
void setMode(boolean mode);
void select(ExitCondition ec);
void init();
+ void dispose();
void setCallbacks(Callbacks callbacks);
ExitCondition getExitCondition(int d);
int getExitConditionCount();
@@ -406,6 +414,7 @@
public String line1;
public String line2;
public String action;
+ public Object tag;
}
public interface Callbacks {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java
index c97ba8d..1bc97a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java
@@ -16,14 +16,22 @@
package com.android.systemui.statusbar.phone;
+import android.app.INotificationManager;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.provider.Settings;
+import android.service.notification.Condition;
+import android.service.notification.IConditionListener;
+import android.util.ArrayMap;
import android.util.Log;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -34,8 +42,10 @@
private final ContentResolver mResolver;
private final Handler mHandler = new Handler();
private final SettingsObserver mObserver;
- private final List<ExitCondition> mExits = Arrays.asList(
- newExit("Until you turn this off", "Until", "You turn this off"));
+ private final List<ExitCondition> mExits = new ArrayList<ExitCondition>(Arrays.asList(
+ newExit("Until you turn this off", "Until", "You turn this off", null)));
+ private final INotificationManager mNoMan;
+ private final ArrayMap<Uri, Condition> mConditions = new ArrayMap<Uri, Condition>();
private Callbacks mCallbacks;
private int mExitIndex;
@@ -45,6 +55,13 @@
mContext = context;
mResolver = mContext.getContentResolver();
mObserver = new SettingsObserver(mHandler);
+ mNoMan = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ try {
+ mNoMan.requestZenModeConditions(mListener, true /*requested*/);
+ } catch (RemoteException e) {
+ // noop
+ }
mObserver.init();
init();
}
@@ -75,6 +92,16 @@
mExitIndex = 0;
dispatchChanged();
}
+ setZenModeCondition();
+ }
+
+ @Override
+ public void dispose() {
+ try {
+ mNoMan.requestZenModeConditions(mListener, false /*requested*/);
+ } catch (RemoteException e) {
+ // noop
+ }
}
private void dispatchChanged() {
@@ -117,13 +144,28 @@
}
mExitIndex = i;
dispatchChanged();
+ setZenModeCondition();
}
- private static ExitCondition newExit(String summary, String line1, String line2) {
+ private void setZenModeCondition() {
+ if (mExitIndex < 0 || mExitIndex >= mExits.size()) {
+ Log.w(TAG, "setZenModeCondition to bad index " + mExitIndex + " of " + mExits.size());
+ return;
+ }
+ final Uri conditionUri = (Uri) mExits.get(mExitIndex).tag;
+ try {
+ mNoMan.setZenModeCondition(conditionUri);
+ } catch (RemoteException e) {
+ // noop
+ }
+ }
+
+ private static ExitCondition newExit(String summary, String line1, String line2, Object tag) {
final ExitCondition rt = new ExitCondition();
rt.summary = summary;
rt.line1 = line1;
rt.line2 = line2;
+ rt.tag = tag;
return rt;
}
@@ -168,4 +210,21 @@
return v != Settings.Global.ZEN_MODE_OFF;
}
}
+
+ private final IConditionListener mListener = new IConditionListener.Stub() {
+ @Override
+ public void onConditionsReceived(Condition[] conditions) {
+ if (conditions == null || conditions.length == 0) return;
+ for (Condition c : conditions) {
+ mConditions.put(c.id, c);
+ }
+ for (int i = mExits.size() - 1; i > 0; i--) {
+ mExits.remove(i);
+ }
+ for (Condition c : mConditions.values()) {
+ mExits.add(newExit(c.caption, "", "", c.id));
+ }
+ dispatchChanged();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index 2dba669..c94c65f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -135,8 +135,8 @@
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
mEdgeSwipeHelper = new EdgeSwipeHelper(touchSlop);
- int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
- int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height);
+ int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
+ int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
mExpandHelper = new ExpandHelper(getContext(), this, minHeight, maxHeight);
mContentHolder = (ViewGroup) findViewById(R.id.content_holder);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
deleted file mode 100644
index 266cecf..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * 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.systemui.statusbar.policy;
-
-import android.animation.LayoutTransition;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-import com.android.systemui.ExpandHelper;
-import com.android.systemui.R;
-import com.android.systemui.SwipeHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-
-import java.util.HashMap;
-
-public class NotificationRowLayout
- extends LinearLayout
- implements SwipeHelper.Callback, ExpandHelper.Callback
-{
- private static final String TAG = "NotificationRowLayout";
- private static final boolean DEBUG = false;
- private static final boolean SLOW_ANIMATIONS = DEBUG;
-
- private static final int APPEAR_ANIM_LEN = SLOW_ANIMATIONS ? 5000 : 250;
- private static final int DISAPPEAR_ANIM_LEN = APPEAR_ANIM_LEN;
-
- boolean mAnimateBounds = true;
-
- Rect mTmpRect = new Rect();
-
- HashMap<View, ValueAnimator> mAppearingViews = new HashMap<View, ValueAnimator>();
- HashMap<View, ValueAnimator> mDisappearingViews = new HashMap<View, ValueAnimator>();
-
- private SwipeHelper mSwipeHelper;
-
- private OnSizeChangedListener mOnSizeChangedListener;
-
- // Flag set during notification removal animation to avoid causing too much work until
- // animation is done
- boolean mRemoveViews = true;
-
- private LayoutTransition mRealLayoutTransition;
-
- public NotificationRowLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public NotificationRowLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- mRealLayoutTransition = new LayoutTransition();
- mRealLayoutTransition.setAnimateParentHierarchy(true);
- setLayoutTransitionsEnabled(true);
-
- setOrientation(LinearLayout.VERTICAL);
-
- if (DEBUG) {
- setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
- @Override
- public void onChildViewAdded(View parent, View child) {
- Log.d(TAG, "view added: " + child + "; new count: " + getChildCount());
- }
- @Override
- public void onChildViewRemoved(View parent, View child) {
- Log.d(TAG, "view removed: " + child + "; new count: " + (getChildCount() - 1));
- }
- });
-
- setBackgroundColor(0x80FF8000);
- }
-
- float densityScale = getResources().getDisplayMetrics().density;
- float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
- mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
- }
-
- public void setLongPressListener(View.OnLongClickListener listener) {
- mSwipeHelper.setLongPressListener(listener);
- }
-
- public void setOnSizeChangedListener(OnSizeChangedListener l) {
- mOnSizeChangedListener = l;
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- super.onWindowFocusChanged(hasWindowFocus);
- if (!hasWindowFocus) {
- mSwipeHelper.removeLongPressCallback();
- }
- }
-
- public void setAnimateBounds(boolean anim) {
- mAnimateBounds = anim;
- }
-
- private void logLayoutTransition() {
- Log.v(TAG, "layout " +
- (mRealLayoutTransition.isChangingLayout() ? "is " : "is not ") +
- "in transition and animations " +
- (mRealLayoutTransition.isRunning() ? "are " : "are not ") +
- "running.");
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
- if (DEBUG) logLayoutTransition();
-
- return mSwipeHelper.onInterceptTouchEvent(ev) ||
- super.onInterceptTouchEvent(ev);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (DEBUG) Log.v(TAG, "onTouchEvent()");
- if (DEBUG) logLayoutTransition();
-
- return mSwipeHelper.onTouchEvent(ev) ||
- super.onTouchEvent(ev);
- }
-
- public boolean canChildBeDismissed(View v) {
- final View veto = v.findViewById(R.id.veto);
- return (veto != null && veto.getVisibility() != View.GONE);
- }
-
- public boolean canChildBeExpanded(View v) {
- return v instanceof ExpandableNotificationRow
- && ((ExpandableNotificationRow) v).isExpandable();
- }
-
- public void setUserExpandedChild(View v, boolean userExpanded) {
- if (v instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) v).setUserExpanded(userExpanded);
- }
- }
-
- public void setUserLockedChild(View v, boolean userLocked) {
- if (v instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) v).setUserLocked(userLocked);
- }
- }
-
- public void onChildDismissed(View v) {
- if (DEBUG) Log.v(TAG, "onChildDismissed: " + v + " mRemoveViews=" + mRemoveViews);
- final View veto = v.findViewById(R.id.veto);
- if (veto != null && veto.getVisibility() != View.GONE && mRemoveViews) {
- veto.performClick();
- }
- }
-
- public void onBeginDrag(View v) {
- // We need to prevent the surrounding ScrollView from intercepting us now;
- // the scroll position will be locked while we swipe
- requestDisallowInterceptTouchEvent(true);
- }
-
- public void onDragCancelled(View v) {
- }
-
- public View getChildAtPosition(MotionEvent ev) {
- return getChildAtPosition(ev.getX(), ev.getY());
- }
-
- public View getChildAtRawPosition(float touchX, float touchY) {
- int[] location = new int[2];
- getLocationOnScreen(location);
- return getChildAtPosition((float) (touchX - location[0]), (float) (touchY - location[1]));
- }
-
- public View getChildAtPosition(float touchX, float touchY) {
- // find the view under the pointer, accounting for GONE views
- final int count = getChildCount();
- int y = 0;
- int childIdx = 0;
- View slidingChild;
- for (; childIdx < count; childIdx++) {
- slidingChild = getChildAt(childIdx);
- if (slidingChild.getVisibility() == GONE) {
- continue;
- }
- y += slidingChild.getMeasuredHeight();
- if (touchY < y) return slidingChild;
- }
- return null;
- }
-
- public View getChildContentView(View v) {
- return v;
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- float densityScale = getResources().getDisplayMetrics().density;
- mSwipeHelper.setDensityScale(densityScale);
- float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
- mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
- }
-
-
- /**
- * Sets a flag to tell us whether to actually remove views. Removal is delayed by setting this
- * to false during some animations to smooth out performance. Callers should restore the
- * flag to true after the animation is done, and then they should make sure that the views
- * get removed properly.
- */
- public void setViewRemoval(boolean removeViews) {
- if (DEBUG) Log.v(TAG, "setViewRemoval: " + removeViews);
- mRemoveViews = removeViews;
- }
-
- // Suppress layout transitions for a little while.
- public void setLayoutTransitionsEnabled(boolean b) {
- if (b) {
- setLayoutTransition(mRealLayoutTransition);
- } else {
- if (mRealLayoutTransition.isRunning()) {
- mRealLayoutTransition.cancel();
- }
- setLayoutTransition(null);
- }
- }
-
- public void dismissRowAnimated(View child) {
- dismissRowAnimated(child, 0);
- }
-
- public void dismissRowAnimated(View child, int vel) {
- mSwipeHelper.dismissChild(child, vel);
- }
-
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
- if (DEBUG) setWillNotDraw(false);
- }
-
- @Override
- public void onDraw(android.graphics.Canvas c) {
- super.onDraw(c);
- if (DEBUG) logLayoutTransition();
- if (DEBUG) {
- //Log.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
- // + getMeasuredHeight() + "px");
- c.save();
- c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
- android.graphics.Region.Op.DIFFERENCE);
- c.drawColor(0xFFFF8000);
- c.restore();
- }
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- if (mOnSizeChangedListener != null) {
- mOnSizeChangedListener.onSizeChanged(this, w, h, oldw, oldh);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 36d94a9..9a43e37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -31,20 +31,25 @@
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.animation.AnimationUtils;
import android.widget.OverScroller;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
import com.android.systemui.statusbar.policy.ScrollAdapter;
+import java.util.ArrayList;
+
/**
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
*/
public class NotificationStackScrollLayout extends ViewGroup
- implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter {
+ implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
+ ExpandableView.OnHeightChangedListener {
private static final String TAG = "NotificationStackScrollLayout";
private static final boolean DEBUG = false;
@@ -78,6 +83,8 @@
private int mBottomStackPeekSize;
private int mEmptyMarginBottom;
private int mPaddingBetweenElements;
+ private int mTopPadding;
+ private boolean mListenForHeightChanges = true;
/**
* The algorithm which calculates the properties for our children
@@ -87,9 +94,28 @@
/**
* The current State this Layout is in
*/
- private final StackScrollState mCurrentStackScrollState = new StackScrollState(this);
+ private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
+ private ArrayList<View> mChildrenToAddAnimated = new ArrayList<View>();
+ private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>();
+ private ArrayList<ChildHierarchyChangeEvent> mAnimationEvents
+ = new ArrayList<ChildHierarchyChangeEvent>();
+ private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
+ private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
private OnChildLocationsChangedListener mListener;
+ private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
+ private boolean mChildHierarchyDirty;
+ private boolean mIsExpanded = true;
+ private ViewTreeObserver.OnPreDrawListener mAfterLayoutPreDrawListener
+ = new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ updateScrollPositionIfNecessary();
+ updateChildren();
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ return true;
+ }
+ };
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -145,13 +171,13 @@
mSidePaddings = context.getResources()
.getDimensionPixelSize(R.dimen.notification_side_padding);
mCollapsedSize = context.getResources()
- .getDimensionPixelSize(R.dimen.notification_row_min_height);
+ .getDimensionPixelSize(R.dimen.notification_min_height);
mBottomStackPeekSize = context.getResources()
.getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
mEmptyMarginBottom = context.getResources().getDimensionPixelSize(
R.dimen.notification_stack_margin_bottom);
- // currently the padding is in the elements themself
- mPaddingBetweenElements = 0;
+ mPaddingBetweenElements = context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_padding);
mStackScrollAlgorithm = new StackScrollAlgorithm(context);
}
@@ -180,16 +206,7 @@
}
setMaxLayoutHeight(getHeight() - mEmptyMarginBottom);
updateContentHeight();
- getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- updateScrollPositionIfNecessary();
- updateChildren();
- getViewTreeObserver().removeOnPreDrawListener(this);
- return true;
- }
- });
-
+ getViewTreeObserver().addOnPreDrawListener(mAfterLayoutPreDrawListener);
}
public void setChildLocationsChangedListener(OnChildLocationsChangedListener listener) {
@@ -212,11 +229,12 @@
private void setMaxLayoutHeight(int maxLayoutHeight) {
mMaxLayoutHeight = maxLayoutHeight;
- updateAlgorithmHeight();
+ updateAlgorithmHeightAndPadding();
}
- private void updateAlgorithmHeight() {
+ private void updateAlgorithmHeightAndPadding() {
mStackScrollAlgorithm.setLayoutHeight(getLayoutHeight());
+ mStackScrollAlgorithm.setTopPadding(mTopPadding);
}
/**
@@ -224,20 +242,20 @@
* modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
*/
private void updateChildren() {
- if (!isCurrentlyAnimating()) {
- mCurrentStackScrollState.setScrollY(mOwnScrollY);
- mStackScrollAlgorithm.getStackScrollState(mCurrentStackScrollState);
- mCurrentStackScrollState.apply();
+ mCurrentStackScrollState.setScrollY(mOwnScrollY);
+ mStackScrollAlgorithm.getStackScrollState(mCurrentStackScrollState);
+ if (!isCurrentlyAnimating() && !mChildHierarchyDirty) {
+ applyCurrentState();
if (mListener != null) {
mListener.onChildLocationsChanged(this);
}
} else {
- // TODO: handle animation
+ startAnimationToState(mCurrentStackScrollState);
}
}
private boolean isCurrentlyAnimating() {
- return false;
+ return mStateAnimator.isRunning();
}
private void updateScrollPositionIfNecessary() {
@@ -247,10 +265,52 @@
}
}
- public void setCurrentStackHeight(int currentStackHeight) {
- this.mCurrentStackHeight = currentStackHeight;
- updateAlgorithmHeight();
- updateChildren();
+ public int getTopPadding() {
+ return mTopPadding;
+ }
+
+ public void setTopPadding(int topPadding) {
+ if (mTopPadding != topPadding) {
+ mTopPadding = topPadding;
+ updateAlgorithmHeightAndPadding();
+ updateContentHeight();
+ updateChildren();
+ }
+ }
+
+ /**
+ * Update the height of the stack to a new height.
+ *
+ * @param height the new height of the stack
+ */
+ public void setStackHeight(float height) {
+ setIsExpanded(height > 0.0f);
+ int newStackHeight = (int) height;
+ int itemHeight = getItemHeight();
+ int bottomStackPeekSize = mBottomStackPeekSize;
+ int minStackHeight = itemHeight + bottomStackPeekSize;
+ int stackHeight;
+ if (newStackHeight - mTopPadding >= minStackHeight) {
+ setTranslationY(0);
+ stackHeight = newStackHeight;
+ } else {
+
+ // We did not reach the position yet where we actually start growing,
+ // so we translate the stack upwards.
+ int translationY = (newStackHeight - minStackHeight);
+ // A slight parallax effect is introduced in order for the stack to catch up with
+ // the top card.
+ float partiallyThere = (float) (newStackHeight - mTopPadding) / minStackHeight;
+ partiallyThere = Math.max(0, partiallyThere);
+ translationY += (1 - partiallyThere) * bottomStackPeekSize;
+ setTranslationY(translationY - mTopPadding);
+ stackHeight = (int) (height - (translationY - mTopPadding));
+ }
+ if (stackHeight != mCurrentStackHeight) {
+ mCurrentStackHeight = stackHeight;
+ updateAlgorithmHeightAndPadding();
+ updateChildren();
+ }
}
/**
@@ -282,6 +342,7 @@
veto.performClick();
}
setSwipingInProgress(false);
+ mSwipedOutViews.add(v);
}
public void onBeginDrag(View v) {
@@ -306,12 +367,12 @@
// find the view under the pointer, accounting for GONE views
final int count = getChildCount();
for (int childIdx = 0; childIdx < count; childIdx++) {
- View slidingChild = getChildAt(childIdx);
+ ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
if (slidingChild.getVisibility() == GONE) {
continue;
}
float top = slidingChild.getTranslationY();
- float bottom = top + slidingChild.getHeight();
+ float bottom = top + slidingChild.getActualHeight();
int left = slidingChild.getLeft();
int right = slidingChild.getRight();
@@ -615,16 +676,11 @@
private int getScrollRange() {
int scrollRange = 0;
- View firstChild = getFirstChildNotGone();
+ ExpandableView firstChild = (ExpandableView) getFirstChildNotGone();
if (firstChild != null) {
int contentHeight = getContentHeight();
int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild);
- int firstChildExpandPotential = firstChildMaxExpandHeight - firstChild.getHeight();
- // If we already scrolled in, the first child is layouted smaller than it actually
- // could be when expanded. We have to compensate for this loss of the contentHeight
- // by adding the expand potential again.
- contentHeight += firstChildExpandPotential;
scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize);
if (scrollRange > 0 && getChildCount() > 0) {
// We want to at least be able collapse the first item and not ending in a weird
@@ -666,13 +722,20 @@
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
- height += child.getHeight();
- if (i < getChildCount()-1) {
+ if (height != 0) {
+ // add the padding before this element
height += mPaddingBetweenElements;
}
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ height += row.getMaximumAllowedExpandHeight();
+ } else if (child instanceof ExpandableView) {
+ ExpandableView expandableView = (ExpandableView) child;
+ height += expandableView.getActualHeight();
+ }
}
}
- mContentHeight = height;
+ mContentHeight = height + mTopPadding;
}
/**
@@ -723,14 +786,118 @@
@Override
protected void onViewRemoved(View child) {
super.onViewRemoved(child);
+ ((ExpandableView) child).setOnHeightChangedListener(null);
mCurrentStackScrollState.removeViewStateForView(child);
mStackScrollAlgorithm.notifyChildrenChanged(this);
+ updateScrollStateForRemovedChild(child);
+ if (mIsExpanded) {
+
+ // Generate Animations
+ mChildrenToRemoveAnimated.add(child);
+ mChildHierarchyDirty = true;
+ }
+ }
+
+ /**
+ * Updates the scroll position when a child was removed
+ *
+ * @param removedChild the removed child
+ */
+ private void updateScrollStateForRemovedChild(View removedChild) {
+ int startingPosition = getPositionInLinearLayout(removedChild);
+ int childHeight = removedChild.getHeight() + mPaddingBetweenElements;
+ int endPosition = startingPosition + childHeight;
+ if (endPosition <= mOwnScrollY) {
+ // This child is fully scrolled of the top, so we have to deduct its height from the
+ // scrollPosition
+ mOwnScrollY -= childHeight;
+ } else if (startingPosition < mOwnScrollY) {
+ // This child is currently being scrolled into, set the scroll position to the start of
+ // this child
+ mOwnScrollY = startingPosition;
+ }
+ }
+
+ private int getPositionInLinearLayout(View requestedChild) {
+ int position = 0;
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child == requestedChild) {
+ return position;
+ }
+ if (child.getVisibility() != View.GONE) {
+ position += child.getHeight();
+ if (i < getChildCount()-1) {
+ position += mPaddingBetweenElements;
+ }
+ }
+ }
+ return 0;
}
@Override
protected void onViewAdded(View child) {
super.onViewAdded(child);
mStackScrollAlgorithm.notifyChildrenChanged(this);
+ ((ExpandableView) child).setOnHeightChangedListener(this);
+ if (child.getVisibility() != View.GONE) {
+ generateAddAnimation(child);
+ }
+ }
+
+ public void generateAddAnimation(View child) {
+ if (mIsExpanded) {
+
+ // Generate Animations
+ mChildrenToAddAnimated.add(child);
+ mChildHierarchyDirty = true;
+ }
+ }
+
+ /**
+ * Change the position of child to a new location
+ *
+ * @param child the view to change the position for
+ * @param newIndex the new index
+ */
+ public void changeViewPosition(View child, int newIndex) {
+ if (child != null && child.getParent() == this) {
+ // TODO: handle this
+ }
+ }
+
+ private void startAnimationToState(StackScrollState finalState) {
+ if (mChildHierarchyDirty) {
+ generateChildHierarchyEvents();
+ mChildHierarchyDirty = false;
+ }
+ mStateAnimator.startAnimationForEvents(mAnimationEvents, finalState);
+ }
+
+ private void generateChildHierarchyEvents() {
+ generateChildAdditionEvents();
+ generateChildRemovalEvents();
+ mChildHierarchyDirty = false;
+ }
+
+ private void generateChildRemovalEvents() {
+ for (View child : mChildrenToRemoveAnimated) {
+ boolean childWasSwipedOut = mSwipedOutViews.contains(child);
+ int animationType = childWasSwipedOut
+ ? ChildHierarchyChangeEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
+ : ChildHierarchyChangeEvent.ANIMATION_TYPE_REMOVE;
+ mAnimationEvents.add(new ChildHierarchyChangeEvent(child, animationType));
+ }
+ mSwipedOutViews.clear();
+ mChildrenToRemoveAnimated.clear();
+ }
+
+ private void generateChildAdditionEvents() {
+ for (View child : mChildrenToAddAnimated) {
+ mAnimationEvents.add(new ChildHierarchyChangeEvent(child,
+ ChildHierarchyChangeEvent.ANIMATION_TYPE_ADD));
+ }
+ mChildrenToAddAnimated.clear();
}
private boolean onInterceptTouchEventScroll(MotionEvent ev) {
@@ -884,17 +1051,63 @@
mStackScrollAlgorithm.onExpansionStopped();
}
- public void setIsExpanded(boolean isExpanded) {
+ private void setIsExpanded(boolean isExpanded) {
+ mIsExpanded = isExpanded;
mStackScrollAlgorithm.setIsExpanded(isExpanded);
if (!isExpanded) {
mOwnScrollY = 0;
}
}
+ @Override
+ public void onHeightChanged(ExpandableView view) {
+ if (mListenForHeightChanges && !isCurrentlyAnimating()) {
+ updateContentHeight();
+ updateScrollPositionIfNecessary();
+ if (mOnHeightChangedListener != null) {
+ mOnHeightChangedListener.onHeightChanged(view);
+ }
+ updateChildren();
+ }
+ }
+
+ public void setOnHeightChangedListener(
+ ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
+ this.mOnHeightChangedListener = mOnHeightChangedListener;
+ }
+
+ public void onChildAnimationFinished() {
+ applyCurrentState();
+ mAnimationEvents.clear();
+ }
+
+ private void applyCurrentState() {
+ mListenForHeightChanges = false;
+ mCurrentStackScrollState.apply();
+ mListenForHeightChanges = true;
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
public interface OnChildLocationsChangedListener {
public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout);
}
+
+ static class ChildHierarchyChangeEvent {
+
+ static int ANIMATION_TYPE_ADD = 1;
+ static int ANIMATION_TYPE_REMOVE = 2;
+ static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 3;
+ final long eventStartTime;
+ final View changingView;
+ final int animationType;
+
+ ChildHierarchyChangeEvent(View view, int type) {
+ eventStartTime = AnimationUtils.currentAnimationTimeMillis();
+ changingView = view;
+ animationType = type;
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index d9e7f66..9bde673 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -23,6 +23,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.ExpandableView;
import java.util.ArrayList;
@@ -49,11 +50,15 @@
private StackIndentationFunctor mBottomStackIndentationFunctor;
private int mLayoutHeight;
+
+ /** mLayoutHeight - mTopPadding */
+ private int mInnerHeight;
+ private int mTopPadding;
private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
private boolean mIsExpansionChanging;
private int mFirstChildMaxHeight;
private boolean mIsExpanded;
- private View mFirstChildWhileExpanding;
+ private ExpandableView mFirstChildWhileExpanding;
private boolean mExpandedOnStart;
private int mTopStackTotalSize;
@@ -62,11 +67,10 @@
}
private void initConstants(Context context) {
-
- // currently the padding is in the elements themself
- mPaddingBetweenElements = 0;
+ mPaddingBetweenElements = context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_padding);
mCollapsedSize = context.getResources()
- .getDimensionPixelSize(R.dimen.notification_row_min_height);
+ .getDimensionPixelSize(R.dimen.notification_min_height);
mTopStackPeekSize = context.getResources()
.getDimensionPixelSize(R.dimen.top_stack_peek_amount);
mBottomStackPeekSize = context.getResources()
@@ -83,7 +87,7 @@
mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
MAX_ITEMS_IN_BOTTOM_STACK,
mBottomStackPeekSize,
- mCollapsedSize + mPaddingBetweenElements + mBottomStackPeekSize,
+ mCollapsedSize + mBottomStackPeekSize + mPaddingBetweenElements,
0.5f);
}
@@ -102,11 +106,10 @@
algorithmState.scrolledPixelsTop = 0;
algorithmState.itemsInBottomStack = 0.0f;
algorithmState.partialInBottom = 0.0f;
+ algorithmState.scrollY = resultState.getScrollY() + mCollapsedSize;
updateVisibleChildren(resultState, algorithmState);
- algorithmState.scrollY = getAlgorithmScrollPosition(resultState, algorithmState);
-
// Phase 1:
findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState);
@@ -118,42 +121,6 @@
}
/**
- * Calculates the scroll offset of the algorithm, based on the resultState.
- *
- * @param resultState the state to base the calculation on
- * @param algorithmState The state in which the current pass of the algorithm is currently in
- * @return the scroll offset used for the algorithm
- */
- private int getAlgorithmScrollPosition(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState) {
-
- int resultScroll = resultState.getScrollY() + mCollapsedSize;
-
- // If the first child was collapsed in an earlier pass, we have to decrease the scroll
- // position to get into the same state again.
- if (algorithmState.visibleChildren.size() > 0) {
- View firstView = algorithmState.visibleChildren.get(0);
- if (firstView instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow firstRow = (ExpandableNotificationRow) firstView;
- if (firstRow.isUserLocked()) {
- // User is currently modifying this height.
- return resultScroll;
- }
- int scrolledInAmount = 0;
- // If the child size was not decreased due to scrolling, we don't substract it,
- if (!mIsExpansionChanging) {
- scrolledInAmount = firstRow.getExpandPotential();
- } else if (mExpandedOnStart && mFirstChildWhileExpanding == firstView) {
- scrolledInAmount = firstRow.getMaximumAllowedExpandHeight() -
- mFirstChildMaxHeight;
- }
- resultScroll -= scrolledInAmount;
- }
- }
- return resultScroll;
- }
-
- /**
* Update the visible children on the state.
*/
private void updateVisibleChildren(StackScrollState resultState,
@@ -163,7 +130,7 @@
state.visibleChildren.clear();
state.visibleChildren.ensureCapacity(childCount);
for (int i = 0; i < childCount; i++) {
- View v = hostView.getChildAt(i);
+ ExpandableView v = (ExpandableView) hostView.getChildAt(i);
if (v.getVisibility() != View.GONE) {
state.visibleChildren.add(v);
}
@@ -181,7 +148,7 @@
StackScrollAlgorithmState algorithmState) {
// The starting position of the bottom stack peek
- float bottomPeekStart = mLayoutHeight - mBottomStackPeekSize;
+ float bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
// The position where the bottom stack starts.
float bottomStackStart = bottomPeekStart - mCollapsedSize;
@@ -195,10 +162,10 @@
int childCount = algorithmState.visibleChildren.size();
int numberOfElementsCompletelyIn = (int) algorithmState.itemsInTopStack;
for (int i = 0; i < childCount; i++) {
- View child = algorithmState.visibleChildren.get(i);
+ ExpandableView child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
childViewState.location = StackScrollState.ViewState.LOCATION_UNKNOWN;
- int childHeight = child.getHeight();
+ int childHeight = getMaxAllowedChildHeight(child);
float yPositionInScrollViewAfterElement = yPositionInScrollView
+ childHeight
+ mPaddingBetweenElements;
@@ -263,6 +230,8 @@
}
currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
yPositionInScrollView = yPositionInScrollViewAfterElement;
+
+ childViewState.yTranslation += mTopPadding;
}
}
@@ -287,12 +256,12 @@
private void clampPositionToBottomStackStart(StackScrollState.ViewState childViewState,
int childHeight) {
childViewState.yTranslation = Math.min(childViewState.yTranslation,
- mLayoutHeight - mBottomStackPeekSize - childHeight);
+ mInnerHeight - mBottomStackPeekSize - childHeight);
}
/**
* Clamp the yTranslation of the child up such that its end is at lest on the end of the top
- * stack.
+ * stack.get
*
* @param childViewState the view state of the child
* @param childHeight the height of this child
@@ -307,8 +276,11 @@
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
return row.getMaximumAllowedExpandHeight();
+ } else if (child instanceof ExpandableView) {
+ ExpandableView expandableView = (ExpandableView) child;
+ return expandableView.getActualHeight();
}
- return child.getHeight();
+ return child == null? mCollapsedSize : child.getHeight();
}
private void updateStateForChildTransitioningInBottom(StackScrollAlgorithmState algorithmState,
@@ -323,7 +295,8 @@
// the offset starting at the transitionPosition of the bottom stack
float offset = mBottomStackIndentationFunctor.getValue(algorithmState.partialInBottom);
algorithmState.itemsInBottomStack += algorithmState.partialInBottom;
- childViewState.yTranslation = transitioningPositionStart + offset - childHeight;
+ childViewState.yTranslation = transitioningPositionStart + offset - childHeight
+ - mPaddingBetweenElements;
// We want at least to be at the end of the top stack when collapsing
clampPositionToTopStackEnd(childViewState, childHeight);
@@ -339,7 +312,8 @@
if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) {
// We are visually entering the bottom stack
currentYPosition = transitioningPositionStart
- + mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack);
+ + mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack)
+ - mPaddingBetweenElements;
childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_PEEKING;
} else {
// we are fully inside the stack
@@ -350,7 +324,7 @@
childViewState.alpha = 1.0f - algorithmState.partialInBottom;
}
childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_HIDDEN;
- currentYPosition = mLayoutHeight;
+ currentYPosition = mInnerHeight;
}
childViewState.yTranslation = currentYPosition - childHeight;
clampPositionToTopStackEnd(childViewState, childHeight);
@@ -418,9 +392,9 @@
// find the number of elements in the top stack.
for (int i = 0; i < childCount; i++) {
- View child = algorithmState.visibleChildren.get(i);
+ ExpandableView child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
- int childHeight = child.getHeight();
+ int childHeight = getMaxAllowedChildHeight(child);
float yPositionInScrollViewAfterElement = yPositionInScrollView
+ childHeight
+ mPaddingBetweenElements;
@@ -428,7 +402,7 @@
if (i == 0 && algorithmState.scrollY == mCollapsedSize) {
// The starting position of the bottom stack peek
- int bottomPeekStart = mLayoutHeight - mBottomStackPeekSize;
+ int bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
// Collapse and expand the first child while the shade is being expanded
float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
? mFirstChildMaxHeight
@@ -507,12 +481,18 @@
}
}
- public int getLayoutHeight() {
- return mLayoutHeight;
- }
-
public void setLayoutHeight(int layoutHeight) {
this.mLayoutHeight = layoutHeight;
+ updateInnerHeight();
+ }
+
+ public void setTopPadding(int topPadding) {
+ mTopPadding = topPadding;
+ updateInnerHeight();
+ }
+
+ private void updateInnerHeight() {
+ mInnerHeight = mLayoutHeight - mTopPadding;
}
public void onExpansionStarted(StackScrollState currentState) {
@@ -523,13 +503,13 @@
}
private void updateFirstChildHeightWhileExpanding(ViewGroup hostView) {
- mFirstChildWhileExpanding = findFirstVisibleChild(hostView);
+ mFirstChildWhileExpanding = (ExpandableView) findFirstVisibleChild(hostView);
if (mFirstChildWhileExpanding != null) {
if (mExpandedOnStart) {
// We are collapsing the shade, so the first child can get as most as high as the
// current height.
- mFirstChildMaxHeight = mFirstChildWhileExpanding.getHeight();
+ mFirstChildMaxHeight = mFirstChildWhileExpanding.getActualHeight();
} else {
// We are expanding the shade, expand it to its full height.
@@ -542,9 +522,13 @@
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight,
int oldBottom) {
- mFirstChildMaxHeight = getMaxAllowedChildHeight(
- mFirstChildWhileExpanding);
- mFirstChildWhileExpanding.removeOnLayoutChangeListener(this);
+ if (mFirstChildWhileExpanding != null) {
+ mFirstChildMaxHeight = getMaxAllowedChildHeight(
+ mFirstChildWhileExpanding);
+ } else {
+ mFirstChildMaxHeight = 0;
+ }
+ v.removeOnLayoutChangeListener(this);
}
});
} else {
@@ -622,7 +606,7 @@
/**
* The children from the host view which are not gone.
*/
- public final ArrayList<View> visibleChildren = new ArrayList<View>();
+ public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 9742e65..8c75adc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -22,7 +22,7 @@
import android.view.View;
import android.view.ViewGroup;
-import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableView;
import java.util.HashMap;
import java.util.Map;
@@ -36,12 +36,11 @@
private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
private final ViewGroup mHostView;
- private Map<View, ViewState> mStateMap;
+ private Map<ExpandableView, ViewState> mStateMap;
private int mScrollY;
private final Rect mClipRect = new Rect();
private int mBackgroundRoundedRectCornerRadius;
private final Outline mChildOutline = new Outline();
- private final int mChildDividerHeight;
public int getScrollY() {
return mScrollY;
@@ -53,11 +52,9 @@
public StackScrollState(ViewGroup hostView) {
mHostView = hostView;
- mStateMap = new HashMap<View, ViewState>();
+ mStateMap = new HashMap<ExpandableView, ViewState>();
mBackgroundRoundedRectCornerRadius = hostView.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
- mChildDividerHeight = hostView.getResources().getDimensionPixelSize(R.dimen
- .notification_divider_height);
}
public ViewGroup getHostView() {
@@ -67,20 +64,19 @@
public void resetViewStates() {
int numChildren = mHostView.getChildCount();
for (int i = 0; i < numChildren; i++) {
- View child = mHostView.getChildAt(i);
+ ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
ViewState viewState = mStateMap.get(child);
if (viewState == null) {
viewState = new ViewState();
mStateMap.put(child, viewState);
}
// initialize with the default values of the view
- viewState.height = child.getHeight();
- viewState.alpha = 1;
+ viewState.height = child.getActualHeight();
viewState.gone = child.getVisibility() == View.GONE;
+ viewState.alpha = 1;
}
}
-
public ViewState getViewStateForView(View requestedView) {
return mStateMap.get(requestedView);
}
@@ -98,7 +94,7 @@
float previousNotificationEnd = 0;
float previousNotificationStart = 0;
for (int i = 0; i < numChildren; i++) {
- View child = mHostView.getChildAt(i);
+ ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
ViewState state = mStateMap.get(child);
if (state == null) {
Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
@@ -109,7 +105,7 @@
float alpha = child.getAlpha();
float yTranslation = child.getTranslationY();
float zTranslation = child.getTranslationZ();
- int height = child.getHeight();
+ int height = child.getActualHeight();
float newAlpha = state.alpha;
float newYTranslation = state.yTranslation;
float newZTranslation = state.zTranslation;
@@ -152,14 +148,14 @@
// apply height
if (height != newHeight) {
- applyNewHeight(child, newHeight);
+ child.setActualHeight(newHeight);
}
// apply clipping and shadow
float newNotificationEnd = newYTranslation + newHeight;
- updateChildClippingAndShadow(child, newHeight,
- newNotificationEnd - (previousNotificationEnd - mChildDividerHeight),
- newHeight - (previousNotificationStart - newYTranslation));
+ updateChildClippingAndBackground(child, newHeight,
+ newNotificationEnd - (previousNotificationEnd),
+ (int) (newHeight - (previousNotificationStart - newYTranslation)));
previousNotificationStart = newYTranslation;
previousNotificationEnd = newNotificationEnd;
@@ -173,20 +169,21 @@
* @param child the view to update
* @param realHeight the currently applied height of the view
* @param clipHeight the desired clip height, the rest of the view will be clipped from the top
- * @param shadowHeight the desired height of the shadow, the shadow ends on the bottom
+ * @param backgroundHeight the desired background height. The shadows of the view will be
+ * based on this height and the content will be clipped from the top
*/
- private void updateChildClippingAndShadow(View child, int realHeight, float clipHeight,
- float shadowHeight) {
- if (realHeight > shadowHeight) {
- updateChildOutline(child, realHeight, shadowHeight);
- } else {
- updateChildOutline(child, realHeight, realHeight);
- }
+ private void updateChildClippingAndBackground(ExpandableView child, int realHeight,
+ float clipHeight, int backgroundHeight) {
if (realHeight > clipHeight) {
updateChildClip(child, realHeight, clipHeight);
} else {
child.setClipBounds(null);
}
+ if (realHeight > backgroundHeight) {
+ child.setClipTopAmount(realHeight - backgroundHeight);
+ } else {
+ child.setClipTopAmount(0);
+ }
}
/**
@@ -197,58 +194,14 @@
* @param clipHeight the desired clip height, the rest of the view will be clipped from the top
*/
private void updateChildClip(View child, int height, float clipHeight) {
- // The children currently have paddings inside themselfs because of the expansion
- // visualization. In order for the clipping to work correctly we have to set the correct
- // clip rect on the child.
- View container = child.findViewById(R.id.container);
- if (container != null) {
- int clipInset = (int) (height - clipHeight);
- mClipRect.set(0,
- clipInset,
- child.getWidth(),
- height);
- child.setClipBounds(mClipRect);
- }
+ int clipInset = (int) (height - clipHeight);
+ mClipRect.set(0,
+ clipInset,
+ child.getWidth(),
+ height);
+ child.setClipBounds(mClipRect);
}
- /**
- * Updates the outline of a view
- *
- * @param child the view to update
- * @param height the currently applied height of the view
- * @param outlineHeight the desired height of the outline, the outline ends on the bottom
- */
- private void updateChildOutline(View child,
- int height,
- float outlineHeight) {
- // The children currently have paddings inside themselfs because of the expansion
- // visualization. In order for the shadows to work correctly we have to set the correct
- // outline on the child.
- View container = child.findViewById(R.id.container);
- if (container != null) {
- int shadowInset = (int) (height - outlineHeight);
- getOutlineForSize(container.getLeft(),
- container.getTop() + shadowInset,
- container.getWidth(),
- container.getHeight() - shadowInset,
- mChildOutline);
- child.setOutline(mChildOutline);
- }
- }
-
- private void getOutlineForSize(int leftInset, int topInset, int width, int height,
- Outline result) {
- result.setRoundRect(leftInset, topInset, leftInset + width, topInset + height,
- mBackgroundRoundedRectCornerRadius);
- }
-
- private void applyNewHeight(View child, int newHeight) {
- ViewGroup.LayoutParams lp = child.getLayoutParams();
- lp.height = newHeight;
- child.setLayoutParams(lp);
- }
-
-
public static class ViewState {
// These are flags such that we can create masks for filtering.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
new file mode 100644
index 0000000..24daa4f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.stack;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import com.android.systemui.statusbar.ExpandableView;
+
+import java.util.ArrayList;
+
+/**
+ * An stack state animator which handles animations to new StackScrollStates
+ */
+public class StackStateAnimator {
+
+ private static final int ANIMATION_DURATION = 360;
+
+ private final Interpolator mFastOutSlowInInterpolator;
+ public NotificationStackScrollLayout mHostLayout;
+ private boolean mAnimationIsRunning;
+ private ArrayList<NotificationStackScrollLayout.ChildHierarchyChangeEvent> mHandledEvents =
+ new ArrayList<>();
+
+ public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
+ mHostLayout = hostLayout;
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
+ android.R.interpolator.fast_out_slow_in);
+ }
+
+ public boolean isRunning() {
+ return mAnimationIsRunning;
+ }
+
+ public void startAnimationForEvents(
+ ArrayList<NotificationStackScrollLayout.ChildHierarchyChangeEvent> mAnimationEvents,
+ StackScrollState finalState) {
+ int numEvents = mAnimationEvents.size();
+ if (numEvents == 0) {
+ // No events, so we don't perform any animation
+ return;
+ }
+ long lastEventStartTime = mAnimationEvents.get(numEvents - 1).eventStartTime;
+ long eventEnd = lastEventStartTime + ANIMATION_DURATION;
+ long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ long newDuration = eventEnd - currentTime;
+ if (newDuration <= 0) {
+ // last event is long before this, so we don't do anything
+ return;
+ }
+ initializeAddedViewStates(mAnimationEvents, finalState);
+ int childCount = mHostLayout.getChildCount();
+ boolean isFirstAnimatingView = true;
+ for (int i = 0; i < childCount; i++) {
+ final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
+ StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
+ if (viewState == null) {
+ continue;
+ }
+ int childVisibility = child.getVisibility();
+ boolean wasVisible = childVisibility == View.VISIBLE;
+ final float alpha = viewState.alpha;
+ if (!wasVisible && alpha != 0 && !viewState.gone) {
+ child.setVisibility(View.VISIBLE);
+ }
+
+ startPropertyAnimation(newDuration, isFirstAnimatingView, child, viewState, alpha);
+
+ // TODO: animate clipBounds
+ child.setClipBounds(null);
+ int currentHeigth = child.getActualHeight();
+ if (viewState.height != currentHeigth) {
+ startHeightAnimation(newDuration, child, viewState, currentHeigth);
+ }
+ isFirstAnimatingView = false;
+ }
+ mAnimationIsRunning = true;
+ }
+
+ private void startPropertyAnimation(long newDuration, final boolean hasFinishAction,
+ final ExpandableView child, StackScrollState.ViewState viewState, final float alpha) {
+ child.animate().setInterpolator(mFastOutSlowInInterpolator)
+ .alpha(alpha)
+ .translationY(viewState.yTranslation)
+ .translationZ(viewState.zTranslation)
+ .setDuration(newDuration)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mAnimationIsRunning = false;
+ if (hasFinishAction) {
+ mHandledEvents.clear();
+ mHostLayout.onChildAnimationFinished();
+ }
+ if (alpha == 0) {
+ child.setVisibility(View.INVISIBLE);
+ }
+ }
+ });
+ }
+
+ private void startHeightAnimation(long newDuration, final ExpandableView child,
+ StackScrollState.ViewState viewState, int currentHeigth) {
+ ValueAnimator heightAnimator = ValueAnimator.ofInt(currentHeigth, viewState.height);
+ heightAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ heightAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ child.setActualHeight((int) animation.getAnimatedValue());
+ }
+ });
+ heightAnimator.setDuration(newDuration);
+ heightAnimator.start();
+ }
+
+ /**
+ * Initialize the viewStates for the added children
+ *
+ * @param animationEvents the animation events who contain the added children
+ * @param finalState the final state to animate to
+ */
+ private void initializeAddedViewStates(
+ ArrayList<NotificationStackScrollLayout.ChildHierarchyChangeEvent> animationEvents,
+ StackScrollState finalState) {
+ for (NotificationStackScrollLayout.ChildHierarchyChangeEvent event: animationEvents) {
+ View changingView = event.changingView;
+ if (event.animationType == NotificationStackScrollLayout.ChildHierarchyChangeEvent
+ .ANIMATION_TYPE_ADD && !mHandledEvents.contains(event)) {
+
+ // This item is added, initialize it's properties.
+ StackScrollState.ViewState viewState = finalState.getViewStateForView(changingView);
+ if (viewState == null) {
+ // The position for this child was never generated, let's continue.
+ continue;
+ }
+ changingView.setAlpha(0);
+ changingView.setTranslationY(viewState.yTranslation);
+ changingView.setTranslationZ(viewState.zTranslation);
+ mHandledEvents.add(event);
+ }
+ }
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 77e2462..8bc669a 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3859,12 +3859,6 @@
mKeyguardDelegate.isShowingAndNotOccluded() :
mKeyguardDelegate.isShowing()));
- if (keyCode == KeyEvent.KEYCODE_POWER
- || keyCode == KeyEvent.KEYCODE_SLEEP
- || keyCode == KeyEvent.KEYCODE_WAKEUP) {
- policyFlags |= WindowManagerPolicy.FLAG_WAKE;
- }
-
if (DEBUG_INPUT) {
Log.d(TAG, "interceptKeyTq keycode=" + keyCode
+ " interactive=" + interactive + " keyguardActive=" + keyguardActive
@@ -3878,8 +3872,8 @@
// Basic policy based on interactive state.
int result;
- boolean isWakeKey = (policyFlags & (WindowManagerPolicy.FLAG_WAKE
- | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
+ boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
+ || event.isWakeKey();
if (interactive || (isInjected && !isWakeKey)) {
// When the screen is on or if the key is injected pass the key to the application.
result = ACTION_PASS_TO_USER;
diff --git a/services/Android.mk b/services/Android.mk
index 165f456..5fcef64 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -25,7 +25,8 @@
backup \
devicepolicy \
print \
- usb
+ usb \
+ voiceinteraction
# The convention is to name each service module 'services.$(module_name)'
LOCAL_STATIC_JAVA_LIBRARIES := $(addprefix services.,$(services))
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 0b688b6..0082b1e 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -74,6 +74,8 @@
import android.os.Environment.UserEnvironment;
import android.os.storage.IMountService;
import android.provider.Settings;
+import android.system.ErrnoException;
+import android.system.Os;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -142,9 +144,6 @@
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-
public class BackupManagerService extends IBackupManager.Stub {
private static final String TAG = "BackupManagerService";
@@ -2471,7 +2470,7 @@
// operations any more during this pass).
Slog.w(TAG, "Unable to save widget state for " + pkgName);
try {
- Libcore.os.ftruncate(fd, filepos);
+ Os.ftruncate(fd, filepos);
} catch (ErrnoException ee) {
Slog.w(TAG, "Unable to roll back!");
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 62deec2..d6ecb46 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -426,29 +426,33 @@
Slog.e(TAG, "no geocoder provider found");
}
- // bind to fused provider
- FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
- FusedProxy fusedProxy = FusedProxy.createAndBind(
- mContext,
- mLocationHandler,
- flpHardwareProvider.getLocationHardware(),
- com.android.internal.R.bool.config_enableFusedLocationOverlay,
- com.android.internal.R.string.config_fusedLocationProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames);
- if(fusedProxy == null) {
- Slog.e(TAG, "No FusedProvider found.");
- }
+ // bind to fused provider if supported
+ if (FlpHardwareProvider.isSupported()) {
+ FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
+ FusedProxy fusedProxy = FusedProxy.createAndBind(
+ mContext,
+ mLocationHandler,
+ flpHardwareProvider.getLocationHardware(),
+ com.android.internal.R.bool.config_enableFusedLocationOverlay,
+ com.android.internal.R.string.config_fusedLocationProviderPackageName,
+ com.android.internal.R.array.config_locationProviderPackageNames);
+ if(fusedProxy == null) {
+ Slog.e(TAG, "Unable to bind FusedProxy.");
+ }
- // bind to geofence provider
- GeofenceProxy provider = GeofenceProxy.createAndBind(mContext,
- com.android.internal.R.bool.config_enableGeofenceOverlay,
- com.android.internal.R.string.config_geofenceProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames,
- mLocationHandler,
- gpsProvider.getGpsGeofenceProxy(),
- flpHardwareProvider.getGeofenceHardware());
- if (provider == null) {
- Slog.e(TAG, "no geofence provider found");
+ // bind to geofence provider
+ GeofenceProxy provider = GeofenceProxy.createAndBind(mContext,
+ com.android.internal.R.bool.config_enableGeofenceOverlay,
+ com.android.internal.R.string.config_geofenceProviderPackageName,
+ com.android.internal.R.array.config_locationProviderPackageNames,
+ mLocationHandler,
+ gpsProvider.getGpsGeofenceProxy(),
+ flpHardwareProvider.getGeofenceHardware());
+ if (provider == null) {
+ Slog.e(TAG, "Unable to bind FLP Geofence proxy.");
+ }
+ } else {
+ Slog.e(TAG, "FLP HAL not supported.");
}
String[] testProviderStrings = resources.getStringArray(
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index fa803e2..fe97c71 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -421,11 +421,8 @@
}
/* This goes in response as msg.arg2 */
- int clientId = -1;
- int keyId = clientInfo.mClientIds.indexOfValue(id);
- if (keyId != -1) {
- clientId = clientInfo.mClientIds.keyAt(keyId);
- } else {
+ int clientId = clientInfo.getClientId(id);
+ if (clientId < 0) {
// This can happen because of race conditions. For example,
// SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
// and we may get in this situation.
@@ -904,5 +901,18 @@
mClientRequests.clear();
}
+ // mClientIds is a sparse array of listener id -> mDnsClient id. For a given mDnsClient id,
+ // return the corresponding listener id. mDnsClient id is also called a global id.
+ private int getClientId(final int globalId) {
+ // This doesn't use mClientIds.indexOfValue because indexOfValue uses == (not .equals)
+ // while also coercing the int primitives to Integer objects.
+ for (int i = 0, nSize = mClientIds.size(); i < nSize; i++) {
+ int mDnsId = mClientIds.valueAt(i);
+ if (globalId == mDnsId) {
+ return mClientIds.keyAt(i);
+ }
+ }
+ return -1;
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ed007e9..9eaddbd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -28,19 +28,23 @@
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import android.Manifest;
import android.app.AppOpsManager;
import android.app.IActivityContainer;
import android.app.IActivityContainerCallback;
import android.appwidget.AppWidgetManager;
import android.graphics.Rect;
import android.os.BatteryStats;
+import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.ProcessStats;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessCpuTracker;
@@ -56,6 +60,7 @@
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.firewall.IntentFirewall;
@@ -333,6 +338,9 @@
// How many bytes to write into the dropbox log before truncating
static final int DROPBOX_MAX_SIZE = 256 * 1024;
+ /** All system services */
+ SystemServiceManager mSystemServiceManager;
+
/** Run all ActivityStacks through this */
ActivityStackSupervisor mStackSupervisor;
@@ -399,7 +407,7 @@
/**
* List of intents that were used to start the most recent tasks.
*/
- private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
+ final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
@@ -879,17 +887,23 @@
* Set while we are wanting to sleep, to prevent any
* activities from being started/resumed.
*/
- boolean mSleeping = false;
+ private boolean mSleeping = false;
+
+ /**
+ * Set while we are running a voice interaction. This overrides
+ * sleeping while it is active.
+ */
+ private boolean mRunningVoice = false;
/**
* State of external calls telling us if the device is asleep.
*/
- boolean mWentToSleep = false;
+ private boolean mWentToSleep = false;
/**
* State of external call telling us if the lock screen is shown.
*/
- boolean mLockScreenShown = false;
+ private boolean mLockScreenShown = false;
/**
* Set if we are shutting down the system, similar to sleeping.
@@ -1003,11 +1017,11 @@
static class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
- static final int CHANGE_IMPORTANCE= 1<<1;
+ static final int CHANGE_PROCESS_STATE = 1<<1;
int changes;
int uid;
int pid;
- int importance;
+ int processState;
boolean foregroundActivities;
}
@@ -1117,6 +1131,8 @@
static final int REQUEST_ALL_PSS_MSG = 39;
static final int START_PROFILES_MSG = 40;
static final int UPDATE_TIME = 41;
+ static final int SYSTEM_USER_START_MSG = 42;
+ static final int SYSTEM_USER_CURRENT_MSG = 43;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1696,15 +1712,15 @@
break;
}
case REPORT_USER_SWITCH_MSG: {
- dispatchUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
+ dispatchUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2);
break;
}
case CONTINUE_USER_SWITCH_MSG: {
- continueUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
+ continueUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2);
break;
}
case USER_SWITCH_TIMEOUT_MSG: {
- timeoutUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
+ timeoutUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2);
break;
}
case IMMERSIVE_MODE_LOCK_MSG: {
@@ -1751,6 +1767,14 @@
}
break;
}
+ case SYSTEM_USER_START_MSG: {
+ mSystemServiceManager.startUser(msg.arg1);
+ break;
+ }
+ case SYSTEM_USER_CURRENT_MSG: {
+ mSystemServiceManager.switchUser(msg.arg1);
+ break;
+ }
}
}
};
@@ -1813,6 +1837,82 @@
}
};
+ /**
+ * Monitor for package changes and update our internal state.
+ */
+ private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ // Remove all tasks with activities in the specified package from the list of recent tasks
+ synchronized (ActivityManagerService.this) {
+ for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+ TaskRecord tr = mRecentTasks.get(i);
+ ComponentName cn = tr.intent.getComponent();
+ if (cn != null && cn.getPackageName().equals(packageName)) {
+ // If the package name matches, remove the task and kill the process
+ removeTaskByIdLocked(tr.taskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onPackageChanged(String packageName, int uid, String[] components) {
+ final PackageManager pm = mContext.getPackageManager();
+ final ArrayList<Pair<Intent, Integer>> recentTaskIntents =
+ new ArrayList<Pair<Intent, Integer>>();
+ final ArrayList<Integer> tasksToRemove = new ArrayList<Integer>();
+ // Copy the list of recent tasks so that we don't hold onto the lock on
+ // ActivityManagerService for long periods while checking if components exist.
+ synchronized (ActivityManagerService.this) {
+ for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+ TaskRecord tr = mRecentTasks.get(i);
+ recentTaskIntents.add(new Pair<Intent, Integer>(tr.intent, tr.taskId));
+ }
+ }
+ // Check the recent tasks and filter out all tasks with components that no longer exist.
+ Intent tmpI = new Intent();
+ for (int i = recentTaskIntents.size() - 1; i >= 0; i--) {
+ Pair<Intent, Integer> p = recentTaskIntents.get(i);
+ ComponentName cn = p.first.getComponent();
+ if (cn != null && cn.getPackageName().equals(packageName)) {
+ try {
+ // Add the task to the list to remove if the component no longer exists
+ tmpI.setComponent(cn);
+ if (pm.queryIntentActivities(tmpI, PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
+ tasksToRemove.add(p.second);
+ }
+ } catch (Exception e) {}
+ }
+ }
+ // Prune all the tasks with removed components from the list of recent tasks
+ synchronized (ActivityManagerService.this) {
+ for (int i = tasksToRemove.size() - 1; i >= 0; i--) {
+ // Remove the task but don't kill the process (since other components in that
+ // package may still be running and in the background)
+ removeTaskByIdLocked(tasksToRemove.get(i), 0);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
+ // Force stop the specified packages
+ if (packages != null) {
+ for (String pkg : packages) {
+ synchronized (ActivityManagerService.this) {
+ if (forceStopPackageLocked(pkg, -1, false, false, false, false, false, 0,
+ "finished booting")) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+ };
+
public void setSystemProcess() {
try {
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
@@ -2059,6 +2159,10 @@
Watchdog.getInstance().addThread(mHandler);
}
+ public void setSystemServiceManager(SystemServiceManager mgr) {
+ mSystemServiceManager = mgr;
+ }
+
private void start() {
mProcessCpuThread.start();
@@ -2254,6 +2358,11 @@
if (mFocusedActivity != r) {
if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedActivityLocked: r=" + r);
mFocusedActivity = r;
+ if (r.task != null && r.task.voiceInteractor != null) {
+ startRunningVoiceLocked();
+ } else {
+ finishRunningVoiceLocked();
+ }
mStackSupervisor.setFocusedStack(r);
if (r != null) {
mWindowManager.setFocusedApp(r.appToken, true);
@@ -2262,6 +2371,12 @@
}
}
+ final void clearFocusedActivity(ActivityRecord r) {
+ if (mFocusedActivity == r) {
+ mFocusedActivity = null;
+ }
+ }
+
@Override
public void setFocusedStack(int stackId) {
if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: stackId=" + stackId);
@@ -2990,7 +3105,7 @@
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
mStackSupervisor.startActivityLocked(null, intent, null, ri.activityInfo,
- null, null, 0, 0, 0, null, 0, null, false, null, null);
+ null, null, null, null, 0, 0, 0, null, 0, null, false, null, null);
}
}
}
@@ -3085,11 +3200,10 @@
observer.onForegroundActivitiesChanged(item.pid, item.uid,
item.foregroundActivities);
}
- if ((item.changes&ProcessChangeItem.CHANGE_IMPORTANCE) != 0) {
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "IMPORTANCE CHANGED pid="
- + item.pid + " uid=" + item.uid + ": " + item.importance);
- observer.onImportanceChanged(item.pid, item.uid,
- item.importance);
+ if ((item.changes&ProcessChangeItem.CHANGE_PROCESS_STATE) != 0) {
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "PROCSTATE CHANGED pid="
+ + item.pid + " uid=" + item.uid + ": " + item.processState);
+ observer.onProcessStateChanged(item.pid, item.uid, item.processState);
}
}
} catch (RemoteException e) {
@@ -3121,7 +3235,7 @@
}
for (int i=0; i<N; i++) {
PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
- mStackSupervisor.startActivityUncheckedLocked(pal.r, pal.sourceRecord, pal.startFlags,
+ mStackSupervisor.startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
doResume && i == (N-1), null);
}
mPendingActivityLaunches.clear();
@@ -3147,7 +3261,7 @@
false, true, "startActivity", null);
// TODO: Switch to user app stacks here.
return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
- resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
+ null, null, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
null, null, options, userId, null);
}
@@ -3162,7 +3276,7 @@
WaitResult res = new WaitResult();
// TODO: Switch to user app stacks here.
mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
- resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
+ null, null, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
res, null, options, UserHandle.getCallingUserId(), null);
return res;
}
@@ -3177,7 +3291,7 @@
false, true, "startActivityWithConfig", null);
// TODO: Switch to user app stacks here.
int ret = mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
- resolvedType, resultTo, resultWho, requestCode, startFlags,
+ resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
null, null, null, config, options, userId, null);
return ret;
}
@@ -3215,6 +3329,31 @@
}
@Override
+ public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
+ Intent intent, String resolvedType, IVoiceInteractionSession session,
+ IVoiceInteractor interactor, int startFlags, String profileFile,
+ ParcelFileDescriptor profileFd, Bundle options, int userId) {
+ if (checkCallingPermission(Manifest.permission.BIND_VOICE_INTERACTION)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: startVoiceActivity() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.BIND_VOICE_INTERACTION;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ if (session == null || interactor == null) {
+ throw new NullPointerException("null session or interactor");
+ }
+ userId = handleIncomingUser(callingPid, callingUid, userId,
+ false, true, "startVoiceActivity", null);
+ // TODO: Switch to user app stacks here.
+ return mStackSupervisor.startActivityMayWait(null, callingUid, callingPackage, intent,
+ resolvedType, session, interactor, null, null, 0, startFlags,
+ profileFile, profileFd, null, null, options, userId, null);
+ }
+
+ @Override
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent, Bundle options) {
// Refuse possible leaked file descriptors
@@ -3307,7 +3446,7 @@
final long origId = Binder.clearCallingIdentity();
int res = mStackSupervisor.startActivityLocked(r.app.thread, intent,
- r.resolvedType, aInfo, resultTo != null ? resultTo.appToken : null,
+ r.resolvedType, aInfo, null, null, resultTo != null ? resultTo.appToken : null,
resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage, 0,
options, false, null, null);
Binder.restoreCallingIdentity(origId);
@@ -3330,7 +3469,7 @@
// TODO: Switch to user app stacks here.
int ret = mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent, resolvedType,
- resultTo, resultWho, requestCode, startFlags,
+ null, null, resultTo, resultWho, requestCode, startFlags,
null, null, null, null, options, userId, container);
return ret;
}
@@ -3366,6 +3505,10 @@
if (N > 0 && mRecentTasks.get(0) == task) {
return;
}
+ // Another quick case: never add voice sessions.
+ if (task.voiceSession != null) {
+ return;
+ }
// Remove any existing entries that are the same kind of task.
final Intent intent = task.intent;
final boolean document = intent != null && intent.isDocument();
@@ -5200,26 +5343,8 @@
}
final void finishBooting() {
- IntentFilter pkgFilter = new IntentFilter();
- pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
- pkgFilter.addDataScheme("package");
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
- if (pkgs != null) {
- for (String pkg : pkgs) {
- synchronized (ActivityManagerService.this) {
- if (forceStopPackageLocked(pkg, -1, false, false, false, false, false, 0,
- "finished booting")) {
- setResultCode(Activity.RESULT_OK);
- return;
- }
- }
- }
- }
- }
- }, pkgFilter);
+ // Register receivers to handle package update events
+ mPackageMonitor.register(mContext, Looper.getMainLooper(), false);
synchronized (this) {
// Ensure that any processes we had put on hold are now started
@@ -8509,11 +8634,27 @@
return mSleeping || mShuttingDown;
}
+ public boolean isSleeping() {
+ return mSleeping;
+ }
+
void goingToSleep() {
synchronized(this) {
mWentToSleep = true;
updateEventDispatchingLocked();
+ goToSleepIfNeededLocked();
+ }
+ }
+ void finishRunningVoiceLocked() {
+ if (mRunningVoice) {
+ mRunningVoice = false;
+ goToSleepIfNeededLocked();
+ }
+ }
+
+ void goToSleepIfNeededLocked() {
+ if (mWentToSleep && !mRunningVoice) {
if (!mSleeping) {
mSleeping = true;
mStackSupervisor.goingToSleepLocked();
@@ -8576,7 +8717,7 @@
}
private void comeOutOfSleepIfNeededLocked() {
- if (!mWentToSleep && !mLockScreenShown) {
+ if ((!mWentToSleep && !mLockScreenShown) || mRunningVoice) {
if (mSleeping) {
mSleeping = false;
mStackSupervisor.comeOutOfSleepIfNeededLocked();
@@ -8592,6 +8733,13 @@
}
}
+ void startRunningVoiceLocked() {
+ if (!mRunningVoice) {
+ mRunningVoice = true;
+ comeOutOfSleepIfNeededLocked();
+ }
+ }
+
private void updateEventDispatchingLocked() {
mWindowManager.setEventDispatching(mBooted && !mWentToSleep && !mShuttingDown);
}
@@ -9298,7 +9446,7 @@
proc.notCachedSinceIdle = true;
proc.initialIdlePss = 0;
proc.nextPssTime = ProcessList.computeNextPssTime(proc.curProcState, true,
- mSleeping, now);
+ isSleeping(), now);
}
}
@@ -9597,6 +9745,8 @@
if (goingCallback != null) goingCallback.run();
+ mSystemServiceManager.startUser(mCurrentUserId);
+
synchronized (this) {
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
try {
@@ -9653,6 +9803,8 @@
}, 0, null, null,
android.Manifest.permission.INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+ } catch (Throwable t) {
+ Slog.wtf(TAG, "Failed sending first user broadcasts", t);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10482,6 +10634,7 @@
int adj = app.curAdj;
outInfo.importance = oomAdjToImportance(adj, outInfo);
outInfo.importanceReasonCode = app.adjTypeCode;
+ outInfo.processState = app.curProcState;
}
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
@@ -11152,8 +11305,8 @@
pw.println(" mSleeping=" + mSleeping + " mWentToSleep=" + mWentToSleep
+ " mLockScreenShown " + mLockScreenShown);
}
- if (mShuttingDown) {
- pw.println(" mShuttingDown=" + mShuttingDown);
+ if (mShuttingDown || mRunningVoice) {
+ pw.print(" mShuttingDown=" + mShuttingDown + " mRunningVoice=" + mRunningVoice);
}
}
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
@@ -14537,7 +14690,7 @@
app.keeping = true;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
- // System process can do UI, and when they do we want to have
+ // System processes can do UI, and when they do we want to have
// them trim their memory after the user leaves the UI. To
// facilitate this, here we need to determine whether or not it
// is currently showing UI.
@@ -15155,86 +15308,7 @@
adj = app.modifyRawOomAdj(adj);
app.curProcState = procState;
-
- int importance = app.memImportance;
- if (importance == 0 || adj != app.curAdj || schedGroup != app.curSchedGroup) {
- app.curAdj = adj;
- app.curSchedGroup = schedGroup;
- if (!interesting) {
- // For this reporting, if there is not something explicitly
- // interesting in this process then we will push it to the
- // background importance.
- importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
- } else if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
- importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
- } else if (adj >= ProcessList.SERVICE_B_ADJ) {
- importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
- } else if (adj >= ProcessList.HOME_APP_ADJ) {
- importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
- } else if (adj >= ProcessList.SERVICE_ADJ) {
- importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
- } else if (adj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
- importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
- } else if (adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
- importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
- } else if (adj >= ProcessList.VISIBLE_APP_ADJ) {
- importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
- } else if (adj >= ProcessList.FOREGROUND_APP_ADJ) {
- importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
- } else {
- importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERSISTENT;
- }
- }
-
- int changes = importance != app.memImportance ? ProcessChangeItem.CHANGE_IMPORTANCE : 0;
- if (foregroundActivities != app.foregroundActivities) {
- changes |= ProcessChangeItem.CHANGE_ACTIVITIES;
- }
- if (changes != 0) {
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Changes in " + app + ": " + changes);
- app.memImportance = importance;
- app.foregroundActivities = foregroundActivities;
- int i = mPendingProcessChanges.size()-1;
- ProcessChangeItem item = null;
- while (i >= 0) {
- item = mPendingProcessChanges.get(i);
- if (item.pid == app.pid) {
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Re-using existing item: " + item);
- break;
- }
- i--;
- }
- if (i < 0) {
- // No existing item in pending changes; need a new one.
- final int NA = mAvailProcessChanges.size();
- if (NA > 0) {
- item = mAvailProcessChanges.remove(NA-1);
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Retreiving available item: " + item);
- } else {
- item = new ProcessChangeItem();
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Allocating new item: " + item);
- }
- item.changes = 0;
- item.pid = app.pid;
- item.uid = app.info.uid;
- if (mPendingProcessChanges.size() == 0) {
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG,
- "*** Enqueueing dispatch processes changed!");
- mHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
- }
- mPendingProcessChanges.add(item);
- }
- item.changes |= changes;
- item.importance = importance;
- item.foregroundActivities = foregroundActivities;
- if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Item "
- + Integer.toHexString(System.identityHashCode(item))
- + " " + app.toShortString() + ": changes=" + item.changes
- + " importance=" + item.importance
- + " foreground=" + item.foregroundActivities
- + " type=" + app.adjType + " source=" + app.adjSource
- + " target=" + app.adjTarget);
- }
+ app.foregroundActivities = foregroundActivities;
return app.curRawAdj;
}
@@ -15273,7 +15347,7 @@
if (memLowered || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
app.pssProcState = app.setProcState;
app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, true,
- mSleeping, now);
+ isSleeping(), now);
mPendingPssProcesses.add(app);
}
}
@@ -15310,7 +15384,7 @@
}
}
return !processingBroadcasts
- && (mSleeping || mStackSupervisor.allResumedActivitiesIdle());
+ && (isSleeping() || mStackSupervisor.allResumedActivitiesIdle());
}
/**
@@ -15507,7 +15581,7 @@
}
private final boolean applyOomAdjLocked(ProcessRecord app, boolean wasKeeping,
- ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) {
+ ProcessRecord TOP_APP, boolean doingAll, long now) {
boolean success = true;
if (app.curRawAdj != app.setRawAdj) {
@@ -15526,6 +15600,8 @@
app.setRawAdj = app.curRawAdj;
}
+ int changes = 0;
+
if (app.curAdj != app.setAdj) {
ProcessList.setOomAdj(app.pid, app.curAdj);
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(
@@ -15567,9 +15643,14 @@
app.curSchedGroup <= Process.THREAD_GROUP_BG_NONINTERACTIVE);
}
}
+ if (app.repForegroundActivities != app.foregroundActivities) {
+ app.repForegroundActivities = app.foregroundActivities;
+ changes |= ProcessChangeItem.CHANGE_ACTIVITIES;
+ }
if (app.repProcState != app.curProcState) {
app.repProcState = app.curProcState;
- if (!reportingProcessState && app.thread != null) {
+ changes |= ProcessChangeItem.CHANGE_PROCESS_STATE;
+ if (app.thread != null) {
try {
if (false) {
//RuntimeException h = new RuntimeException("here");
@@ -15585,7 +15666,7 @@
app.setProcState)) {
app.lastStateTime = now;
app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, true,
- mSleeping, now);
+ isSleeping(), now);
if (DEBUG_PSS) Slog.d(TAG, "Process state change from "
+ ProcessList.makeProcStateString(app.setProcState) + " to "
+ ProcessList.makeProcStateString(app.curProcState) + " next pss in "
@@ -15595,7 +15676,7 @@
&& now > (app.lastStateTime+ProcessList.PSS_MIN_TIME_FROM_STATE_CHANGE))) {
requestPssLocked(app, app.setProcState);
app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, false,
- mSleeping, now);
+ isSleeping(), now);
} else if (false && DEBUG_PSS) {
Slog.d(TAG, "Not requesting PSS of " + app + ": next=" + (app.nextPssTime-now));
}
@@ -15614,6 +15695,51 @@
app.procStateChanged = true;
}
}
+
+ if (changes != 0) {
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Changes in " + app + ": " + changes);
+ int i = mPendingProcessChanges.size()-1;
+ ProcessChangeItem item = null;
+ while (i >= 0) {
+ item = mPendingProcessChanges.get(i);
+ if (item.pid == app.pid) {
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Re-using existing item: " + item);
+ break;
+ }
+ i--;
+ }
+ if (i < 0) {
+ // No existing item in pending changes; need a new one.
+ final int NA = mAvailProcessChanges.size();
+ if (NA > 0) {
+ item = mAvailProcessChanges.remove(NA-1);
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Retreiving available item: " + item);
+ } else {
+ item = new ProcessChangeItem();
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Allocating new item: " + item);
+ }
+ item.changes = 0;
+ item.pid = app.pid;
+ item.uid = app.info.uid;
+ if (mPendingProcessChanges.size() == 0) {
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG,
+ "*** Enqueueing dispatch processes changed!");
+ mHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
+ }
+ mPendingProcessChanges.add(item);
+ }
+ item.changes |= changes;
+ item.processState = app.repProcState;
+ item.foregroundActivities = app.repForegroundActivities;
+ if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Item "
+ + Integer.toHexString(System.identityHashCode(item))
+ + " " + app.toShortString() + ": changes=" + item.changes
+ + " procState=" + item.processState
+ + " foreground=" + item.foregroundActivities
+ + " type=" + app.adjType + " source=" + app.adjSource
+ + " target=" + app.adjTarget);
+ }
+
return success;
}
@@ -15624,7 +15750,7 @@
}
private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
- ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) {
+ ProcessRecord TOP_APP, boolean doingAll, long now) {
if (app.thread == null) {
return false;
}
@@ -15633,8 +15759,7 @@
computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
- return applyOomAdjLocked(app, wasKeeping, TOP_APP, doingAll,
- reportingProcessState, now);
+ return applyOomAdjLocked(app, wasKeeping, TOP_APP, doingAll, now);
}
final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
@@ -15700,10 +15825,6 @@
}
final boolean updateOomAdjLocked(ProcessRecord app) {
- return updateOomAdjLocked(app, false);
- }
-
- final boolean updateOomAdjLocked(ProcessRecord app, boolean doingProcessState) {
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
final boolean wasCached = app.cached;
@@ -15716,7 +15837,7 @@
// need to do a complete oom adj.
final int cachedAdj = app.curRawAdj >= ProcessList.CACHED_APP_MIN_ADJ
? app.curRawAdj : ProcessList.UNKNOWN_ADJ;
- boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false, doingProcessState,
+ boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false,
SystemClock.uptimeMillis());
if (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ) {
// Changed to/from cached state, so apps after it in the LRU
@@ -15849,7 +15970,7 @@
}
}
- applyOomAdjLocked(app, wasKeeping, TOP_APP, true, false, now);
+ applyOomAdjLocked(app, wasKeeping, TOP_APP, true, now);
// Count the number of process types.
switch (app.curProcState) {
@@ -15932,7 +16053,7 @@
}
mLastMemoryLevel = memFactor;
mLastNumProcesses = mLruProcesses.size();
- boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !mSleeping, now);
+ boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !isSleeping(), now);
final int trackerMemFactor = mProcessStats.getMemFactorLocked();
if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
if (mLowRamStartTime == 0) {
@@ -16475,7 +16596,15 @@
needStart = true;
}
+ if (uss.mState == UserStartedState.STATE_BOOTING) {
+ // Booting up a new user, need to tell system services about it.
+ // Note that this is on the same handler as scheduling of broadcasts,
+ // which is important because it needs to go first.
+ mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId));
+ }
+
if (foreground) {
+ mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId));
mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
@@ -16830,6 +16959,7 @@
}
uss.mState = UserStartedState.STATE_SHUTDOWN;
}
+ mSystemServiceManager.stopUser(userId);
broadcastIntentLocked(null, null, shutdownIntent,
null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE,
true, false, MY_PID, Process.SYSTEM_UID, userId);
@@ -16879,7 +17009,12 @@
}
}
- mStackSupervisor.removeUserLocked(userId);
+ if (stopped) {
+ mSystemServiceManager.cleanupUser(userId);
+ synchronized (this) {
+ mStackSupervisor.removeUserLocked(userId);
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 3e59def..7a44473 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -633,7 +633,7 @@
// case we will deliver it if this is the current top activity on its
// stack.
boolean unsent = true;
- if ((state == ActivityState.RESUMED || (service.mSleeping
+ if ((state == ActivityState.RESUMED || (service.isSleeping()
&& task.stack.topRunningActivityLocked(null) == this))
&& app != null && app.thread != null) {
try {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a2a9336..6769c9c 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -38,6 +38,8 @@
import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.CONTAINER_STATE_HAS_SURFACE;
+import android.service.voice.IVoiceInteractionSession;
+import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService.ItemMatcher;
@@ -501,6 +503,11 @@
if (DEBUG_TASKS) Slog.d(TAG, "Looking for task of " + target + " in " + this);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
+ if (task.voiceSession != null) {
+ // We never match voice sessions; those always run independently.
+ if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": voice session");
+ continue;
+ }
if (task.userId != userId) {
// Looking for a different task.
if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": different user");
@@ -1503,7 +1510,7 @@
// If the most recent activity was noHistory but was only stopped rather
// than stopped+finished because the device went to sleep, we need to make
// sure to finish it as we're making a new activity topmost.
- if (mService.mSleeping && mLastNoHistoryActivity != null &&
+ if (mService.isSleeping() && mLastNoHistoryActivity != null &&
!mLastNoHistoryActivity.finishing) {
if (DEBUG_STATES) Slog.d(TAG, "no-history finish of " + mLastNoHistoryActivity +
" on new resume");
@@ -2034,7 +2041,7 @@
+ " out to bottom task " + bottom.task);
} else {
targetTask = createTaskRecord(mStackSupervisor.getNextTaskId(), target.info,
- null, false);
+ null, null, null, false);
newThumbHolder = targetTask;
targetTask.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
@@ -2322,7 +2329,10 @@
mStackSupervisor.moveHomeToTop();
}
}
- mService.setFocusedActivityLocked(mStackSupervisor.topRunningActivityLocked());
+ ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
+ if (top != null) {
+ mService.setFocusedActivityLocked(top);
+ }
}
}
@@ -2331,7 +2341,7 @@
if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
|| (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
if (!r.finishing) {
- if (!mService.mSleeping) {
+ if (!mService.isSleeping()) {
if (DEBUG_STATES) {
Slog.d(TAG, "no-history finish of " + r);
}
@@ -2710,7 +2720,7 @@
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
destIntent.getComponent(), 0, srec.userId);
int res = mStackSupervisor.startActivityLocked(srec.app.thread, destIntent,
- null, aInfo, parent.appToken, null,
+ null, aInfo, null, null, parent.appToken, null,
0, -1, parent.launchedFromUid, parent.launchedFromPackage,
0, null, true, null, null);
foundParentInTask = res == ActivityManager.START_SUCCESS;
@@ -2739,9 +2749,7 @@
if (mPausingActivity == r) {
mPausingActivity = null;
}
- if (mService.mFocusedActivity == r) {
- mService.mFocusedActivity = null;
- }
+ mService.clearFocusedActivity(r);
r.configDestroy = false;
r.frozenBeforeDestroy = false;
@@ -3723,6 +3731,11 @@
mTaskHistory.get(taskNdx + 1).mOnTopOfHome = true;
}
mTaskHistory.remove(task);
+ if (task.voiceInteractor != null) {
+ // This task was a voice interaction, so it should not remain on the
+ // recent tasks list.
+ mService.mRecentTasks.remove(task);
+ }
if (mTaskHistory.isEmpty()) {
if (DEBUG_STACK) Slog.i(TAG, "removeTask: moving to back stack=" + this);
@@ -3736,8 +3749,10 @@
}
}
- TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent, boolean toTop) {
- TaskRecord task = new TaskRecord(taskId, info, intent);
+ TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ boolean toTop) {
+ TaskRecord task = new TaskRecord(taskId, info, intent, voiceSession, voiceInteractor);
addTask(task, toTop);
return task;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e044d3f..3770a07 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import static android.Manifest.permission.START_ANY_ACTIVITY;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -77,6 +76,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionSession;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -87,6 +87,7 @@
import android.view.InputEvent;
import android.view.Surface;
import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.TransferPipe;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
@@ -687,13 +688,14 @@
void startHomeActivity(Intent intent, ActivityInfo aInfo) {
moveHomeToTop();
- startActivityLocked(null, intent, null, aInfo, null, null, 0, 0, 0, null, 0,
+ startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0,
null, false, null, null);
}
final int startActivityMayWait(IApplicationThread caller, int callingUid,
- String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, String profileFile,
+ String callingPackage, Intent intent, String resolvedType,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile,
ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,
Bundle options, int userId, IActivityContainer iContainer) {
// Refuse possible leaked file descriptors
@@ -802,7 +804,8 @@
}
}
- int res = startActivityLocked(caller, intent, resolvedType, aInfo, resultTo, resultWho,
+ int res = startActivityLocked(caller, intent, resolvedType, aInfo,
+ voiceSession, voiceInteractor, resultTo, resultWho,
requestCode, callingPid, callingUid, callingPackage, startFlags, options,
componentSpecified, null, container);
@@ -918,7 +921,7 @@
theseOptions = null;
}
int res = startActivityLocked(caller, intent, resolvedTypes[i],
- aInfo, resultTo, null, -1, callingPid, callingUid, callingPackage,
+ aInfo, null, null, resultTo, null, -1, callingPid, callingUid, callingPackage,
0, theseOptions, componentSpecified, outActivity, null);
if (res < 0) {
return res;
@@ -1034,8 +1037,8 @@
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info,
new Configuration(mService.mConfiguration), r.compat,
- app.repProcState, r.icicle, results, newIntents, !andResume,
- mService.isNextTransitionForward(), profileFile, profileFd,
+ r.task.voiceInteractor, app.repProcState, r.icicle, results, newIntents,
+ !andResume, mService.isNextTransitionForward(), profileFile, profileFd,
profileAutoStop, options);
if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
@@ -1143,8 +1146,9 @@
}
final int startActivityLocked(IApplicationThread caller,
- Intent intent, String resolvedType, ActivityInfo aInfo, IBinder resultTo,
- String resultWho, int requestCode,
+ Intent intent, String resolvedType, ActivityInfo aInfo,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ IBinder resultTo, String resultWho, int requestCode,
int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options,
boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container) {
int err = ActivityManager.START_SUCCESS;
@@ -1187,7 +1191,7 @@
}
ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;
- int launchFlags = intent.getFlags();
+ final int launchFlags = intent.getFlags();
if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
&& sourceRecord != null) {
@@ -1232,6 +1236,38 @@
err = ActivityManager.START_CLASS_NOT_FOUND;
}
+ if (err == ActivityManager.START_SUCCESS && sourceRecord != null
+ && sourceRecord.task.voiceSession != null) {
+ // If this activity is being launched as part of a voice session, we need
+ // to ensure that it is safe to do so. If the upcoming activity will also
+ // be part of the voice session, we can only launch it if it has explicitly
+ // said it supports the VOICE category, or it is a part of the calling app.
+ if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
+ && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
+ try {
+ if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
+ intent, resolvedType)) {
+ err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+ }
+ } catch (RemoteException e) {
+ err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+ }
+ }
+ }
+
+ if (err == ActivityManager.START_SUCCESS && voiceSession != null) {
+ // If the caller is starting a new voice session, just make sure the target
+ // is actually allowing it to run this way.
+ try {
+ if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
+ intent, resolvedType)) {
+ err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+ }
+ } catch (RemoteException e) {
+ err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+ }
+ }
+
if (err != ActivityManager.START_SUCCESS) {
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1,
@@ -1305,8 +1341,8 @@
}
final ActivityStack stack = getFocusedStack();
- if (stack.mResumedActivity == null
- || stack.mResumedActivity.info.applicationInfo.uid != callingUid) {
+ if (voiceSession == null && (stack.mResumedActivity == null
+ || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
PendingActivityLaunch pal =
new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
@@ -1330,7 +1366,8 @@
mService.doPendingActivityLaunchesLocked(false);
- err = startActivityUncheckedLocked(r, sourceRecord, startFlags, true, options);
+ err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
+ startFlags, true, options);
if (allPausedActivitiesComplete()) {
// If someone asked to have the keyguard dismissed on the next
@@ -1410,8 +1447,9 @@
}
final int startActivityUncheckedLocked(ActivityRecord r,
- ActivityRecord sourceRecord, int startFlags, boolean doResume,
- Bundle options) {
+ ActivityRecord sourceRecord,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
+ boolean doResume, Bundle options) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
@@ -1755,7 +1793,7 @@
r.setTask(targetStack.createTaskRecord(getNextTaskId(),
newTaskInfo != null ? newTaskInfo : r.info,
newTaskIntent != null ? newTaskIntent : intent,
- true), null, true);
+ voiceSession, voiceInteractor, true), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
r.task);
} else {
@@ -1833,7 +1871,7 @@
targetStack.moveToFront();
ActivityRecord prev = targetStack.topActivity();
r.setTask(prev != null ? prev.task
- : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
+ : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, null, null, true),
null, true);
mWindowManager.moveTaskToTop(r.task.taskId);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
@@ -3104,7 +3142,7 @@
&& "content".equals(intent.getData().getScheme())) {
mimeType = mService.getProviderMimeType(intent.getData(), userId);
}
- return startActivityMayWait(null, -1, null, intent, mimeType, null, null, 0, 0, null,
+ return startActivityMayWait(null, -1, null, intent, mimeType, null, null, null, null, 0, 0, null,
null, null, null, null, userId, this);
}
@@ -3120,6 +3158,40 @@
null, 0, FORCE_NEW_TASK_FLAGS, FORCE_NEW_TASK_FLAGS, null, this);
}
+ private void checkEmbeddedAllowedInner(Intent intent, String resolvedType) {
+ int userId = mService.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), mCurrentUser, false, true, "ActivityContainer", null);
+ if (resolvedType == null) {
+ resolvedType = intent.getType();
+ if (resolvedType == null && intent.getData() != null
+ && "content".equals(intent.getData().getScheme())) {
+ resolvedType = mService.getProviderMimeType(intent.getData(), userId);
+ }
+ }
+ ActivityInfo aInfo = resolveActivity(intent, resolvedType, 0, null, null, userId);
+ if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
+ throw new SecurityException(
+ "Attempt to embed activity that has not set allowEmbedded=\"true\"");
+ }
+ }
+
+ /** Throw a SecurityException if allowEmbedded is not true */
+ @Override
+ public final void checkEmbeddedAllowed(Intent intent) {
+ checkEmbeddedAllowedInner(intent, null);
+ }
+
+ /** Throw a SecurityException if allowEmbedded is not true */
+ @Override
+ public final void checkEmbeddedAllowedIntentSender(IIntentSender intentSender) {
+ if (!(intentSender instanceof PendingIntentRecord)) {
+ throw new IllegalArgumentException("Bad PendingIntent object");
+ }
+ PendingIntentRecord pendingIntent = (PendingIntentRecord) intentSender;
+ checkEmbeddedAllowedInner(pendingIntent.key.requestIntent,
+ pendingIntent.key.requestResolvedType);
+ }
+
@Override
public IBinder asBinder() {
return this;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index dbe773c..83e8a4b 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -110,6 +110,20 @@
return data;
}
+ public long computeBatteryTimeRemaining() {
+ synchronized (mStats) {
+ long time = mStats.computeBatteryTimeRemaining(SystemClock.elapsedRealtime());
+ return time >= 0 ? (time/1000) : time;
+ }
+ }
+
+ public long computeChargeTimeRemaining() {
+ synchronized (mStats) {
+ long time = mStats.computeChargeTimeRemaining(SystemClock.elapsedRealtime());
+ return time >= 0 ? (time/1000) : time;
+ }
+ }
+
public void addIsolatedUid(int isolatedUid, int appUid) {
enforceCallingPermission();
synchronized (mStats) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index b15fa5d..9d6481a 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -503,7 +503,7 @@
// are already core system stuff so don't matter for this.
r.curApp = filter.receiverList.app;
filter.receiverList.app.curReceiver = r;
- mService.updateOomAdjLocked(r.curApp, true);
+ mService.updateOomAdjLocked(r.curApp);
}
}
try {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index d04a6b2..8d7d300 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -77,7 +77,6 @@
int curSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
int trimMemoryLevel; // Last selected memory trimming level
- int memImportance; // Importance constant computed from curAdj
int curProcState = -1; // Currently computed process state: ActivityManager.PROCESS_STATE_*
int repProcState = -1; // Last reported process state
int setProcState = -1; // Last set process state in process tracker
@@ -91,6 +90,7 @@
boolean hasStartedServices; // Are there any started services running in this process?
boolean foregroundServices; // Running any services that are foreground?
boolean foregroundActivities; // Running any activities that are foreground?
+ boolean repForegroundActivities; // Last reported foreground activities.
boolean systemNoUi; // This is a system process, but not currently showing UI.
boolean hasShownUi; // Has UI been shown in this process since it was started?
boolean pendingUiClean; // Want to clean up resources from showing UI?
@@ -267,9 +267,10 @@
pw.print(prefix); pw.print("persistent="); pw.print(persistent);
pw.print(" removed="); pw.println(removed);
}
- if (hasClientActivities || foregroundActivities) {
+ if (hasClientActivities || foregroundActivities || repForegroundActivities) {
pw.print(prefix); pw.print("hasClientActivities="); pw.print(hasClientActivities);
- pw.print(" foregroundActivities="); pw.println(foregroundActivities);
+ pw.print(" foregroundActivities="); pw.print(foregroundActivities);
+ pw.print(" (rep="); pw.print(repForegroundActivities); pw.println(")");
}
if (hasStartedServices) {
pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 80a219dd..68da54d 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -28,7 +28,9 @@
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionSession;
import android.util.Slog;
+import com.android.internal.app.IVoiceInteractor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -36,6 +38,8 @@
final class TaskRecord extends ThumbnailHolder {
final int taskId; // Unique identifier for this task.
final String affinity; // The affinity name for this task, or null.
+ final IVoiceInteractionSession voiceSession; // Voice interaction session driving task
+ final IVoiceInteractor voiceInteractor; // Associated interactor to provide to app
Intent intent; // The original intent that started the task.
Intent affinityIntent; // Intent of affinity-moved activity that started this task.
ComponentName origActivity; // The non-alias activity component of the intent.
@@ -64,9 +68,12 @@
* Display.DEFAULT_DISPLAY. */
boolean mOnTopOfHome = false;
- TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
+ TaskRecord(int _taskId, ActivityInfo info, Intent _intent,
+ IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
taskId = _taskId;
affinity = info.taskAffinity;
+ voiceSession = _voiceSession;
+ voiceInteractor = _voiceInteractor;
setIntent(_intent, info);
}
@@ -473,6 +480,12 @@
if (affinity != null) {
pw.print(prefix); pw.print("affinity="); pw.println(affinity);
}
+ if (voiceSession != null || voiceInteractor != null) {
+ pw.print(prefix); pw.print("VOICE: session=0x");
+ pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
+ pw.print(" interactor=0x");
+ pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor)));
+ }
if (intent != null) {
StringBuilder sb = new StringBuilder(128);
sb.append(prefix); sb.append("intent={");
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index fab84a8..09f1c56 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -60,11 +60,16 @@
private static final int FLP_RESULT_ID_UNKNOWN = -5;
private static final int FLP_RESULT_INVALID_GEOFENCE_TRANSITION = -6;
+ // FlpHal monitor status codes, they must be equal to the ones in fused_location.h
+ private static final int FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE = 1<<0;
+ private static final int FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE = 1<<1;
+
public static FlpHardwareProvider getInstance(Context context) {
if (sSingletonInstance == null) {
sSingletonInstance = new FlpHardwareProvider(context);
}
+ nativeInit();
return sSingletonInstance;
}
@@ -92,6 +97,7 @@
}
public static boolean isSupported() {
+ nativeInit();
return nativeIsSupported();
}
@@ -141,6 +147,8 @@
int transition,
long timestamp,
int sourcesUsed) {
+ // the transition Id does not require translation because the values in fused_location.h
+ // and GeofenceHardware are in sync
getGeofenceHardwareSink().reportGeofenceTransition(
geofenceId,
updateLocationInformation(location),
@@ -157,9 +165,23 @@
updatedLocation = updateLocationInformation(location);
}
+ int monitorStatus;
+ switch (status) {
+ case FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE:
+ monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
+ break;
+ case FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE:
+ monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
+ break;
+ default:
+ Log.e(TAG, "Invalid FlpHal Geofence monitor status: " + status);
+ monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
+ break;
+ }
+
getGeofenceHardwareSink().reportGeofenceMonitorStatus(
GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
- status,
+ monitorStatus,
updatedLocation,
source);
}
@@ -196,9 +218,9 @@
// Core members
private static native void nativeClassInit();
private static native boolean nativeIsSupported();
+ private static native void nativeInit();
// FlpLocationInterface members
- private native void nativeInit();
private native int nativeGetBatchSize();
private native void nativeStartBatching(int requestId, FusedBatchOptions options);
private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject);
@@ -238,12 +260,10 @@
public static final String GEOFENCING = "Geofencing";
public IFusedLocationHardware getLocationHardware() {
- nativeInit();
return mLocationHardware;
}
public IFusedGeofenceHardware getGeofenceHardware() {
- nativeInit();
return mGeofenceHardwareService;
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 855ae23..416a6b1 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -410,7 +410,7 @@
}
@Override
- public void onImportanceChanged(int pid, int uid, int importance) {
+ public void onProcessStateChanged(int pid, int uid, int procState) {
}
@Override
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
new file mode 100644
index 0000000..5567944
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -0,0 +1,263 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.service.notification.Condition;
+import android.service.notification.ConditionProviderService;
+import android.service.notification.IConditionListener;
+import android.service.notification.IConditionProvider;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import libcore.util.Objects;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+public class ConditionProviders extends ManagedServices {
+
+ private final ZenModeHelper mZenModeHelper;
+ private final ArrayMap<IBinder, IConditionListener> mListeners
+ = new ArrayMap<IBinder, IConditionListener>();
+ private final ArrayMap<Uri, ManagedServiceInfo> mConditions
+ = new ArrayMap<Uri, ManagedServiceInfo>();
+
+ private Uri mCurrentConditionId;
+
+ public ConditionProviders(Context context, Handler handler,
+ UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
+ super(context, handler, new Object(), userProfiles);
+ mZenModeHelper = zenModeHelper;
+ mZenModeHelper.addCallback(new ZenModeHelperCallback());
+ }
+
+ @Override
+ protected Config getConfig() {
+ Config c = new Config();
+ c.caption = "condition provider";
+ c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
+ c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
+ c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
+ c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
+ c.clientLabel = R.string.condition_provider_service_binding_label;
+ return c;
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ super.dump(pw);
+ synchronized(mMutex) {
+ pw.print(" mCurrentConditionId="); pw.println(mCurrentConditionId);
+ pw.print(" mListeners("); pw.print(mListeners.size()); pw.println("):");
+ for (int i = 0; i < mListeners.size(); i++) {
+ pw.print(" "); pw.println(mListeners.keyAt(i));
+ }
+ pw.print(" mConditions("); pw.print(mConditions.size()); pw.println("):");
+ for (int i = 0; i < mConditions.size(); i++) {
+ pw.print(" "); pw.print(mConditions.keyAt(i));
+ final ManagedServiceInfo info = mConditions.valueAt(i);
+ pw.print(" -> "); pw.print(info.component);
+ if (!mServices.contains(info)) {
+ pw.print(" (orphan)");
+ }
+ pw.println();
+ }
+ }
+ }
+
+ @Override
+ protected IInterface asInterface(IBinder binder) {
+ return IConditionProvider.Stub.asInterface(binder);
+ }
+
+ @Override
+ protected void onServiceAdded(IInterface service) {
+ Slog.d(TAG, "onServiceAdded " + service);
+ final IConditionProvider provider = (IConditionProvider) service;
+ try {
+ provider.onConnected();
+ } catch (RemoteException e) {
+ // we tried
+ }
+ }
+
+ @Override
+ protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
+ if (removed == null) return;
+ if (mCurrentConditionId != null) {
+ if (removed.equals(mConditions.get(mCurrentConditionId))) {
+ mCurrentConditionId = null;
+ mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
+ }
+ }
+ for (int i = mConditions.size() - 1; i >= 0; i--) {
+ if (removed.equals(mConditions.valueAt(i))) {
+ mConditions.removeAt(i);
+ }
+ }
+ }
+
+ public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
+ synchronized(mMutex) {
+ return checkServiceTokenLocked(provider);
+ }
+ }
+
+ public void requestZenModeConditions(IConditionListener callback, boolean requested) {
+ synchronized(mMutex) {
+ if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback
+ + " requested=" + requested);
+ if (callback == null) return;
+ if (requested) {
+ mListeners.put(callback.asBinder(), callback);
+ requestConditionsLocked(Condition.FLAG_RELEVANT_NOW);
+ } else {
+ mListeners.remove(callback.asBinder());
+ if (mListeners.isEmpty()) {
+ requestConditionsLocked(0);
+ }
+ }
+ }
+ }
+
+ public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
+ synchronized(mMutex) {
+ if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
+ + (conditions == null ? null : Arrays.asList(conditions)));
+ if (conditions == null || conditions.length == 0) return;
+ final int N = conditions.length;
+ boolean valid = true;
+ for (int i = 0; i < N; i++) {
+ final Uri id = conditions[i].id;
+ if (!Condition.isValidId(id, pkg)) {
+ Slog.w(TAG, "Ignoring conditions from " + pkg + " for invalid id: " + id);
+ valid = false;
+ }
+ }
+ if (!valid) return;
+
+ for (int i = 0; i < N; i++) {
+ mConditions.put(conditions[i].id, info);
+ }
+ for (IConditionListener listener : mListeners.values()) {
+ try {
+ listener.onConditionsReceived(conditions);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error sending conditions to listener " + listener, e);
+ }
+ }
+ if (mCurrentConditionId != null) {
+ for (int i = 0; i < N; i++) {
+ final Condition c = conditions[i];
+ if (!c.id.equals(mCurrentConditionId)) continue;
+ if (c.state == Condition.STATE_TRUE || c.state == Condition.STATE_ERROR) {
+ triggerExitLocked(c.state == Condition.STATE_ERROR);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ private void triggerExitLocked(boolean error) {
+ if (error) {
+ Slog.w(TAG, "Zen mode exit condition failed");
+ } else if (DEBUG) {
+ Slog.d(TAG, "Zen mode exit condition triggered");
+ }
+ mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
+ unsubscribeLocked(mCurrentConditionId);
+ mCurrentConditionId = null;
+ }
+
+ public void setZenModeCondition(Uri conditionId) {
+ synchronized(mMutex) {
+ if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
+ if (Objects.equal(mCurrentConditionId, conditionId)) return;
+
+ if (mCurrentConditionId != null) {
+ unsubscribeLocked(mCurrentConditionId);
+ }
+ if (conditionId != null) {
+ final ManagedServiceInfo info = mConditions.get(conditionId);
+ final IConditionProvider provider = provider(info);
+ if (provider == null) return;
+ try {
+ provider.onSubscribe(conditionId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error subscribing to " + conditionId
+ + " from " + info.component, e);
+ }
+ }
+ mCurrentConditionId = conditionId;
+ }
+ }
+
+ private void unsubscribeLocked(Uri conditionId) {
+ final ManagedServiceInfo info = mConditions.get(mCurrentConditionId);
+ final IConditionProvider provider = provider(info);
+ if (provider == null) return;
+ try {
+ provider.onUnsubscribe(conditionId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error unsubscribing to " + conditionId + " from " + info.component, e);
+ }
+ }
+
+ private static IConditionProvider provider(ManagedServiceInfo info) {
+ return info == null ? null : (IConditionProvider) info.service;
+ }
+
+ private void requestConditionsLocked(int flags) {
+ for (ManagedServiceInfo info : mServices) {
+ final IConditionProvider provider = provider(info);
+ if (provider == null) continue;
+ try {
+ provider.onRequestConditions(flags);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error requesting conditions from " + info.component, e);
+ }
+ }
+ }
+
+ private class ZenModeHelperCallback extends ZenModeHelper.Callback {
+ @Override
+ void onZenModeChanged() {
+ final int mode = mZenModeHelper.getZenMode();
+ if (mode == Global.ZEN_MODE_OFF) {
+ synchronized (mMutex) {
+ if (mCurrentConditionId != null) {
+ if (DEBUG) Slog.d(TAG, "Zen mode off, forcing unsubscribe from "
+ + mCurrentConditionId);
+ unsubscribeLocked(mCurrentConditionId);
+ mCurrentConditionId = null;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
new file mode 100644
index 0000000..0621f58
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -0,0 +1,621 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Manages the lifecycle of application-provided services bound by system server.
+ *
+ * Services managed by this helper must have:
+ * - An associated system settings value with a list of enabled component names.
+ * - A well-known action for services to use in their intent-filter.
+ * - A system permission for services to require in order to ensure system has exclusive binding.
+ * - A settings page for user configuration of enabled services, and associated intent action.
+ * - A remote interface definition (aidl) provided by the service used for communication.
+ */
+abstract public class ManagedServices {
+ protected final String TAG = getClass().getSimpleName();
+ protected static final boolean DEBUG = true;
+
+ private static final String ENABLED_SERVICES_SEPARATOR = ":";
+
+ private final Context mContext;
+ protected final Object mMutex;
+ private final UserProfiles mUserProfiles;
+ private final SettingsObserver mSettingsObserver;
+ private final Config mConfig;
+
+ // contains connections to all connected services, including app services
+ // and system services
+ protected final ArrayList<ManagedServiceInfo> mServices = new ArrayList<ManagedServiceInfo>();
+ // things that will be put into mServices as soon as they're ready
+ private final ArrayList<String> mServicesBinding = new ArrayList<String>();
+ // lists the component names of all enabled (and therefore connected)
+ // app services for current profiles.
+ private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
+ = new ArraySet<ComponentName>();
+ // Just the packages from mEnabledServicesForCurrentProfiles
+ private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<String>();
+
+ public ManagedServices(Context context, Handler handler, Object mutex,
+ UserProfiles userProfiles) {
+ mContext = context;
+ mMutex = mutex;
+ mUserProfiles = userProfiles;
+ mConfig = getConfig();
+ mSettingsObserver = new SettingsObserver(handler);
+ }
+
+ abstract protected Config getConfig();
+
+ private String getCaption() {
+ return mConfig.caption;
+ }
+
+ abstract protected IInterface asInterface(IBinder binder);
+
+ abstract protected void onServiceAdded(IInterface service);
+
+ protected void onServiceRemovedLocked(ManagedServiceInfo removed) { }
+
+ private ManagedServiceInfo newServiceInfo(IInterface service,
+ ComponentName component, int userid, boolean isSystem, ServiceConnection connection,
+ int targetSdkVersion) {
+ return new ManagedServiceInfo(service, component, userid, isSystem, connection,
+ targetSdkVersion);
+ }
+
+ public void onBootPhaseAppsCanStart() {
+ mSettingsObserver.observe();
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println(" All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size()
+ + ") enabled for current profiles:");
+ for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
+ pw.println(" " + cmpt);
+ }
+
+ pw.println(" Live " + getCaption() + "s (" + mServices.size() + "):");
+ for (ManagedServiceInfo info : mServices) {
+ pw.println(" " + info.component
+ + " (user " + info.userid + "): " + info.service
+ + (info.isSystem?" SYSTEM":""));
+ }
+ }
+
+ public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
+ if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
+ + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
+ + " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
+ boolean anyServicesInvolved = false;
+ if (pkgList != null && (pkgList.length > 0)) {
+ for (String pkgName : pkgList) {
+ if (mEnabledServicesPackageNames.contains(pkgName)) {
+ anyServicesInvolved = true;
+ }
+ }
+ }
+
+ if (anyServicesInvolved) {
+ // if we're not replacing a package, clean up orphaned bits
+ if (!queryReplace) {
+ disableNonexistentServices();
+ }
+ // make sure we're still bound to any of our services who may have just upgraded
+ rebindServices();
+ }
+ }
+
+ public ManagedServiceInfo checkServiceTokenLocked(IInterface service) {
+ checkNotNull(service);
+ final IBinder token = service.asBinder();
+ final int N = mServices.size();
+ for (int i=0; i<N; i++) {
+ final ManagedServiceInfo info = mServices.get(i);
+ if (info.service.asBinder() == token) return info;
+ }
+ throw new SecurityException("Disallowed call from unknown " + getCaption() + ": "
+ + service);
+ }
+
+ public void unregisterService(IInterface service, int userid) {
+ checkNotNull(service);
+ // no need to check permissions; if your service binder is in the list,
+ // that's proof that you had permission to add it in the first place
+ unregisterServiceImpl(service, userid);
+ }
+
+ public void registerService(IInterface service, ComponentName component, int userid) {
+ checkNotNull(service);
+ registerServiceImpl(service, component, userid);
+ }
+
+ /**
+ * Remove access for any services that no longer exist.
+ */
+ private void disableNonexistentServices() {
+ int[] userIds = mUserProfiles.getCurrentProfileIds();
+ final int N = userIds.length;
+ for (int i = 0 ; i < N; ++i) {
+ disableNonexistentServices(userIds[i]);
+ }
+ }
+
+ private void disableNonexistentServices(int userId) {
+ String flatIn = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ mConfig.secureSettingName,
+ userId);
+ if (!TextUtils.isEmpty(flatIn)) {
+ if (DEBUG) Slog.v(TAG, "flat before: " + flatIn);
+ PackageManager pm = mContext.getPackageManager();
+ List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
+ new Intent(mConfig.serviceInterface),
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+ userId);
+ if (DEBUG) Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
+ Set<ComponentName> installed = new ArraySet<ComponentName>();
+ for (int i = 0, count = installedServices.size(); i < count; i++) {
+ ResolveInfo resolveInfo = installedServices.get(i);
+ ServiceInfo info = resolveInfo.serviceInfo;
+
+ if (!mConfig.bindPermission.equals(info.permission)) {
+ Slog.w(TAG, "Skipping " + getCaption() + " service "
+ + info.packageName + "/" + info.name
+ + ": it does not require the permission "
+ + mConfig.bindPermission);
+ continue;
+ }
+ installed.add(new ComponentName(info.packageName, info.name));
+ }
+
+ String flatOut = "";
+ if (!installed.isEmpty()) {
+ String[] enabled = flatIn.split(ENABLED_SERVICES_SEPARATOR);
+ ArrayList<String> remaining = new ArrayList<String>(enabled.length);
+ for (int i = 0; i < enabled.length; i++) {
+ ComponentName enabledComponent = ComponentName.unflattenFromString(enabled[i]);
+ if (installed.contains(enabledComponent)) {
+ remaining.add(enabled[i]);
+ }
+ }
+ flatOut = TextUtils.join(ENABLED_SERVICES_SEPARATOR, remaining);
+ }
+ if (DEBUG) Slog.v(TAG, "flat after: " + flatOut);
+ if (!flatIn.equals(flatOut)) {
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ mConfig.secureSettingName,
+ flatOut, userId);
+ }
+ }
+ }
+
+ /**
+ * Called whenever packages change, the user switches, or the secure setting
+ * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
+ */
+ private void rebindServices() {
+ if (DEBUG) Slog.d(TAG, "rebindServices");
+ final int[] userIds = mUserProfiles.getCurrentProfileIds();
+ final int nUserIds = userIds.length;
+
+ final SparseArray<String> flat = new SparseArray<String>();
+
+ for (int i = 0; i < nUserIds; ++i) {
+ flat.put(userIds[i], Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ mConfig.secureSettingName,
+ userIds[i]));
+ }
+
+ ManagedServiceInfo[] toRemove = new ManagedServiceInfo[mServices.size()];
+ final SparseArray<ArrayList<ComponentName>> toAdd
+ = new SparseArray<ArrayList<ComponentName>>();
+
+ synchronized (mMutex) {
+ // unbind and remove all existing services
+ toRemove = mServices.toArray(toRemove);
+
+ final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>();
+ final ArraySet<String> newPackages = new ArraySet<String>();
+
+ for (int i = 0; i < nUserIds; ++i) {
+ final ArrayList<ComponentName> add = new ArrayList<ComponentName>();
+ toAdd.put(userIds[i], add);
+
+ // decode the list of components
+ String toDecode = flat.get(userIds[i]);
+ if (toDecode != null) {
+ String[] components = toDecode.split(ENABLED_SERVICES_SEPARATOR);
+ for (int j = 0; j < components.length; j++) {
+ final ComponentName component
+ = ComponentName.unflattenFromString(components[j]);
+ if (component != null) {
+ newEnabled.add(component);
+ add.add(component);
+ newPackages.add(component.getPackageName());
+ }
+ }
+
+ }
+ }
+ mEnabledServicesForCurrentProfiles = newEnabled;
+ mEnabledServicesPackageNames = newPackages;
+ }
+
+ for (ManagedServiceInfo info : toRemove) {
+ final ComponentName component = info.component;
+ final int oldUser = info.userid;
+ Slog.v(TAG, "disabling " + getCaption() + " for user "
+ + oldUser + ": " + component);
+ unregisterService(component, info.userid);
+ }
+
+ for (int i = 0; i < nUserIds; ++i) {
+ final ArrayList<ComponentName> add = toAdd.get(userIds[i]);
+ final int N = add.size();
+ for (int j = 0; j < N; j++) {
+ final ComponentName component = add.get(j);
+ Slog.v(TAG, "enabling " + getCaption() + " for user " + userIds[i] + ": "
+ + component);
+ registerService(component, userIds[i]);
+ }
+ }
+ }
+
+ /**
+ * Version of registerService that takes the name of a service component to bind to.
+ */
+ private void registerService(final ComponentName name, final int userid) {
+ if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid);
+
+ synchronized (mMutex) {
+ final String servicesBindingTag = name.toString() + "/" + userid;
+ if (mServicesBinding.contains(servicesBindingTag)) {
+ // stop registering this thing already! we're working on it
+ return;
+ }
+ mServicesBinding.add(servicesBindingTag);
+
+ final int N = mServices.size();
+ for (int i=N-1; i>=0; i--) {
+ final ManagedServiceInfo info = mServices.get(i);
+ if (name.equals(info.component)
+ && info.userid == userid) {
+ // cut old connections
+ if (DEBUG) Slog.v(TAG, " disconnecting old " + getCaption() + ": "
+ + info.service);
+ removeServiceLocked(i);
+ if (info.connection != null) {
+ mContext.unbindService(info.connection);
+ }
+ }
+ }
+
+ Intent intent = new Intent(mConfig.serviceInterface);
+ intent.setComponent(name);
+
+ intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel);
+
+ final PendingIntent pendingIntent = PendingIntent.getActivity(
+ mContext, 0, new Intent(mConfig.settingsAction), 0);
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
+
+ ApplicationInfo appInfo = null;
+ try {
+ appInfo = mContext.getPackageManager().getApplicationInfo(
+ name.getPackageName(), 0);
+ } catch (NameNotFoundException e) {
+ // Ignore if the package doesn't exist we won't be able to bind to the service.
+ }
+ final int targetSdkVersion =
+ appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;
+
+ try {
+ if (DEBUG) Slog.v(TAG, "binding: " + intent);
+ if (!mContext.bindServiceAsUser(intent,
+ new ServiceConnection() {
+ IInterface mService;
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ boolean added = false;
+ synchronized (mMutex) {
+ mServicesBinding.remove(servicesBindingTag);
+ try {
+ mService = asInterface(binder);
+ ManagedServiceInfo info = newServiceInfo(mService, name,
+ userid, false /*isSystem*/, this, targetSdkVersion);
+ binder.linkToDeath(info, 0);
+ added = mServices.add(info);
+ } catch (RemoteException e) {
+ // already dead
+ }
+ }
+ if (added) {
+ onServiceAdded(mService);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Slog.v(TAG, getCaption() + " connection lost: " + name);
+ }
+ },
+ Context.BIND_AUTO_CREATE,
+ new UserHandle(userid)))
+ {
+ mServicesBinding.remove(servicesBindingTag);
+ Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
+ return;
+ }
+ } catch (SecurityException ex) {
+ Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Remove a service for the given user by ComponentName
+ */
+ private void unregisterService(ComponentName name, int userid) {
+ synchronized (mMutex) {
+ final int N = mServices.size();
+ for (int i=N-1; i>=0; i--) {
+ final ManagedServiceInfo info = mServices.get(i);
+ if (name.equals(info.component)
+ && info.userid == userid) {
+ removeServiceLocked(i);
+ if (info.connection != null) {
+ try {
+ mContext.unbindService(info.connection);
+ } catch (IllegalArgumentException ex) {
+ // something happened to the service: we think we have a connection
+ // but it's bogus.
+ Slog.e(TAG, getCaption() + " " + name + " could not be unbound: " + ex);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a service from the list but does not unbind
+ *
+ * @return the removed service.
+ */
+ private ManagedServiceInfo removeServiceImpl(IInterface service, final int userid) {
+ if (DEBUG) Slog.d(TAG, "removeServiceImpl service=" + service + " u=" + userid);
+ ManagedServiceInfo serviceInfo = null;
+ synchronized (mMutex) {
+ final int N = mServices.size();
+ for (int i=N-1; i>=0; i--) {
+ final ManagedServiceInfo info = mServices.get(i);
+ if (info.service.asBinder() == service.asBinder()
+ && info.userid == userid) {
+ if (DEBUG) Slog.d(TAG, "Removing active service " + info.component);
+ serviceInfo = removeServiceLocked(i);
+ }
+ }
+ }
+ return serviceInfo;
+ }
+
+ private ManagedServiceInfo removeServiceLocked(int i) {
+ final ManagedServiceInfo info = mServices.remove(i);
+ onServiceRemovedLocked(info);
+ return info;
+ }
+
+ private void checkNotNull(IInterface service) {
+ if (service == null) {
+ throw new IllegalArgumentException(getCaption() + " must not be null");
+ }
+ }
+
+ private void registerServiceImpl(final IInterface service,
+ final ComponentName component, final int userid) {
+ synchronized (mMutex) {
+ try {
+ ManagedServiceInfo info = newServiceInfo(service, component, userid,
+ true /*isSystem*/, null, Build.VERSION_CODES.L);
+ service.asBinder().linkToDeath(info, 0);
+ mServices.add(info);
+ } catch (RemoteException e) {
+ // already dead
+ }
+ }
+ }
+
+ /**
+ * Removes a service from the list and unbinds.
+ */
+ private void unregisterServiceImpl(IInterface service, int userid) {
+ ManagedServiceInfo info = removeServiceImpl(service, userid);
+ if (info != null && info.connection != null) {
+ mContext.unbindService(info.connection);
+ }
+ }
+
+ private class SettingsObserver extends ContentObserver {
+ private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(mConfig.secureSettingName);
+
+ private SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ private void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(mSecureSettingsUri,
+ false, this, UserHandle.USER_ALL);
+ update(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
+ }
+
+ private void update(Uri uri) {
+ if (uri == null || mSecureSettingsUri.equals(uri)) {
+ rebindServices();
+ }
+ }
+ }
+
+ public class ManagedServiceInfo implements IBinder.DeathRecipient {
+ public IInterface service;
+ public ComponentName component;
+ public int userid;
+ public boolean isSystem;
+ public ServiceConnection connection;
+ public int targetSdkVersion;
+
+ public ManagedServiceInfo(IInterface service, ComponentName component,
+ int userid, boolean isSystem, ServiceConnection connection, int targetSdkVersion) {
+ this.service = service;
+ this.component = component;
+ this.userid = userid;
+ this.isSystem = isSystem;
+ this.connection = connection;
+ this.targetSdkVersion = targetSdkVersion;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("ManagedServiceInfo[")
+ .append("component=").append(component)
+ .append(",userid=").append(userid)
+ .append(",isSystem=").append(isSystem)
+ .append(",targetSdkVersion=").append(targetSdkVersion)
+ .append(",connection=").append(connection == null ? null : "<connection>")
+ .append(",service=").append(service)
+ .append(']').toString();
+ }
+
+ public boolean enabledAndUserMatches(int nid) {
+ if (!isEnabledForCurrentProfiles()) {
+ return false;
+ }
+ if (this.userid == UserHandle.USER_ALL) return true;
+ if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
+ return supportsProfiles() && mUserProfiles.isCurrentProfile(nid);
+ }
+
+ public boolean supportsProfiles() {
+ return targetSdkVersion >= Build.VERSION_CODES.L;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) Slog.d(TAG, "binderDied");
+ // Remove the service, but don't unbind from the service. The system will bring the
+ // service back up, and the onServiceConnected handler will readd the service with the
+ // new binding. If this isn't a bound service, and is just a registered
+ // service, just removing it from the list is all we need to do anyway.
+ removeServiceImpl(this.service, this.userid);
+ }
+
+ /** convenience method for looking in mEnabledServicesForCurrentProfiles */
+ public boolean isEnabledForCurrentProfiles() {
+ if (this.isSystem) return true;
+ if (this.connection == null) return false;
+ return mEnabledServicesForCurrentProfiles.contains(this.component);
+ }
+ }
+
+ public static class UserProfiles {
+ // Profiles of the current user.
+ private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
+
+ public void updateCache(Context context) {
+ UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ if (userManager != null) {
+ int currentUserId = ActivityManager.getCurrentUser();
+ List<UserInfo> profiles = userManager.getProfiles(currentUserId);
+ synchronized (mCurrentProfiles) {
+ mCurrentProfiles.clear();
+ for (UserInfo user : profiles) {
+ mCurrentProfiles.put(user.id, user);
+ }
+ }
+ }
+ }
+
+ public int[] getCurrentProfileIds() {
+ synchronized (mCurrentProfiles) {
+ int[] users = new int[mCurrentProfiles.size()];
+ final int N = mCurrentProfiles.size();
+ for (int i = 0; i < N; ++i) {
+ users[i] = mCurrentProfiles.keyAt(i);
+ }
+ return users;
+ }
+ }
+
+ public boolean isCurrentProfile(int userId) {
+ synchronized (mCurrentProfiles) {
+ return mCurrentProfiles.get(userId) != null;
+ }
+ }
+ }
+
+ protected static class Config {
+ String caption;
+ String serviceInterface;
+ String secureSettingName;
+ String bindPermission;
+ String settingsAction;
+ int clientLabel;
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationListeners.java b/services/core/java/com/android/server/notification/NotificationListeners.java
deleted file mode 100644
index 91d2f98..0000000
--- a/services/core/java/com/android/server/notification/NotificationListeners.java
+++ /dev/null
@@ -1,608 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.notification.INotificationListener;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.R;
-import com.android.server.notification.NotificationManagerService.UserProfiles;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-public class NotificationListeners {
- private static final String TAG = "NotificationListeners";
- private static final boolean DBG = NotificationManagerService.DBG;
-
- private static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":";
-
- private final Context mContext;
- private final Handler mHandler;
- private final Object mMutex;
- private final UserProfiles mUserProfiles;
- private final SettingsObserver mSettingsObserver;
-
- // contains connections to all connected listeners, including app services
- // and system listeners
- private final ArrayList<NotificationListenerInfo> mListeners
- = new ArrayList<NotificationListenerInfo>();
- // things that will be put into mListeners as soon as they're ready
- private final ArrayList<String> mServicesBinding = new ArrayList<String>();
- // lists the component names of all enabled (and therefore connected) listener
- // app services for current profiles.
- private ArraySet<ComponentName> mEnabledListenersForCurrentProfiles
- = new ArraySet<ComponentName>();
- // Just the packages from mEnabledListenersForCurrentProfiles
- private ArraySet<String> mEnabledListenerPackageNames = new ArraySet<String>();
-
- public NotificationListeners(Context context, Handler handler, Object mutex,
- UserProfiles userProfiles) {
- mContext = context;
- mHandler = handler;
- mMutex = mutex;
- mUserProfiles = userProfiles;
- mSettingsObserver = new SettingsObserver(mHandler);
- }
-
- public void onBootPhaseAppsCanStart() {
- mSettingsObserver.observe();
- }
-
- protected void onServiceAdded(INotificationListener mListener) {
- // for subclasses
- }
-
- public void dump(PrintWriter pw) {
- pw.println(" Listeners (" + mEnabledListenersForCurrentProfiles.size()
- + ") enabled for current profiles:");
- for (ComponentName cmpt : mEnabledListenersForCurrentProfiles) {
- pw.println(" " + cmpt);
- }
-
- pw.println(" Live listeners (" + mListeners.size() + "):");
- for (NotificationListenerInfo info : mListeners) {
- pw.println(" " + info.component
- + " (user " + info.userid + "): " + info.listener
- + (info.isSystem?" SYSTEM":""));
- }
- }
-
- public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
- boolean anyListenersInvolved = false;
- if (pkgList != null && (pkgList.length > 0)) {
- for (String pkgName : pkgList) {
- if (mEnabledListenerPackageNames.contains(pkgName)) {
- anyListenersInvolved = true;
- }
- }
- }
-
- if (anyListenersInvolved) {
- // if we're not replacing a package, clean up orphaned bits
- if (!queryReplace) {
- disableNonexistentListeners();
- }
- // make sure we're still bound to any of our
- // listeners who may have just upgraded
- rebindListenerServices();
- }
- }
-
- /**
- * asynchronously notify all listeners about a new notification
- */
- public void notifyPostedLocked(StatusBarNotification sbn) {
- // make a copy in case changes are made to the underlying Notification object
- final StatusBarNotification sbnClone = sbn.clone();
- for (final NotificationListenerInfo info : mListeners) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- info.notifyPostedIfUserMatch(sbnClone);
- }
- });
- }
- }
-
- /**
- * asynchronously notify all listeners about a removed notification
- */
- public void notifyRemovedLocked(StatusBarNotification sbn) {
- // make a copy in case changes are made to the underlying Notification object
- // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the notification
- final StatusBarNotification sbnLight = sbn.cloneLight();
-
- for (final NotificationListenerInfo info : mListeners) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- info.notifyRemovedIfUserMatch(sbnLight);
- }
- });
- }
- }
-
- public NotificationListenerInfo checkListenerTokenLocked(INotificationListener listener) {
- checkNullListener(listener);
- final IBinder token = listener.asBinder();
- final int N = mListeners.size();
- for (int i=0; i<N; i++) {
- final NotificationListenerInfo info = mListeners.get(i);
- if (info.listener.asBinder() == token) return info;
- }
- throw new SecurityException("Disallowed call from unknown listener: " + listener);
- }
-
- public void unregisterListener(INotificationListener listener, int userid) {
- checkNullListener(listener);
- // no need to check permissions; if your listener binder is in the list,
- // that's proof that you had permission to add it in the first place
- unregisterListenerImpl(listener, userid);
- }
-
- public void registerListener(INotificationListener listener,
- ComponentName component, int userid) {
- checkNullListener(listener);
- registerListenerImpl(listener, component, userid);
- }
-
- /**
- * Remove notification access for any services that no longer exist.
- */
- private void disableNonexistentListeners() {
- int[] userIds = mUserProfiles.getCurrentProfileIds();
- final int N = userIds.length;
- for (int i = 0 ; i < N; ++i) {
- disableNonexistentListeners(userIds[i]);
- }
- }
-
- private void disableNonexistentListeners(int userId) {
- String flatIn = Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
- userId);
- if (!TextUtils.isEmpty(flatIn)) {
- if (DBG) Slog.v(TAG, "flat before: " + flatIn);
- PackageManager pm = mContext.getPackageManager();
- List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
- new Intent(NotificationListenerService.SERVICE_INTERFACE),
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
- userId);
-
- Set<ComponentName> installed = new ArraySet<ComponentName>();
- for (int i = 0, count = installedServices.size(); i < count; i++) {
- ResolveInfo resolveInfo = installedServices.get(i);
- ServiceInfo info = resolveInfo.serviceInfo;
-
- if (!android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE.equals(
- info.permission)) {
- Slog.w(TAG, "Skipping notification listener service "
- + info.packageName + "/" + info.name
- + ": it does not require the permission "
- + android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE);
- continue;
- }
- installed.add(new ComponentName(info.packageName, info.name));
- }
-
- String flatOut = "";
- if (!installed.isEmpty()) {
- String[] enabled = flatIn.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
- ArrayList<String> remaining = new ArrayList<String>(enabled.length);
- for (int i = 0; i < enabled.length; i++) {
- ComponentName enabledComponent = ComponentName.unflattenFromString(enabled[i]);
- if (installed.contains(enabledComponent)) {
- remaining.add(enabled[i]);
- }
- }
- flatOut = TextUtils.join(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR, remaining);
- }
- if (DBG) Slog.v(TAG, "flat after: " + flatOut);
- if (!flatIn.equals(flatOut)) {
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
- flatOut, userId);
- }
- }
- }
-
- /**
- * Called whenever packages change, the user switches, or ENABLED_NOTIFICATION_LISTENERS
- * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
- */
- private void rebindListenerServices() {
- final int[] userIds = mUserProfiles.getCurrentProfileIds();
- final int nUserIds = userIds.length;
-
- final SparseArray<String> flat = new SparseArray<String>();
-
- for (int i = 0; i < nUserIds; ++i) {
- flat.put(userIds[i], Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
- userIds[i]));
- }
-
- NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()];
- final SparseArray<ArrayList<ComponentName>> toAdd
- = new SparseArray<ArrayList<ComponentName>>();
-
- synchronized (mMutex) {
- // unbind and remove all existing listeners
- toRemove = mListeners.toArray(toRemove);
-
- final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>();
- final ArraySet<String> newPackages = new ArraySet<String>();
-
- for (int i = 0; i < nUserIds; ++i) {
- final ArrayList<ComponentName> add = new ArrayList<ComponentName>();
- toAdd.put(userIds[i], add);
-
- // decode the list of components
- String toDecode = flat.get(userIds[i]);
- if (toDecode != null) {
- String[] components = toDecode.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
- for (int j = 0; j < components.length; j++) {
- final ComponentName component
- = ComponentName.unflattenFromString(components[j]);
- if (component != null) {
- newEnabled.add(component);
- add.add(component);
- newPackages.add(component.getPackageName());
- }
- }
-
- }
- }
- mEnabledListenersForCurrentProfiles = newEnabled;
- mEnabledListenerPackageNames = newPackages;
- }
-
- for (NotificationListenerInfo info : toRemove) {
- final ComponentName component = info.component;
- final int oldUser = info.userid;
- Slog.v(TAG, "disabling notification listener for user "
- + oldUser + ": " + component);
- unregisterListenerService(component, info.userid);
- }
-
- for (int i = 0; i < nUserIds; ++i) {
- final ArrayList<ComponentName> add = toAdd.get(userIds[i]);
- final int N = add.size();
- for (int j = 0; j < N; j++) {
- final ComponentName component = add.get(j);
- Slog.v(TAG, "enabling notification listener for user " + userIds[i] + ": "
- + component);
- registerListenerService(component, userIds[i]);
- }
- }
- }
-
- /**
- * Version of registerListener that takes the name of a
- * {@link android.service.notification.NotificationListenerService} to bind to.
- *
- * This is the mechanism by which third parties may subscribe to notifications.
- */
- private void registerListenerService(final ComponentName name, final int userid) {
- NotificationUtil.checkCallerIsSystem();
-
- if (DBG) Slog.v(TAG, "registerListenerService: " + name + " u=" + userid);
-
- synchronized (mMutex) {
- final String servicesBindingTag = name.toString() + "/" + userid;
- if (mServicesBinding.contains(servicesBindingTag)) {
- // stop registering this thing already! we're working on it
- return;
- }
- mServicesBinding.add(servicesBindingTag);
-
- final int N = mListeners.size();
- for (int i=N-1; i>=0; i--) {
- final NotificationListenerInfo info = mListeners.get(i);
- if (name.equals(info.component)
- && info.userid == userid) {
- // cut old connections
- if (DBG) Slog.v(TAG, " disconnecting old listener: " + info.listener);
- mListeners.remove(i);
- if (info.connection != null) {
- mContext.unbindService(info.connection);
- }
- }
- }
-
- Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
- intent.setComponent(name);
-
- intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
- R.string.notification_listener_binding_label);
-
- final PendingIntent pendingIntent = PendingIntent.getActivity(
- mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0);
- intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
-
- ApplicationInfo appInfo = null;
- try {
- appInfo = mContext.getPackageManager().getApplicationInfo(
- name.getPackageName(), 0);
- } catch (NameNotFoundException e) {
- // Ignore if the package doesn't exist we won't be able to bind to the service.
- }
- final int targetSdkVersion =
- appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;
-
- try {
- if (DBG) Slog.v(TAG, "binding: " + intent);
- if (!mContext.bindServiceAsUser(intent,
- new ServiceConnection() {
- INotificationListener mListener;
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- boolean added = false;
- synchronized (mMutex) {
- mServicesBinding.remove(servicesBindingTag);
- try {
- mListener = INotificationListener.Stub.asInterface(service);
- NotificationListenerInfo info
- = new NotificationListenerInfo(
- mListener, name, userid, this,
- targetSdkVersion);
- service.linkToDeath(info, 0);
- added = mListeners.add(info);
- } catch (RemoteException e) {
- // already dead
- }
- }
- if (added) {
- onServiceAdded(mListener);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Slog.v(TAG, "notification listener connection lost: " + name);
- }
- },
- Context.BIND_AUTO_CREATE,
- new UserHandle(userid)))
- {
- mServicesBinding.remove(servicesBindingTag);
- Slog.w(TAG, "Unable to bind listener service: " + intent);
- return;
- }
- } catch (SecurityException ex) {
- Slog.e(TAG, "Unable to bind listener service: " + intent, ex);
- return;
- }
- }
- }
-
- /**
- * Remove a listener service for the given user by ComponentName
- */
- private void unregisterListenerService(ComponentName name, int userid) {
- NotificationUtil.checkCallerIsSystem();
-
- synchronized (mMutex) {
- final int N = mListeners.size();
- for (int i=N-1; i>=0; i--) {
- final NotificationListenerInfo info = mListeners.get(i);
- if (name.equals(info.component)
- && info.userid == userid) {
- mListeners.remove(i);
- if (info.connection != null) {
- try {
- mContext.unbindService(info.connection);
- } catch (IllegalArgumentException ex) {
- // something happened to the service: we think we have a connection
- // but it's bogus.
- Slog.e(TAG, "Listener " + name + " could not be unbound: " + ex);
- }
- }
- }
- }
- }
- }
-
- /**
- * Removes a listener from the list but does not unbind from the listener's service.
- *
- * @return the removed listener.
- */
- private NotificationListenerInfo removeListenerImpl(
- final INotificationListener listener, final int userid) {
- NotificationListenerInfo listenerInfo = null;
- synchronized (mMutex) {
- final int N = mListeners.size();
- for (int i=N-1; i>=0; i--) {
- final NotificationListenerInfo info = mListeners.get(i);
- if (info.listener.asBinder() == listener.asBinder()
- && info.userid == userid) {
- listenerInfo = mListeners.remove(i);
- }
- }
- }
- return listenerInfo;
- }
-
- private void checkNullListener(INotificationListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("Listener must not be null");
- }
- }
-
- private void registerListenerImpl(final INotificationListener listener,
- final ComponentName component, final int userid) {
- synchronized (mMutex) {
- try {
- NotificationListenerInfo info
- = new NotificationListenerInfo(listener, component, userid,
- /*isSystem*/ true, Build.VERSION_CODES.L);
- listener.asBinder().linkToDeath(info, 0);
- mListeners.add(info);
- } catch (RemoteException e) {
- // already dead
- }
- }
- }
-
- /**
- * Removes a listener from the list and unbinds from its service.
- */
- private void unregisterListenerImpl(final INotificationListener listener, final int userid) {
- NotificationListenerInfo info = removeListenerImpl(listener, userid);
- if (info != null && info.connection != null) {
- mContext.unbindService(info.connection);
- }
- }
-
- private class SettingsObserver extends ContentObserver {
- private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
- = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
-
- private SettingsObserver(Handler handler) {
- super(handler);
- }
-
- private void observe() {
- ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
- false, this, UserHandle.USER_ALL);
- update(null);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- update(uri);
- }
-
- private void update(Uri uri) {
- if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
- rebindListenerServices();
- }
- }
- }
-
- public class NotificationListenerInfo implements IBinder.DeathRecipient {
- public INotificationListener listener;
- public ComponentName component;
- public int userid;
- public boolean isSystem;
- public ServiceConnection connection;
- public int targetSdkVersion;
-
- public NotificationListenerInfo(INotificationListener listener, ComponentName component,
- int userid, boolean isSystem, int targetSdkVersion) {
- this.listener = listener;
- this.component = component;
- this.userid = userid;
- this.isSystem = isSystem;
- this.connection = null;
- this.targetSdkVersion = targetSdkVersion;
- }
-
- public NotificationListenerInfo(INotificationListener listener, ComponentName component,
- int userid, ServiceConnection connection, int targetSdkVersion) {
- this.listener = listener;
- this.component = component;
- this.userid = userid;
- this.isSystem = false;
- this.connection = connection;
- this.targetSdkVersion = targetSdkVersion;
- }
-
- public boolean enabledAndUserMatches(StatusBarNotification sbn) {
- final int nid = sbn.getUserId();
- if (!isEnabledForCurrentProfiles()) {
- return false;
- }
- if (this.userid == UserHandle.USER_ALL) return true;
- if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
- return supportsProfiles() && mUserProfiles.isCurrentProfile(nid);
- }
-
- public boolean supportsProfiles() {
- return targetSdkVersion >= Build.VERSION_CODES.L;
- }
-
- public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
- if (!enabledAndUserMatches(sbn)) {
- return;
- }
- try {
- listener.onNotificationPosted(sbn);
- } catch (RemoteException ex) {
- Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
- }
- }
-
- public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
- if (!enabledAndUserMatches(sbn)) return;
- try {
- listener.onNotificationRemoved(sbn);
- } catch (RemoteException ex) {
- Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
- }
- }
-
- @Override
- public void binderDied() {
- // Remove the listener, but don't unbind from the service. The system will bring the
- // service back up, and the onServiceConnected handler will readd the listener with the
- // new binding. If this isn't a bound service, and is just a registered
- // INotificationListener, just removing it from the list is all we need to do anyway.
- removeListenerImpl(this.listener, this.userid);
- }
-
- /** convenience method for looking in mEnabledListenersForCurrentProfiles */
- public boolean isEnabledForCurrentProfiles() {
- if (this.isSystem) return true;
- if (this.connection == null) return false;
- return mEnabledListenersForCurrentProfiles.contains(this.component);
- }
- }
-}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5f096cb..6e4eb56 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -22,6 +22,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
@@ -35,10 +36,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
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.database.ContentObserver;
import android.graphics.Bitmap;
@@ -49,15 +50,19 @@
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IInterface;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.INotificationListener;
+import android.service.notification.IConditionListener;
+import android.service.notification.IConditionProvider;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
+import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -65,7 +70,6 @@
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.Xml;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -78,7 +82,8 @@
import com.android.server.SystemService;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
-import com.android.server.notification.NotificationListeners.NotificationListenerInfo;
+import com.android.server.notification.ManagedServices.ManagedServiceInfo;
+import com.android.server.notification.ManagedServices.UserProfiles;
import com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -101,7 +106,6 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.NoSuchElementException;
/** {@hide} */
@@ -191,8 +195,9 @@
final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
- private NotificationListeners mListeners;
private final UserProfiles mUserProfiles = new UserProfiles();
+ private NotificationListeners mListeners;
+ private ConditionProviders mConditionProviders;
private final NotificationUsageStats mUsageStats = new NotificationUsageStats();
@@ -702,7 +707,7 @@
String pkgList[] = null;
boolean queryReplace = queryRemove &&
intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- if (DBG) Slog.i(TAG, "queryReplace=" + queryReplace);
+ if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
} else if (queryRestart) {
@@ -745,6 +750,7 @@
}
}
mListeners.onPackagesChanged(queryReplace, pkgList);
+ mConditionProviders.onPackagesChanged(queryReplace, pkgList);
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
// Keep track of screen on/off state, but do not turn off the notification light
// until user passes through the lock screen or views the notification.
@@ -834,7 +840,7 @@
mHandler = new WorkerHandler();
mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
- mZenModeHelper.setCallback(new ZenModeHelper.Callback() {
+ mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
public void onConfigChanged() {
savePolicyFile();
@@ -845,18 +851,9 @@
importOldBlockDb();
- mListeners = new NotificationListeners(getContext(),
- mHandler, mNotificationList, mUserProfiles) {
- @Override
- public void onServiceAdded(INotificationListener listener) {
- final String[] keys = getActiveNotificationKeysFromListener(listener);
- try {
- listener.onListenerConnected(keys);
- } catch (RemoteException e) {
- // we tried
- }
- }
- };
+ mListeners = new NotificationListeners();
+ mConditionProviders = new ConditionProviders(getContext(),
+ mHandler, mUserProfiles, mZenModeHelper);
mStatusBar = getLocalService(StatusBarManagerInternal.class);
mStatusBar.setNotificationDelegate(mNotificationDelegate);
@@ -972,6 +969,7 @@
// bind to listener services.
mSettingsObserver.observe();
mListeners.onBootPhaseAppsCanStart();
+ mConditionProviders.onBootPhaseAppsCanStart();
}
}
@@ -1005,8 +1003,7 @@
return ;
}
- final boolean isSystemToast =
- NotificationUtil.isCallerSystem() || ("android".equals(pkg));
+ final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
if (!isSystemToast) {
@@ -1097,7 +1094,7 @@
@Override
public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
- NotificationUtil.checkCallerIsSystemOrSameApp(pkg);
+ checkCallerIsSystemOrSameApp(pkg);
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
// Don't allow client applications to cancel foreground service notis.
@@ -1109,7 +1106,7 @@
@Override
public void cancelAllNotifications(String pkg, int userId) {
- NotificationUtil.checkCallerIsSystemOrSameApp(pkg);
+ checkCallerIsSystemOrSameApp(pkg);
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
@@ -1123,7 +1120,7 @@
@Override
public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
- NotificationUtil.checkCallerIsSystem();
+ checkCallerIsSystem();
setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
}
@@ -1133,7 +1130,7 @@
*/
@Override
public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
- NotificationUtil.checkCallerIsSystem();
+ checkCallerIsSystem();
return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
== AppOpsManager.MODE_ALLOWED);
}
@@ -1201,8 +1198,8 @@
@Override
public void registerListener(final INotificationListener listener,
final ComponentName component, final int userid) {
- NotificationUtil.checkCallerIsSystem();
- mListeners.registerListener(listener, component, userid);
+ checkCallerIsSystem();
+ mListeners.registerService(listener, component, userid);
}
/**
@@ -1210,7 +1207,7 @@
*/
@Override
public void unregisterListener(INotificationListener listener, int userid) {
- mListeners.unregisterListener(listener, userid);
+ mListeners.unregisterService(listener, userid);
}
/**
@@ -1227,8 +1224,7 @@
long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationList) {
- final NotificationListenerInfo info =
- mListeners.checkListenerTokenLocked(token);
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
if (keys != null) {
final int N = keys.length;
for (int i = 0; i < N; i++) {
@@ -1237,7 +1233,7 @@
if (userId != info.userid && userId != UserHandle.USER_ALL &&
!mUserProfiles.isCurrentProfile(userId)) {
throw new SecurityException("Disallowed call from listener: "
- + info.listener);
+ + info.service);
}
if (r != null) {
cancelNotificationFromListenerLocked(info, callingUid, callingPid,
@@ -1255,7 +1251,7 @@
}
}
- private void cancelNotificationFromListenerLocked(NotificationListenerInfo info,
+ private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
@@ -1278,8 +1274,7 @@
long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationList) {
- final NotificationListenerInfo info =
- mListeners.checkListenerTokenLocked(token);
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
if (info.supportsProfiles()) {
Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
+ "from " + info.component
@@ -1305,14 +1300,14 @@
public StatusBarNotification[] getActiveNotificationsFromListener(
INotificationListener token, String[] keys) {
synchronized (mNotificationList) {
- final NotificationListenerInfo info = mListeners.checkListenerTokenLocked(token);
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
final ArrayList<StatusBarNotification> list
= new ArrayList<StatusBarNotification>();
if (keys == null) {
final int N = mNotificationList.size();
for (int i=0; i<N; i++) {
StatusBarNotification sbn = mNotificationList.get(i).sbn;
- if (info.enabledAndUserMatches(sbn)) {
+ if (info.enabledAndUserMatches(sbn.getUserId())) {
list.add(sbn);
}
}
@@ -1320,7 +1315,7 @@
final int N = keys.length;
for (int i=0; i<N; i++) {
NotificationRecord r = mNotificationsByKey.get(keys[i]);
- if (r != null && info.enabledAndUserMatches(r.sbn)) {
+ if (r != null && info.enabledAndUserMatches(r.sbn.getUserId())) {
list.add(r.sbn);
}
}
@@ -1336,17 +1331,48 @@
@Override
public ZenModeConfig getZenModeConfig() {
- NotificationUtil.checkCallerIsSystem();
+ checkCallerIsSystem();
return mZenModeHelper.getConfig();
}
@Override
public boolean setZenModeConfig(ZenModeConfig config) {
- NotificationUtil.checkCallerIsSystem();
+ checkCallerIsSystem();
return mZenModeHelper.setConfig(config);
}
@Override
+ public void notifyConditions(String pkg, IConditionProvider provider,
+ Condition[] conditions) {
+ final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
+ checkCallerIsSystemOrSameApp(pkg);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mConditionProviders.notifyConditions(pkg, info, conditions);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void requestZenModeConditions(IConditionListener callback, boolean requested) {
+ enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
+ mConditionProviders.requestZenModeConditions(callback, requested);
+ }
+
+ @Override
+ public void setZenModeCondition(Uri conditionId) {
+ enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
+ mConditionProviders.setZenModeCondition(conditionId);
+ }
+
+ private void enforceSystemOrSystemUI(String message) {
+ if (isCallerSystem()) return;
+ getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ message);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -1362,12 +1388,12 @@
private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
synchronized (mNotificationList) {
- final NotificationListenerInfo info = mListeners.checkListenerTokenLocked(token);
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
final ArrayList<String> keys = new ArrayList<String>();
final int N = mNotificationList.size();
for (int i=0; i<N; i++) {
final StatusBarNotification sbn = mNotificationList.get(i).sbn;
- if (info.enabledAndUserMatches(sbn)) {
+ if (info.enabledAndUserMatches(sbn.getUserId())) {
keys.add(sbn.getKey());
}
}
@@ -1378,8 +1404,6 @@
void dumpImpl(PrintWriter pw) {
pw.println("Current Notification Manager state:");
- mListeners.dump(pw);
-
int N;
synchronized (mToastQueue) {
@@ -1433,6 +1457,12 @@
pw.println("\n Zen Mode:");
mZenModeHelper.dump(pw, " ");
+
+ pw.println("\n Notification listeners:");
+ mListeners.dump(pw);
+
+ pw.println("\n Condition providers:");
+ mConditionProviders.dump(pw);
}
}
@@ -1455,9 +1485,8 @@
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
+ " notification=" + notification);
}
- NotificationUtil.checkCallerIsSystemOrSameApp(pkg);
- final boolean isSystemNotification =
- NotificationUtil.isUidSystem(callingUid) || ("android".equals(pkg));
+ checkCallerIsSystemOrSameApp(pkg);
+ final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
final int userId = ActivityManager.handleIncomingUser(callingPid,
callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
@@ -2028,7 +2057,7 @@
void cancelNotification(final int callingUid, final int callingPid,
final String pkg, final String tag, final int id,
final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
- final int userId, final int reason, final NotificationListenerInfo listener) {
+ final int userId, final int reason, final ManagedServiceInfo listener) {
// In enqueueNotificationInternal notifications are added by scheduling the
// work on the worker handler. Hence, we also schedule the cancel on this
// handler to avoid a scenario where an add notification call followed by a
@@ -2099,7 +2128,7 @@
*/
boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
int mustNotHaveFlags, boolean doit, int userId, int reason,
- NotificationListenerInfo listener) {
+ ManagedServiceInfo listener) {
EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
listener == null ? null : listener.component.toShortString());
@@ -2141,7 +2170,7 @@
}
void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
- NotificationListenerInfo listener, boolean includeCurrentProfiles) {
+ ManagedServiceInfo listener, boolean includeCurrentProfiles) {
EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
null, userId, 0, 0, reason,
listener == null ? null : listener.component.toShortString());
@@ -2234,38 +2263,129 @@
}
}
- public static class UserProfiles {
- // Profiles of the current user.
- private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
+ private static boolean isUidSystem(int uid) {
+ final int appid = UserHandle.getAppId(uid);
+ return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
+ }
- private void updateCache(Context context) {
- UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- if (userManager != null) {
- int currentUserId = ActivityManager.getCurrentUser();
- List<UserInfo> profiles = userManager.getProfiles(currentUserId);
- synchronized (mCurrentProfiles) {
- mCurrentProfiles.clear();
- for (UserInfo user : profiles) {
- mCurrentProfiles.put(user.id, user);
+ private static boolean isCallerSystem() {
+ return isUidSystem(Binder.getCallingUid());
+ }
+
+ private static void checkCallerIsSystem() {
+ if (isCallerSystem()) {
+ return;
+ }
+ throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
+ }
+
+ private static void checkCallerIsSystemOrSameApp(String pkg) {
+ if (isCallerSystem()) {
+ return;
+ }
+ final int uid = Binder.getCallingUid();
+ try {
+ ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
+ pkg, 0, UserHandle.getCallingUserId());
+ if (!UserHandle.isSameApp(ai.uid, uid)) {
+ throw new SecurityException("Calling uid " + uid + " gave package"
+ + pkg + " which is owned by uid " + ai.uid);
+ }
+ } catch (RemoteException re) {
+ throw new SecurityException("Unknown package " + pkg + "\n" + re);
+ }
+ }
+
+ public class NotificationListeners extends ManagedServices {
+
+ public NotificationListeners() {
+ super(getContext(), mHandler, mNotificationList, mUserProfiles);
+ }
+
+ @Override
+ protected Config getConfig() {
+ Config c = new Config();
+ c.caption = "notification listener";
+ c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
+ c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
+ c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
+ c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
+ c.clientLabel = R.string.notification_listener_binding_label;
+ return c;
+ }
+
+ @Override
+ protected IInterface asInterface(IBinder binder) {
+ return INotificationListener.Stub.asInterface(binder);
+ }
+
+ @Override
+ public void onServiceAdded(IInterface service) {
+ final INotificationListener listener = (INotificationListener) service;
+ final String[] keys = getActiveNotificationKeysFromListener(listener);
+ try {
+ listener.onListenerConnected(keys);
+ } catch (RemoteException e) {
+ // we tried
+ }
+ }
+
+ /**
+ * asynchronously notify all listeners about a new notification
+ */
+ public void notifyPostedLocked(StatusBarNotification sbn) {
+ // make a copy in case changes are made to the underlying Notification object
+ final StatusBarNotification sbnClone = sbn.clone();
+ for (final ManagedServiceInfo info : mServices) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyPostedIfUserMatch(info, sbnClone);
}
- }
+ });
}
}
- public int[] getCurrentProfileIds() {
- synchronized (mCurrentProfiles) {
- int[] users = new int[mCurrentProfiles.size()];
- final int N = mCurrentProfiles.size();
- for (int i = 0; i < N; ++i) {
- users[i] = mCurrentProfiles.keyAt(i);
- }
- return users;
+ /**
+ * asynchronously notify all listeners about a removed notification
+ */
+ public void notifyRemovedLocked(StatusBarNotification sbn) {
+ // make a copy in case changes are made to the underlying Notification object
+ // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
+ // notification
+ final StatusBarNotification sbnLight = sbn.cloneLight();
+ for (ManagedServiceInfo serviceInfo : mServices) {
+ final ManagedServiceInfo info = (ManagedServiceInfo) serviceInfo;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyRemovedIfUserMatch(info, sbnLight);
+ }
+ });
}
}
- public boolean isCurrentProfile(int userId) {
- synchronized (mCurrentProfiles) {
- return mCurrentProfiles.get(userId) != null;
+ private void notifyPostedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
+ if (!info.enabledAndUserMatches(sbn.getUserId())) {
+ return;
+ }
+ final INotificationListener listener = (INotificationListener)info.service;
+ try {
+ listener.onNotificationPosted(sbn);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
+ }
+ }
+
+ private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
+ if (!info.enabledAndUserMatches(sbn.getUserId())) {
+ return;
+ }
+ final INotificationListener listener = (INotificationListener)info.service;
+ try {
+ listener.onNotificationRemoved(sbn);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationUtil.java b/services/core/java/com/android/server/notification/NotificationUtil.java
deleted file mode 100644
index 459adce..0000000
--- a/services/core/java/com/android/server/notification/NotificationUtil.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import android.app.AppGlobals;
-import android.content.pm.ApplicationInfo;
-import android.os.Binder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.UserHandle;
-
-public class NotificationUtil {
-
- // Return true if the UID is a system or phone UID and therefore should not have
- // any notifications or toasts blocked.
- public static boolean isUidSystem(int uid) {
- final int appid = UserHandle.getAppId(uid);
- return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
- }
-
- // same as isUidSystem(int, int) for the Binder caller's UID.
- public static boolean isCallerSystem() {
- return isUidSystem(Binder.getCallingUid());
- }
-
- public static void checkCallerIsSystem() {
- if (isCallerSystem()) {
- return;
- }
- throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
- }
-
- public static void checkCallerIsSystemOrSameApp(String pkg) {
- if (isCallerSystem()) {
- return;
- }
- final int uid = Binder.getCallingUid();
- try {
- ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
- pkg, 0, UserHandle.getCallingUserId());
- if (!UserHandle.isSameApp(ai.uid, uid)) {
- throw new SecurityException("Calling uid " + uid + " gave package"
- + pkg + " which is owned by uid " + ai.uid);
- }
- } catch (RemoteException re) {
- throw new SecurityException("Unknown package " + pkg + "\n" + re);
- }
- }
-}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 80f5b5c..154ac96 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -46,6 +46,7 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
@@ -69,8 +70,8 @@
private final SettingsObserver mSettingsObserver;
private final AppOpsManager mAppOps;
private final ZenModeConfig mDefaultConfig;
+ private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
- private Callback mCallback;
private int mZenMode;
private ZenModeConfig mConfig;
@@ -83,6 +84,9 @@
"com.google.android.talk",
"com.android.mms"
));
+ private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
+ "com.google.android.deskclock"
+ ));
public ZenModeHelper(Context context, Handler handler) {
mContext = context;
@@ -115,12 +119,15 @@
return new ZenModeConfig();
}
- public void setCallback(Callback callback) {
- mCallback = callback;
+ public void addCallback(Callback callback) {
+ mCallbacks.add(callback);
}
public boolean shouldIntercept(String pkg, Notification n) {
if (mZenMode != Global.ZEN_MODE_OFF) {
+ if (isAlarm(pkg, n)) {
+ return false;
+ }
if (isCall(pkg, n)) {
return !mConfig.allowCalls;
}
@@ -132,6 +139,14 @@
return false;
}
+ public int getZenMode() {
+ return mZenMode;
+ }
+
+ public void setZenMode(int zenModeValue) {
+ Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue);
+ }
+
public void updateZenMode() {
final int mode = Global.getInt(mContext.getContentResolver(),
Global.ZEN_MODE, Global.ZEN_MODE_OFF);
@@ -157,6 +172,7 @@
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
zen ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
exceptionPackages);
+ dispatchOnZenModeChanged();
}
public boolean allowDisable(int what, IBinder token, String pkg) {
@@ -193,7 +209,7 @@
if (config.equals(mConfig)) return true;
mConfig = config;
Slog.d(TAG, "mConfig=" + mConfig);
- if (mCallback != null) mCallback.onConfigChanged();
+ dispatchOnConfigChanged();
final String val = Integer.toString(mConfig.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
updateAlarms();
@@ -201,6 +217,22 @@
return true;
}
+ private void dispatchOnConfigChanged() {
+ for (Callback callback : mCallbacks) {
+ callback.onConfigChanged();
+ }
+ }
+
+ private void dispatchOnZenModeChanged() {
+ for (Callback callback : mCallbacks) {
+ callback.onZenModeChanged();
+ }
+ }
+
+ private boolean isAlarm(String pkg, Notification n) {
+ return ALARM_PACKAGES.contains(pkg);
+ }
+
private boolean isCall(String pkg, Notification n) {
return CALL_PACKAGES.contains(pkg);
}
@@ -300,13 +332,14 @@
if (skip) {
Slog.d(TAG, "Skipping zen mode update for the weekend");
} else {
- Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue);
+ ZenModeHelper.this.setZenMode(zenModeValue);
}
updateAlarms();
}
}
- public interface Callback {
- void onConfigChanged();
+ public static class Callback {
+ void onConfigChanged() {}
+ void onZenModeChanged() {}
}
}
diff --git a/services/core/java/com/android/server/pm/ForwardingIntentFilter.java b/services/core/java/com/android/server/pm/ForwardingIntentFilter.java
new file mode 100644
index 0000000..aba796b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ForwardingIntentFilter.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+import android.content.IntentFilter;
+import android.util.Log;
+import java.io.IOException;
+import android.os.UserHandle;
+
+/**
+ * The {@link PackageManagerService} maintains some {@link ForwardingIntentFilter}s for every user.
+ * If an {@link Intent} matches the {@link ForwardingIntentFilter}, then it can be forwarded to the
+ * {@link #mUserIdDest}.
+ */
+class ForwardingIntentFilter extends IntentFilter {
+ private static final String ATTR_USER_ID_DEST = "userIdDest";
+ private static final String ATTR_FILTER = "filter";
+
+ private static final String TAG = "ForwardingIntentFilter";
+
+ // If the intent matches the IntentFilter, then it can be forwarded to this userId.
+ final int mUserIdDest;
+
+ ForwardingIntentFilter(IntentFilter filter, int userIdDest) {
+ super(filter);
+ mUserIdDest = userIdDest;
+ }
+
+ public int getUserIdDest() {
+ return mUserIdDest;
+ }
+
+ ForwardingIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException {
+ String userIdDestString = parser.getAttributeValue(null, ATTR_USER_ID_DEST);
+ if (userIdDestString == null) {
+ String msg = "Missing element under " + TAG +": " + ATTR_USER_ID_DEST + " at " +
+ parser.getPositionDescription();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ mUserIdDest = UserHandle.USER_NULL;
+ } else {
+ mUserIdDest = Integer.parseInt(userIdDestString);
+ }
+ int outerDepth = parser.getDepth();
+ String tagName = parser.getName();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ tagName = parser.getName();
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ } else if (type == XmlPullParser.START_TAG) {
+ if (tagName.equals(ATTR_FILTER)) {
+ break;
+ } else {
+ String msg = "Unknown element under " + Settings.TAG_FORWARDING_INTENT_FILTERS
+ + ": " + tagName + " at " + parser.getPositionDescription();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+ if (tagName.equals(ATTR_FILTER)) {
+ readFromXml(parser);
+ } else {
+ String msg = "Missing element under " + TAG + ": " + ATTR_FILTER +
+ " at " + parser.getPositionDescription();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ public void writeToXml(XmlSerializer serializer) throws IOException {
+ serializer.attribute(null, ATTR_USER_ID_DEST, Integer.toString(mUserIdDest));
+ serializer.startTag(null, ATTR_FILTER);
+ super.writeToXml(serializer);
+ serializer.endTag(null, ATTR_FILTER);
+ }
+
+ @Override
+ public String toString() {
+ return "ForwardingIntentFilter{0x" + Integer.toHexString(System.identityHashCode(this))
+ + " " + Integer.toString(mUserIdDest) + "}";
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ForwardingIntentResolver.java b/services/core/java/com/android/server/pm/ForwardingIntentResolver.java
new file mode 100644
index 0000000..1616395
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ForwardingIntentResolver.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.server.pm;
+
+
+import java.io.PrintWriter;
+import com.android.server.IntentResolver;
+import java.util.List;
+
+/**
+ * Used to find a list of {@link ForwardingIntentFilter}s that match an intent.
+ */
+class ForwardingIntentResolver
+ extends IntentResolver<ForwardingIntentFilter, ForwardingIntentFilter> {
+ @Override
+ protected ForwardingIntentFilter[] newArray(int size) {
+ return new ForwardingIntentFilter[size];
+ }
+
+ @Override
+ protected boolean isPackageForFilter(String packageName, ForwardingIntentFilter filter) {
+ return false;
+ }
+
+ @Override
+ protected void sortResults(List<ForwardingIntentFilter> results) {
+ //We don't sort the results
+ }
+}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index ff816ea..82d3f53 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -211,7 +211,7 @@
return execute(builder.toString());
}
- public int dexopt(String apkPath, int uid, boolean isPublic) {
+ public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
StringBuilder builder = new StringBuilder("dexopt");
builder.append(' ');
builder.append(apkPath);
@@ -219,10 +219,13 @@
builder.append(uid);
builder.append(isPublic ? " 1" : " 0");
builder.append(" *"); // No pkgName arg present
+ builder.append(' ');
+ builder.append(instructionSet);
return execute(builder.toString());
}
- public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName) {
+ public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
+ String instructionSet) {
StringBuilder builder = new StringBuilder("dexopt");
builder.append(' ');
builder.append(apkPath);
@@ -231,6 +234,8 @@
builder.append(isPublic ? " 1" : " 0");
builder.append(' ');
builder.append(pkgName);
+ builder.append(' ');
+ builder.append(instructionSet);
return execute(builder.toString());
}
@@ -245,19 +250,23 @@
return execute(builder.toString());
}
- public int movedex(String srcPath, String dstPath) {
+ public int movedex(String srcPath, String dstPath, String instructionSet) {
StringBuilder builder = new StringBuilder("movedex");
builder.append(' ');
builder.append(srcPath);
builder.append(' ');
builder.append(dstPath);
+ builder.append(' ');
+ builder.append(instructionSet);
return execute(builder.toString());
}
- public int rmdex(String codePath) {
+ public int rmdex(String codePath, String instructionSet) {
StringBuilder builder = new StringBuilder("rmdex");
builder.append(' ');
builder.append(codePath);
+ builder.append(' ');
+ builder.append(instructionSet);
return execute(builder.toString());
}
@@ -344,7 +353,7 @@
}
public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
- String fwdLockApkPath, String asecPath, PackageStats pStats) {
+ String fwdLockApkPath, String asecPath, String instructionSet, PackageStats pStats) {
StringBuilder builder = new StringBuilder("getsize");
builder.append(' ');
builder.append(pkgName);
@@ -358,6 +367,8 @@
builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
builder.append(' ');
builder.append(asecPath != null ? asecPath : "!");
+ builder.append(' ');
+ builder.append(instructionSet);
String s = transaction(builder.toString());
String res[] = s.split(" ");
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 6030d4d..ab63c9c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -16,14 +16,18 @@
package com.android.server.pm;
+import android.app.AppGlobals;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
import android.content.pm.ILauncherApps;
import android.content.pm.IOnAppsChangedListener;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.graphics.Rect;
@@ -170,6 +174,34 @@
}
@Override
+ public boolean isPackageEnabled(String packageName, UserHandle user)
+ throws RemoteException {
+ ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ PackageInfo info = pm.getPackageInfo(packageName, 0, user.getIdentifier());
+ return info != null && info.applicationInfo.enabled;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public boolean isActivityEnabled(ComponentName component, UserHandle user)
+ throws RemoteException {
+ ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
+ return info != null && info.isEnabled();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void startActivityAsUser(ComponentName component, Rect sourceBounds,
Bundle opts, UserHandle user) throws RemoteException {
ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7683205..a133d42 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -23,14 +23,17 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.system.OsConstants.S_IRWXU;
+import static android.system.OsConstants.S_IRGRP;
+import static android.system.OsConstants.S_IXGRP;
+import static android.system.OsConstants.S_IROTH;
+import static android.system.OsConstants.S_IXOTH;
+import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER;
+import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.internal.util.ArrayUtils.removeInt;
-import static libcore.io.OsConstants.S_IRWXU;
-import static libcore.io.OsConstants.S_IRGRP;
-import static libcore.io.OsConstants.S_IXGRP;
-import static libcore.io.OsConstants.S_IROTH;
-import static libcore.io.OsConstants.S_IXOTH;
+import android.content.pm.PackageParser.*;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
@@ -80,7 +83,6 @@
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.ActivityIntentInfo;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
@@ -119,6 +121,9 @@
import android.os.UserManager;
import android.security.KeyStore;
import android.security.SystemKeyStore;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -142,6 +147,8 @@
import java.io.PrintWriter;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -157,12 +164,11 @@
import java.util.Map;
import java.util.Set;
-import libcore.io.ErrnoException;
+import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
-import libcore.io.Libcore;
-import libcore.io.StructStat;
import com.android.internal.R;
+import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.storage.DeviceStorageMonitorInternal;
/**
@@ -267,6 +273,8 @@
static final String mTempContainerPrefix = "smdl2tmp";
+ private static String sPreferredInstructionSet;
+
final ServiceThread mHandlerThread;
private static final String IDMAP_PREFIX = "/data/resource-cache/";
@@ -1239,27 +1247,38 @@
boolean didDexOpt = false;
+ final List<String> instructionSets = getAllInstructionSets();
+
/**
* Ensure all external libraries have had dexopt run on them.
*/
if (mSharedLibraries.size() > 0) {
- Iterator<SharedLibraryEntry> libs = mSharedLibraries.values().iterator();
- while (libs.hasNext()) {
- String lib = libs.next().path;
- if (lib == null) {
- continue;
- }
- try {
- if (dalvik.system.DexFile.isDexOptNeededInternal(lib, null, false)) {
- alreadyDexOpted.add(lib);
- mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
- didDexOpt = true;
+ // NOTE: For now, we're compiling these system "shared libraries"
+ // (and framework jars) into all available architectures. It's possible
+ // to compile them only when we come across an app that uses them (there's
+ // already logic for that in scanPackageLI) but that adds some complexity.
+ for (String instructionSet : instructionSets) {
+ for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
+ final String lib = libEntry.path;
+ if (lib == null) {
+ continue;
}
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Library not found: " + lib);
- } catch (IOException e) {
- Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
- + e.getMessage());
+
+ try {
+ if (dalvik.system.DexFile.isDexOptNeededInternal(
+ lib, null, instructionSet, false)) {
+ alreadyDexOpted.add(lib);
+
+ // The list of "shared libraries" we have at this point is
+ mInstaller.dexopt(lib, Process.SYSTEM_UID, true, instructionSet);
+ didDexOpt = true;
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Library not found: " + lib);
+ } catch (IOException e) {
+ Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
+ + e.getMessage());
+ }
}
}
}
@@ -1282,49 +1301,37 @@
*/
String[] frameworkFiles = frameworkDir.list();
if (frameworkFiles != null) {
- for (int i=0; i<frameworkFiles.length; i++) {
- File libPath = new File(frameworkDir, frameworkFiles[i]);
- String path = libPath.getPath();
- // Skip the file if we alrady did it.
- if (alreadyDexOpted.contains(path)) {
- continue;
- }
- // Skip the file if it is not a type we want to dexopt.
- if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
- continue;
- }
- try {
- if (dalvik.system.DexFile.isDexOptNeededInternal(path, null, false)) {
- mInstaller.dexopt(path, Process.SYSTEM_UID, true);
- didDexOpt = true;
+ // TODO: We could compile these only for the most preferred ABI. We should
+ // first double check that the dex files for these commands are not referenced
+ // by other system apps.
+ for (String instructionSet : instructionSets) {
+ for (int i=0; i<frameworkFiles.length; i++) {
+ File libPath = new File(frameworkDir, frameworkFiles[i]);
+ String path = libPath.getPath();
+ // Skip the file if we already did it.
+ if (alreadyDexOpted.contains(path)) {
+ continue;
}
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Jar not found: " + path);
- } catch (IOException e) {
- Slog.w(TAG, "Exception reading jar: " + path, e);
+ // Skip the file if it is not a type we want to dexopt.
+ if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
+ continue;
+ }
+ try {
+ if (dalvik.system.DexFile.isDexOptNeededInternal(path, null, instructionSet, false)) {
+ mInstaller.dexopt(path, Process.SYSTEM_UID, true, instructionSet);
+ didDexOpt = true;
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Jar not found: " + path);
+ } catch (IOException e) {
+ Slog.w(TAG, "Exception reading jar: " + path, e);
+ }
}
}
}
if (didDexOpt) {
- File dalvikCacheDir = new File(dataDir, "dalvik-cache");
-
- // If we had to do a dexopt of one of the previous
- // things, then something on the system has changed.
- // Consider this significant, and wipe away all other
- // existing dexopt files to ensure we don't leave any
- // dangling around.
- String[] files = dalvikCacheDir.list();
- if (files != null) {
- for (int i=0; i<files.length; i++) {
- String fn = files[i];
- if (fn.startsWith("data@app@")
- || fn.startsWith("data@app-private@")) {
- Slog.i(TAG, "Pruning dalvik file: " + fn);
- (new File(dalvikCacheDir, fn)).delete();
- }
- }
- }
+ pruneDexFiles(new File(dataDir, "dalvik-cache"));
}
// Collect vendor overlay packages.
@@ -1502,6 +1509,12 @@
// the correct library paths.
updateAllSharedLibrariesLPw();
+
+ for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
+ adjustCpuAbisForSharedUserLPw(setting.packages, true /* do dexopt */,
+ false /* force dexopt */, false /* defer dexopt */);
+ }
+
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
Slog.i(TAG, "Time to scan packages: "
@@ -1532,6 +1545,9 @@
mSettings.readDefaultPreferredAppsLPw(this, 0);
}
+ // All the changes are done during package scanning.
+ mSettings.updateInternalDatabaseVersion();
+
// can downgrade to reader
mSettings.writeLPr();
@@ -1548,6 +1564,37 @@
} // synchronized (mInstallLock)
}
+ private static void pruneDexFiles(File cacheDir) {
+ // If we had to do a dexopt of one of the previous
+ // things, then something on the system has changed.
+ // Consider this significant, and wipe away all other
+ // existing dexopt files to ensure we don't leave any
+ // dangling around.
+ //
+ // Additionally, delete all dex files from the root directory
+ // since there shouldn't be any there anyway.
+ File[] files = cacheDir.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (!file.isDirectory()) {
+ Slog.i(TAG, "Pruning dalvik file: " + file.getAbsolutePath());
+ file.delete();
+ } else {
+ File[] subDirList = file.listFiles();
+ if (subDirList != null) {
+ for (File subDirFile : subDirList) {
+ final String fn = subDirFile.getName();
+ if (fn.startsWith("data@app@") || fn.startsWith("data@app-private@")) {
+ Slog.i(TAG, "Pruning dalvik file: " + fn);
+ subDirFile.delete();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
public boolean isFirstBoot() {
return !mRestoredSettings;
}
@@ -1837,7 +1884,6 @@
PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- PackageInfo pi;
final PackageSetting ps = (PackageSetting) p.mExtras;
if (ps == null) {
return null;
@@ -2160,6 +2206,24 @@
}
@Override
+ public boolean activitySupportsIntent(ComponentName component, Intent intent,
+ String resolvedType) {
+ synchronized (mPackages) {
+ PackageParser.Activity a = mActivities.mActivities.get(component);
+ if (a == null) {
+ return false;
+ }
+ for (int i=0; i<a.intents.size(); i++) {
+ if (a.intents.get(i).match(intent.getAction(), resolvedType, intent.getScheme(),
+ intent.getData(), intent.getCategories(), TAG) >= 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ @Override
public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get receiver info");
@@ -2697,6 +2761,59 @@
return PackageManager.SIGNATURE_NO_MATCH;
}
+ /**
+ * If the database version for this type of package (internal storage or
+ * external storage) is less than the version where package signatures
+ * were updated, return true.
+ */
+ private boolean isCompatSignatureUpdateNeeded(PackageParser.Package scannedPkg) {
+ return (isExternal(scannedPkg) && mSettings.isExternalDatabaseVersionOlderThan(
+ DatabaseVersion.SIGNATURE_END_ENTITY))
+ || (!isExternal(scannedPkg) && mSettings.isInternalDatabaseVersionOlderThan(
+ DatabaseVersion.SIGNATURE_END_ENTITY));
+ }
+
+ /**
+ * Used for backward compatibility to make sure any packages with
+ * certificate chains get upgraded to the new style. {@code existingSigs}
+ * will be in the old format (since they were stored on disk from before the
+ * system upgrade) and {@code scannedSigs} will be in the newer format.
+ */
+ private int compareSignaturesCompat(PackageSignatures existingSigs,
+ PackageParser.Package scannedPkg) {
+ if (!isCompatSignatureUpdateNeeded(scannedPkg)) {
+ return PackageManager.SIGNATURE_NO_MATCH;
+ }
+
+ HashSet<Signature> existingSet = new HashSet<Signature>();
+ for (Signature sig : existingSigs.mSignatures) {
+ existingSet.add(sig);
+ }
+ HashSet<Signature> scannedCompatSet = new HashSet<Signature>();
+ for (Signature sig : scannedPkg.mSignatures) {
+ try {
+ Signature[] chainSignatures = sig.getChainSignatures();
+ for (Signature chainSig : chainSignatures) {
+ scannedCompatSet.add(chainSig);
+ }
+ } catch (CertificateEncodingException e) {
+ scannedCompatSet.add(sig);
+ }
+ }
+ /*
+ * Make sure the expanded scanned set contains all signatures in the
+ * existing one.
+ */
+ if (scannedCompatSet.equals(existingSet)) {
+ // Migrate the old signatures to the new scheme.
+ existingSigs.assignSignatures(scannedPkg.mSignatures);
+ // The new KeySets will be re-added later in the scanning process.
+ mSettings.mKeySetManager.removeAppKeySetData(scannedPkg.packageName);
+ return PackageManager.SIGNATURE_MATCH;
+ }
+ return PackageManager.SIGNATURE_NO_MATCH;
+ }
+
public String[] getPackagesForUid(int uid) {
uid = UserHandle.getAppId(uid);
// reader
@@ -3047,6 +3164,33 @@
return null;
}
+ /*
+ * Returns if intent can be forwarded from the userId from to dest
+ */
+ @Override
+ public boolean canForwardTo(Intent intent, String resolvedType, int userIdFrom, int userIdDest) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+ List<ForwardingIntentFilter> matches =
+ getMatchingForwardingIntentFilters(intent, resolvedType, userIdFrom);
+ if (matches != null) {
+ int size = matches.size();
+ for (int i = 0; i < size; i++) {
+ if (matches.get(i).getUserIdDest() == userIdDest) return true;
+ }
+ }
+ return false;
+ }
+
+ private List<ForwardingIntentFilter> getMatchingForwardingIntentFilters(Intent intent,
+ String resolvedType, int userId) {
+ ForwardingIntentResolver fir = mSettings.mForwardingIntentResolvers.get(userId);
+ if (fir != null) {
+ return fir.queryIntent(intent, resolvedType, false, userId);
+ }
+ return null;
+ }
+
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
@@ -3075,7 +3219,38 @@
synchronized (mPackages) {
final String pkgName = intent.getPackage();
if (pkgName == null) {
- return mActivities.queryIntent(intent, resolvedType, flags, userId);
+ List<ResolveInfo> result =
+ mActivities.queryIntent(intent, resolvedType, flags, userId);
+ // Checking if we can forward the intent to another user
+ List<ForwardingIntentFilter> fifs =
+ getMatchingForwardingIntentFilters(intent, resolvedType, userId);
+ if (fifs != null) {
+ ForwardingIntentFilter forwardingIntentFilterWithResult = null;
+ HashSet<Integer> alreadyTriedUserIds = new HashSet<Integer>();
+ for (ForwardingIntentFilter fif : fifs) {
+ int userIdDest = fif.getUserIdDest();
+ // Two {@link ForwardingIntentFilter}s can have the same userIdDest and
+ // match the same an intent. For performance reasons, it is better not to
+ // run queryIntent twice for the same userId
+ if (!alreadyTriedUserIds.contains(userIdDest)) {
+ List<ResolveInfo> resultUser = mActivities.queryIntent(intent,
+ resolvedType, flags, userIdDest);
+ if (resultUser != null) {
+ forwardingIntentFilterWithResult = fif;
+ // As soon as there is a match in another user, we add the
+ // intentForwarderActivity to the list of ResolveInfo.
+ break;
+ }
+ alreadyTriedUserIds.add(userIdDest);
+ }
+ }
+ if (forwardingIntentFilterWithResult != null) {
+ ResolveInfo forwardingResolveInfo = createForwardingResolveInfo(
+ forwardingIntentFilterWithResult, userId);
+ result.add(forwardingResolveInfo);
+ }
+ }
+ return result;
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
@@ -3086,6 +3261,28 @@
}
}
+ private ResolveInfo createForwardingResolveInfo(ForwardingIntentFilter fif, int userIdFrom) {
+ String className;
+ int userIdDest = fif.getUserIdDest();
+ if (userIdDest == UserHandle.USER_OWNER) {
+ className = FORWARD_INTENT_TO_USER_OWNER;
+ } else {
+ className = FORWARD_INTENT_TO_MANAGED_PROFILE;
+ }
+ ComponentName forwardingActivityComponentName = new ComponentName(
+ mAndroidApplication.packageName, className);
+ ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0,
+ userIdFrom);
+ ResolveInfo forwardingResolveInfo = new ResolveInfo();
+ forwardingResolveInfo.activityInfo = forwardingActivityInfo;
+ forwardingResolveInfo.priority = 0;
+ forwardingResolveInfo.preferredOrder = 0;
+ forwardingResolveInfo.match = 0;
+ forwardingResolveInfo.isDefault = true;
+ forwardingResolveInfo.filter = fif;
+ return forwardingResolveInfo;
+ }
+
@Override
public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
@@ -3780,7 +3977,8 @@
PackageParser.Package pkg, File srcFile, int parseFlags) {
if (ps != null
&& ps.codePath.equals(srcFile)
- && ps.timeStamp == srcFile.lastModified()) {
+ && ps.timeStamp == srcFile.lastModified()
+ && !isCompatSignatureUpdateNeeded(pkg)) {
if (ps.signatures.mSignatures != null
&& ps.signatures.mSignatures.length != 0) {
// Optimization: reuse the existing cached certificates
@@ -3891,7 +4089,8 @@
+ " better than installed " + ps.versionCode);
InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
- ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);
+ ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString,
+ getAppInstructionSetFromSettings(ps));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
@@ -3955,7 +4154,8 @@
+ ps.codePathString + ": new version " + pkg.mVersionCode
+ " better than installed " + ps.versionCode);
InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
- ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);
+ ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString,
+ getAppInstructionSetFromSettings(ps));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
@@ -3989,8 +4189,6 @@
codePath = pkg.mScanPath;
// Set application objects path explicitly.
setApplicationInfoPaths(pkg, codePath, resPath);
- // Applications can run with the primary Cpu Abi unless otherwise is specified
- pkg.applicationInfo.requiredCpuAbi = null;
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
| SCAN_UPDATE_SIGNATURE, currentTime, user);
@@ -4030,22 +4228,32 @@
return processName;
}
- private boolean verifySignaturesLP(PackageSetting pkgSetting,
- PackageParser.Package pkg) {
+ private boolean verifySignaturesLP(PackageSetting pkgSetting, PackageParser.Package pkg) {
if (pkgSetting.signatures.mSignatures != null) {
// Already existing package. Make sure signatures match
- if (compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures) !=
- PackageManager.SIGNATURE_MATCH) {
- Slog.e(TAG, "Package " + pkg.packageName
- + " signatures do not match the previously installed version; ignoring!");
- mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
- return false;
- }
+ boolean match = compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures)
+ == PackageManager.SIGNATURE_MATCH;
+ if (!match) {
+ match = compareSignaturesCompat(pkgSetting.signatures, pkg)
+ == PackageManager.SIGNATURE_MATCH;
+ }
+ if (!match) {
+ Slog.e(TAG, "Package " + pkg.packageName
+ + " signatures do not match the previously installed version; ignoring!");
+ mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+ return false;
+ }
}
// Check for shared user signatures
if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
- if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
- pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
+ // Already existing package. Make sure signatures match
+ boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
+ pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
+ if (!match) {
+ match = compareSignaturesCompat(pkgSetting.sharedUser.signatures, pkg)
+ == PackageManager.SIGNATURE_MATCH;
+ }
+ if (!match) {
Slog.e(TAG, "Package " + pkg.packageName
+ " has no signatures that match those in shared user "
+ pkgSetting.sharedUser.name + "; ignoring!");
@@ -4070,12 +4278,16 @@
}
}
+ @Override
public void performBootDexOpt() {
- HashSet<PackageParser.Package> pkgs = null;
+ enforceSystemOrRoot("Only the system can request dexopt be performed");
+
+ final HashSet<PackageParser.Package> pkgs;
synchronized (mPackages) {
pkgs = mDeferredDexOpt;
mDeferredDexOpt = null;
}
+
if (pkgs != null) {
int i = 0;
for (PackageParser.Package pkg : pkgs) {
@@ -4092,16 +4304,17 @@
PackageParser.Package p = pkg;
synchronized (mInstallLock) {
if (!p.mDidDexOpt) {
- performDexOptLI(p, false, false, true);
+ performDexOptLI(p, false /* force dex */, false /* defer */,
+ true /* include dependencies */);
}
}
}
}
}
+ @Override
public boolean performDexOpt(String packageName) {
enforceSystemOrRoot("Only the system can request dexopt be performed");
-
if (!mNoDexOpt) {
return false;
}
@@ -4114,12 +4327,13 @@
}
}
synchronized (mInstallLock) {
- return performDexOptLI(p, false, false, true) == DEX_OPT_PERFORMED;
+ return performDexOptLI(p, false /* force dex */, false /* defer */,
+ true /* include dependencies */) == DEX_OPT_PERFORMED;
}
}
- private void performDexOptLibsLI(ArrayList<String> libs, boolean forceDex, boolean defer,
- HashSet<String> done) {
+ private void performDexOptLibsLI(ArrayList<String> libs, String instructionSet, boolean forceDex,
+ boolean defer, HashSet<String> done) {
for (int i=0; i<libs.size(); i++) {
PackageParser.Package libPkg;
String libName;
@@ -4133,7 +4347,7 @@
}
}
if (libPkg != null && !done.contains(libName)) {
- performDexOptLI(libPkg, forceDex, defer, done);
+ performDexOptLI(libPkg, instructionSet, forceDex, defer, done);
}
}
}
@@ -4143,24 +4357,29 @@
static final int DEX_OPT_DEFERRED = 2;
static final int DEX_OPT_FAILED = -1;
- private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer,
- HashSet<String> done) {
- boolean performed = false;
+ private int performDexOptLI(PackageParser.Package pkg, String instructionSetOverride,
+ boolean forceDex,
+ boolean defer, HashSet<String> done) {
+ final String instructionSet = instructionSetOverride != null ?
+ instructionSetOverride : getAppInstructionSet(pkg.applicationInfo);
+
if (done != null) {
done.add(pkg.packageName);
if (pkg.usesLibraries != null) {
- performDexOptLibsLI(pkg.usesLibraries, forceDex, defer, done);
+ performDexOptLibsLI(pkg.usesLibraries, instructionSet, forceDex, defer, done);
}
if (pkg.usesOptionalLibraries != null) {
- performDexOptLibsLI(pkg.usesOptionalLibraries, forceDex, defer, done);
+ performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSet, forceDex, defer, done);
}
}
+
+ boolean performed = false;
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
String path = pkg.mScanPath;
int ret = 0;
try {
- if (forceDex || dalvik.system.DexFile.isDexOptNeededInternal(path, pkg.packageName,
- defer)) {
+ if (forceDex || dalvik.system.DexFile.isDexOptNeededInternal(path,
+ pkg.packageName, instructionSet, defer)) {
if (!forceDex && defer) {
if (mDeferredDexOpt == null) {
mDeferredDexOpt = new HashSet<PackageParser.Package>();
@@ -4168,10 +4387,12 @@
mDeferredDexOpt.add(pkg);
return DEX_OPT_DEFERRED;
} else {
- Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName);
+ Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName +
+ " (instructionSet=" + instructionSet + ")");
+
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
- pkg.packageName);
+ pkg.packageName, instructionSet);
pkg.mDidDexOpt = true;
performed = true;
}
@@ -4198,17 +4419,58 @@
return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
}
+ private String getAppInstructionSet(ApplicationInfo info) {
+ String instructionSet = getPreferredInstructionSet();
+
+ if (info.requiredCpuAbi != null) {
+ instructionSet = VMRuntime.getInstructionSet(info.requiredCpuAbi);
+ }
+
+ return instructionSet;
+ }
+
+ private String getAppInstructionSetFromSettings(PackageSetting ps) {
+ String instructionSet = getPreferredInstructionSet();
+
+ if (ps.requiredCpuAbiString != null) {
+ instructionSet = VMRuntime.getInstructionSet(ps.requiredCpuAbiString);
+ }
+
+ return instructionSet;
+ }
+
+ private static String getPreferredInstructionSet() {
+ if (sPreferredInstructionSet == null) {
+ sPreferredInstructionSet = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+ }
+
+ return sPreferredInstructionSet;
+ }
+
+ private static List<String> getAllInstructionSets() {
+ final String[] allAbis = Build.SUPPORTED_ABIS;
+ final List<String> allInstructionSets = new ArrayList<String>(allAbis.length);
+
+ for (String abi : allAbis) {
+ final String instructionSet = VMRuntime.getInstructionSet(abi);
+ if (!allInstructionSets.contains(instructionSet)) {
+ allInstructionSets.add(instructionSet);
+ }
+ }
+
+ return allInstructionSets;
+ }
+
private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer,
boolean inclDependencies) {
HashSet<String> done;
- boolean performed = false;
if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
done = new HashSet<String>();
done.add(pkg.packageName);
} else {
done = null;
}
- return performDexOptLI(pkg, forceDex, defer, done);
+ return performDexOptLI(pkg, null /* instruction set override */, forceDex, defer, done);
}
private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) {
@@ -4706,7 +4968,7 @@
if (dataPath.exists()) {
int currentUid = 0;
try {
- StructStat stat = Libcore.os.stat(dataPath.getPath());
+ StructStat stat = Os.stat(dataPath.getPath());
currentUid = stat.st_uid;
} catch (ErrnoException e) {
Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e);
@@ -4863,6 +5125,8 @@
Log.i(TAG, "removed obsolete native libraries for system package "
+ path);
}
+
+ setInternalAppAbi(pkg, pkgSetting);
} else {
if (!isForwardLocked(pkg) && !isExternal(pkg)) {
/*
@@ -4894,6 +5158,28 @@
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
return null;
}
+ } else {
+ // We don't have to copy the shared libraries if we're in the ASEC container
+ // but we still need to scan the file to figure out what ABI the app needs.
+ //
+ // TODO: This duplicates work done in the default container service. It's possible
+ // to clean this up but we'll need to change the interface between this service
+ // and IMediaContainerService (but doing so will spread this logic out, rather
+ // than centralizing it).
+ final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
+ final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
+ if (abi >= 0) {
+ pkg.applicationInfo.requiredCpuAbi = Build.SUPPORTED_ABIS[abi];
+ } else if (abi == PackageManager.NO_NATIVE_LIBRARIES) {
+ // Note that (non upgraded) system apps will not have any native
+ // libraries bundled in their APK, but we're guaranteed not to be
+ // such an app at this point.
+ pkg.applicationInfo.requiredCpuAbi = null;
+ } else {
+ mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ return null;
+ }
+ handle.close();
}
if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
@@ -4970,8 +5256,7 @@
}
if (allowed) {
if (!mSharedLibraries.containsKey(name)) {
- mSharedLibraries.put(name, new SharedLibraryEntry(null,
- pkg.packageName));
+ mSharedLibraries.put(name, new SharedLibraryEntry(null, pkg.packageName));
} else if (!name.equals(pkg.packageName)) {
Slog.w(TAG, "Package " + pkg.packageName + " library "
+ name + " already exists; skipping");
@@ -5044,6 +5329,12 @@
// writer
synchronized (mPackages) {
+ if ((scanMode&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
+ // We don't do this here during boot because we can do it all
+ // at once after scanning all existing packages.
+ adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,
+ true, forceDex, (scanMode & SCAN_DEFER_DEX) != 0);
+ }
// We don't expect installation to fail beyond this point,
if ((scanMode&SCAN_MONITOR) != 0) {
mAppDirs.put(pkg.mPath, pkg);
@@ -5388,6 +5679,55 @@
return pkg;
}
+ public void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
+ boolean doDexOpt, boolean forceDexOpt, boolean deferDexOpt) {
+ String requiredInstructionSet = null;
+ PackageSetting requirer = null;
+ for (PackageSetting ps : packagesForUser) {
+ if (ps.requiredCpuAbiString != null) {
+ final String instructionSet = VMRuntime.getInstructionSet(ps.requiredCpuAbiString);
+ if (requiredInstructionSet != null) {
+ if (!instructionSet.equals(requiredInstructionSet)) {
+ // We have a mismatch between instruction sets (say arm vs arm64).
+ //
+ // TODO: We should rescan all the packages in a shared UID to check if
+ // they do contain shared libs for other ABIs in addition to the ones we've
+ // already extracted. For example, the package might contain both arm64-v8a
+ // and armeabi-v7a shared libs, and we'd have chosen arm64-v8a on 64 bit
+ // devices.
+ String errorMessage = "Instruction set mismatch, " + requirer.pkg.packageName
+ + " requires " + requiredInstructionSet + " whereas " + ps.pkg.packageName
+ + " requires " + instructionSet;
+ Slog.e(TAG, errorMessage);
+
+ reportSettingsProblem(Log.WARN, errorMessage);
+ // Give up, don't bother making any other changes to the package settings.
+ return;
+ }
+ } else {
+ requiredInstructionSet = instructionSet;
+ requirer = ps;
+ }
+ }
+ }
+
+ if (requiredInstructionSet != null) {
+ for (PackageSetting ps : packagesForUser) {
+ if (ps.requiredCpuAbiString == null) {
+ ps.requiredCpuAbiString = requirer.requiredCpuAbiString;
+ if (ps.pkg != null) {
+ ps.pkg.applicationInfo.requiredCpuAbi = requirer.requiredCpuAbiString;
+ Slog.i(TAG, "Adjusting ABI for : " + ps.name + " to " + ps.requiredCpuAbiString);
+ if (doDexOpt) {
+ performDexOptLI(ps.pkg, forceDexOpt, deferDexOpt, true);
+ mInstaller.rmdex(ps.codePathString, getPreferredInstructionSet());
+ }
+ }
+ }
+ }
+ }
+ }
+
private void setUpCustomResolverActivity(PackageParser.Package pkg) {
synchronized (mPackages) {
mResolverReplaced = true;
@@ -5418,7 +5758,7 @@
if (FileUtils.contains(Environment.getRootDirectory(), codePath)) {
codeRoot = Environment.getRootDirectory();
} else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) {
- codeRoot = Environment.getRootDirectory();
+ codeRoot = Environment.getOemDirectory();
} else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) {
codeRoot = Environment.getVendorDirectory();
} else {
@@ -5467,6 +5807,31 @@
pkgSetting.nativeLibraryPathString = nativeLibraryPath;
}
+ // Deduces the required ABI of an upgraded system app.
+ private void setInternalAppAbi(PackageParser.Package pkg, PackageSetting pkgSetting) {
+ final String apkRoot = calculateApkRoot(pkg.applicationInfo.sourceDir);
+ final String apkName = getApkName(pkg.applicationInfo.sourceDir);
+
+ // This is of the form "/system/lib64/<packagename>", "/vendor/lib64/<packagename>"
+ // or similar.
+ final File lib64 = new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath());
+ final File lib = new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath());
+
+ // Assume that the bundled native libraries always correspond to the
+ // most preferred 32 or 64 bit ABI.
+ if (lib64.exists()) {
+ pkg.applicationInfo.requiredCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
+ pkgSetting.requiredCpuAbiString = Build.SUPPORTED_64_BIT_ABIS[0];
+ } else if (lib.exists()) {
+ pkg.applicationInfo.requiredCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
+ pkgSetting.requiredCpuAbiString = Build.SUPPORTED_32_BIT_ABIS[0];
+ } else {
+ // This is the case where the app has no native code.
+ pkg.applicationInfo.requiredCpuAbi = null;
+ pkgSetting.requiredCpuAbiString = null;
+ }
+ }
+
private static int copyNativeLibrariesForInternalApp(File scanFile, final File nativeLibraryDir)
throws IOException {
if (!nativeLibraryDir.isDirectory()) {
@@ -5477,8 +5842,7 @@
}
try {
- Libcore.os.chmod(nativeLibraryDir.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH
- | S_IXOTH);
+ Os.chmod(nativeLibraryDir.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
} catch (ErrnoException e) {
throw new IOException("Cannot chmod native library directory "
+ nativeLibraryDir.getPath(), e);
@@ -8110,7 +8474,8 @@
int mRet;
MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags,
- String packageName, String dataDir, int uid, UserHandle user) {
+ String packageName, String dataDir, String instructionSet,
+ int uid, UserHandle user) {
super(user);
this.srcArgs = srcArgs;
this.observer = observer;
@@ -8119,7 +8484,7 @@
this.uid = uid;
if (srcArgs != null) {
Uri packageUri = Uri.fromFile(new File(srcArgs.getCodePath()));
- targetArgs = createInstallArgs(packageUri, flags, packageName, dataDir);
+ targetArgs = createInstallArgs(packageUri, flags, packageName, dataDir, instructionSet);
} else {
targetArgs = null;
}
@@ -8228,7 +8593,7 @@
}
private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath,
- String nativeLibraryPath) {
+ String nativeLibraryPath, String instructionSet) {
final boolean isInAsec;
if (installOnSd(flags)) {
/* Apps on SD card are always in ASEC containers. */
@@ -8246,21 +8611,23 @@
if (isInAsec) {
return new AsecInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath,
- installOnSd(flags), installForwardLocked(flags));
+ instructionSet, installOnSd(flags), installForwardLocked(flags));
} else {
- return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath);
+ return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath,
+ instructionSet);
}
}
// Used by package mover
- private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName, String dataDir) {
+ private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName, String dataDir,
+ String instructionSet) {
if (installOnSd(flags) || installForwardLocked(flags)) {
String cid = getNextCodePath(packageURI.getPath(), pkgName, "/"
+ AsecInstallArgs.RES_FILE_NAME);
- return new AsecInstallArgs(packageURI, cid, installOnSd(flags),
+ return new AsecInstallArgs(packageURI, cid, instructionSet, installOnSd(flags),
installForwardLocked(flags));
} else {
- return new FileInstallArgs(packageURI, pkgName, dataDir);
+ return new FileInstallArgs(packageURI, pkgName, dataDir, instructionSet);
}
}
@@ -8273,11 +8640,12 @@
final String installerPackageName;
final ManifestDigest manifestDigest;
final UserHandle user;
+ final String instructionSet;
InstallArgs(Uri packageURI,
IPackageInstallObserver observer, IPackageInstallObserver2 observer2,
int flags, String installerPackageName, ManifestDigest manifestDigest,
- UserHandle user) {
+ UserHandle user, String instructionSet) {
this.packageURI = packageURI;
this.flags = flags;
this.observer = observer;
@@ -8285,6 +8653,7 @@
this.installerPackageName = installerPackageName;
this.manifestDigest = manifestDigest;
this.user = user;
+ this.instructionSet = instructionSet;
}
abstract void createCopyFile();
@@ -8340,11 +8709,12 @@
FileInstallArgs(InstallParams params) {
super(params.getPackageUri(), params.observer, params.observer2, params.flags,
params.installerPackageName, params.getManifestDigest(),
- params.getUser());
+ params.getUser(), null /* instruction set */);
}
- FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
- super(null, null, null, 0, null, null, null);
+ FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
+ String instructionSet) {
+ super(null, null, null, 0, null, null, null, instructionSet);
File codeFile = new File(fullCodePath);
installDir = codeFile.getParentFile();
codeFileName = fullCodePath;
@@ -8352,8 +8722,8 @@
libraryPath = nativeLibraryPath;
}
- FileInstallArgs(Uri packageURI, String pkgName, String dataDir) {
- super(packageURI, null, null, 0, null, null, null);
+ FileInstallArgs(Uri packageURI, String pkgName, String dataDir, String instructionSet) {
+ super(packageURI, null, null, 0, null, null, null, instructionSet);
installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
String apkName = getNextCodePath(null, pkgName, ".apk");
codeFileName = new File(installDir, apkName + ".apk").getPath();
@@ -8612,7 +8982,10 @@
void cleanUpResourcesLI() {
String sourceDir = getCodePath();
if (cleanUp()) {
- int retCode = mInstaller.rmdex(sourceDir);
+ if (instructionSet == null) {
+ throw new IllegalStateException("instructionSet == null");
+ }
+ int retCode = mInstaller.rmdex(sourceDir, instructionSet);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove dex file for package: "
+ " at location "
@@ -8676,14 +9049,14 @@
AsecInstallArgs(InstallParams params) {
super(params.getPackageUri(), params.observer, params.observer2, params.flags,
params.installerPackageName, params.getManifestDigest(),
- params.getUser());
+ params.getUser(), null /* instruction set */);
}
AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
- boolean isExternal, boolean isForwardLocked) {
+ String instructionSet, boolean isExternal, boolean isForwardLocked) {
super(null, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
| (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
- null, null, null);
+ null, null, null, instructionSet);
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
@@ -8692,18 +9065,19 @@
setCachePath(subStr1);
}
- AsecInstallArgs(String cid, boolean isForwardLocked) {
+ AsecInstallArgs(String cid, String instructionSet, boolean isForwardLocked) {
super(null, null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0)
| (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
- null, null, null);
+ null, null, null, instructionSet);
this.cid = cid;
setCachePath(PackageHelper.getSdDir(cid));
}
- AsecInstallArgs(Uri packageURI, String cid, boolean isExternal, boolean isForwardLocked) {
+ AsecInstallArgs(Uri packageURI, String cid, String instructionSet,
+ boolean isExternal, boolean isForwardLocked) {
super(packageURI, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
| (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
- null, null, null);
+ null, null, null, instructionSet);
this.cid = cid;
}
@@ -8884,7 +9258,10 @@
void cleanUpResourcesLI() {
String sourceFile = getCodePath();
// Remove dex file
- int retCode = mInstaller.rmdex(sourceFile);
+ if (instructionSet == null) {
+ throw new IllegalStateException("instructionSet == null");
+ }
+ int retCode = mInstaller.rmdex(sourceFile, instructionSet);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove dex file for package: "
+ " at location "
@@ -9269,7 +9646,8 @@
res.removedInfo.args = createInstallArgs(0,
deletedPackage.applicationInfo.sourceDir,
deletedPackage.applicationInfo.publicSourceDir,
- deletedPackage.applicationInfo.nativeLibraryDir);
+ deletedPackage.applicationInfo.nativeLibraryDir,
+ getAppInstructionSet(deletedPackage.applicationInfo));
} else {
res.removedInfo.args = null;
}
@@ -9329,7 +9707,8 @@
private int moveDexFilesLI(PackageParser.Package newPackage) {
int retCode;
if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
- retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath);
+ retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath,
+ getAppInstructionSet(newPackage.applicationInfo));
if (retCode != 0) {
if (mNoDexOpt) {
/*
@@ -10015,7 +10394,8 @@
// Delete application code and resources
if (deleteCodeAndResources && (outInfo != null)) {
outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString,
- ps.resourcePathString, ps.nativeLibraryPathString);
+ ps.resourcePathString, ps.nativeLibraryPathString,
+ getAppInstructionSetFromSettings(ps));
}
return true;
}
@@ -10385,9 +10765,10 @@
boolean dataOnly = false;
String libDirPath = null;
String asecPath = null;
+ PackageSetting ps = null;
synchronized (mPackages) {
p = mPackages.get(packageName);
- PackageSetting ps = mSettings.mPackages.get(packageName);
+ ps = mSettings.mPackages.get(packageName);
if(p == null) {
dataOnly = true;
if((ps == null) || (ps.pkg == null)) {
@@ -10418,7 +10799,8 @@
}
}
int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, libDirPath,
- publicSrcDir, asecPath, pStats);
+ publicSrcDir, asecPath, getAppInstructionSetFromSettings(ps),
+ pStats);
if (res < 0) {
return false;
}
@@ -10730,6 +11112,47 @@
}
}
+ /*
+ * For filters that are added with this method:
+ * if an intent for the user whose id is userIdOrig matches the filter, then this intent can
+ * also be resolved in the user whose id is userIdDest.
+ */
+ @Override
+ public void addForwardingIntentFilter(IntentFilter filter, int userIdOrig, int userIdDest) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException(
+ "addForwardingIntentFilter can only be run by the system");
+ }
+ if (filter.countActions() == 0) {
+ Slog.w(TAG, "Cannot set a forwarding intent filter with no filter actions");
+ return;
+ }
+ synchronized (mPackages) {
+ mSettings.editForwardingIntentResolverLPw(userIdOrig).addFilter(
+ new ForwardingIntentFilter(filter, userIdDest));
+ mSettings.writePackageRestrictionsLPr(userIdOrig);
+ }
+ }
+
+ @Override
+ public void clearForwardingIntentFilters(int userIdOrig) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException(
+ "clearForwardingIntentFilter can only be run by the system");
+ }
+ synchronized (mPackages) {
+ ForwardingIntentResolver fir = mSettings.editForwardingIntentResolverLPw(userIdOrig);
+ HashSet<ForwardingIntentFilter> set =
+ new HashSet<ForwardingIntentFilter>(fir.filterSet());
+ for (ForwardingIntentFilter fif : set) {
+ fir.removeFilter(fif);
+ }
+ mSettings.writePackageRestrictionsLPr(userIdOrig);
+ }
+ }
+
@Override
public ComponentName getHomeActivities(List<ResolveInfo> allHomeCandidates) {
Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -11056,6 +11479,8 @@
public static final int DUMP_KEYSETS = 1 << 11;
+ public static final int DUMP_VERSION = 1 << 12;
+
public static final int OPTION_SHOW_FILTERS = 1 << 0;
private int mTypes;
@@ -11154,6 +11579,7 @@
pw.println(" s[hared-users]: dump shared user IDs");
pw.println(" m[essages]: print collected runtime messages");
pw.println(" v[erifiers]: print package verifier info");
+ pw.println(" version: print database version info");
pw.println(" <package.name>: info about given package");
pw.println(" k[eysets]: print known keysets");
return;
@@ -11202,6 +11628,8 @@
dumpState.setDump(DumpState.DUMP_MESSAGES);
} else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_VERIFIERS);
+ } else if ("version".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_VERSION);
} else if ("k".equals(cmd) || "keysets".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_KEYSETS);
}
@@ -11213,6 +11641,24 @@
// reader
synchronized (mPackages) {
+ if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
+ if (!checkin) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Database versions:");
+ pw.print(" SDK Version:");
+ pw.print(" internal=");
+ pw.print(mSettings.mInternalSdkPlatform);
+ pw.print(" external=");
+ pw.println(mSettings.mExternalSdkPlatform);
+ pw.print(" DB Version:");
+ pw.print(" internal=");
+ pw.print(mSettings.mInternalDatabaseVersion);
+ pw.print(" external=");
+ pw.println(mSettings.mExternalDatabaseVersion);
+ }
+ }
+
if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
if (!checkin) {
if (dumpState.onTitlePrinted())
@@ -11584,7 +12030,9 @@
continue;
}
- final AsecInstallArgs args = new AsecInstallArgs(cid, isForwardLocked(ps));
+ final AsecInstallArgs args = new AsecInstallArgs(cid,
+ getAppInstructionSetFromSettings(ps),
+ isForwardLocked(ps));
// The package status is changed only if the code path
// matches between settings and the container id.
if (ps.codePathString != null && ps.codePathString.equals(args.getCodePath())) {
@@ -11743,6 +12191,9 @@
| (regrantPermissions
? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
: 0));
+
+ mSettings.updateExternalDatabaseVersion();
+
// can downgrade to reader
// Persist settings
mSettings.writeLPr();
@@ -11899,15 +12350,17 @@
* anyway.
*/
if (returnCode != PackageManager.MOVE_SUCCEEDED) {
- processPendingMove(new MoveParams(null, observer, 0, packageName,
+ processPendingMove(new MoveParams(null, observer, 0, packageName, null,
null, -1, user),
returnCode);
} else {
Message msg = mHandler.obtainMessage(INIT_COPY);
+ final String instructionSet = getAppInstructionSet(pkg.applicationInfo);
InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
- pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir);
+ pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir,
+ instructionSet);
MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
- pkg.applicationInfo.dataDir, pkg.applicationInfo.uid, user);
+ pkg.applicationInfo.dataDir, instructionSet, pkg.applicationInfo.uid, user);
msg.obj = mp;
mHandler.sendMessage(msg);
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 80f716c..599d2a7 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -36,6 +36,7 @@
import com.android.internal.util.XmlUtils;
import com.android.server.pm.PackageManagerService.DumpState;
+import java.util.Collection;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -89,6 +90,34 @@
final class Settings {
private static final String TAG = "PackageSettings";
+ /**
+ * Current version of the package database. Set it to the latest version in
+ * the {@link DatabaseVersion} class below to ensure the database upgrade
+ * doesn't happen repeatedly.
+ * <p>
+ * Note that care should be taken to make sure all database upgrades are
+ * idempotent.
+ */
+ private static final int CURRENT_DATABASE_VERSION = DatabaseVersion.SIGNATURE_END_ENTITY;
+
+ /**
+ * This class contains constants that can be referred to from upgrade code.
+ * Insert constant values here that describe the upgrade reason. The version
+ * code must be monotonically increasing.
+ */
+ public static class DatabaseVersion {
+ /**
+ * The initial version of the database.
+ */
+ public static final int FIRST_VERSION = 1;
+
+ /**
+ * Migrating the Signature array from the entire certificate chain to
+ * just the signing certificate.
+ */
+ public static final int SIGNATURE_END_ENTITY = 2;
+ }
+
private static final boolean DEBUG_STOPPED = false;
private static final boolean DEBUG_MU = false;
@@ -102,6 +131,8 @@
private static final String TAG_PACKAGE = "pkg";
private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES =
"persistent-preferred-activities";
+ static final String TAG_FORWARDING_INTENT_FILTERS =
+ "forwarding-intent-filters";
private static final String ATTR_NAME = "name";
private static final String ATTR_USER = "user";
@@ -133,6 +164,14 @@
int mInternalSdkPlatform;
int mExternalSdkPlatform;
+ /**
+ * The current database version for apps on internal storage. This is
+ * used to upgrade the format of the packages.xml database not necessarily
+ * tied to an SDK version.
+ */
+ int mInternalDatabaseVersion;
+ int mExternalDatabaseVersion;
+
Boolean mReadExternalStorageEnforced;
/** Device identity for the purpose of package verification. */
@@ -148,6 +187,10 @@
final SparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities =
new SparseArray<PersistentPreferredIntentResolver>();
+ // For every user, it is used to find to which other users the intent can be forwarded.
+ final SparseArray<ForwardingIntentResolver> mForwardingIntentResolvers =
+ new SparseArray<ForwardingIntentResolver>();
+
final HashMap<String, SharedUserSetting> mSharedUsers =
new HashMap<String, SharedUserSetting>();
private final ArrayList<Object> mUserIds = new ArrayList<Object>();
@@ -266,6 +309,11 @@
return s;
}
+ Collection<SharedUserSetting> getAllSharedUsersLPw() {
+ return mSharedUsers.values();
+ }
+
+
boolean disableSystemPackageLPw(String name) {
final PackageSetting p = mPackages.get(name);
if(p == null) {
@@ -795,6 +843,15 @@
return ppir;
}
+ ForwardingIntentResolver editForwardingIntentResolverLPw(int userId) {
+ ForwardingIntentResolver fir = mForwardingIntentResolvers.get(userId);
+ if (fir == null) {
+ fir = new ForwardingIntentResolver();
+ mForwardingIntentResolvers.put(userId, fir);
+ }
+ return fir;
+ }
+
private File getUserPackagesStateFile(int userId) {
return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml");
}
@@ -825,6 +882,40 @@
}
}
+ /**
+ * Returns whether the current database has is older than {@code version}
+ * for apps on internal storage.
+ */
+ public boolean isInternalDatabaseVersionOlderThan(int version) {
+ return mInternalDatabaseVersion < version;
+ }
+
+ /**
+ * Returns whether the current database has is older than {@code version}
+ * for apps on external storage.
+ */
+ public boolean isExternalDatabaseVersionOlderThan(int version) {
+ return mExternalDatabaseVersion < version;
+ }
+
+ /**
+ * Updates the database version for apps on internal storage. Called after
+ * call the updates to the database format are done for apps on internal
+ * storage after the initial start-up scan.
+ */
+ public void updateInternalDatabaseVersion() {
+ mInternalDatabaseVersion = CURRENT_DATABASE_VERSION;
+ }
+
+ /**
+ * Updates the database version for apps on internal storage. Called after
+ * call the updates to the database format are done for apps on internal
+ * storage after the initial start-up scan.
+ */
+ public void updateExternalDatabaseVersion() {
+ mExternalDatabaseVersion = CURRENT_DATABASE_VERSION;
+ }
+
private void readPreferredActivitiesLPw(XmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
@@ -876,6 +967,28 @@
}
}
+ private void readForwardingIntentFiltersLPw(XmlPullParser parser, int userId)
+ throws XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_ITEM)) {
+ ForwardingIntentFilter fif = new ForwardingIntentFilter(parser);
+ editForwardingIntentResolverLPw(userId).addFilter(fif);
+ } else {
+ String msg = "Unknown element under " + TAG_FORWARDING_INTENT_FILTERS + ": " +
+ parser.getName();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
void readPackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Reading package restrictions for user=" + userId);
@@ -1004,6 +1117,8 @@
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
readPersistentPreferredActivitiesLPw(parser, userId);
+ } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)) {
+ readForwardingIntentFiltersLPw(parser, userId);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ parser.getName());
@@ -1081,6 +1196,20 @@
serializer.endTag(null, TAG_PERSISTENT_PREFERRED_ACTIVITIES);
}
+ void writeForwardingIntentFiltersLPr(XmlSerializer serializer, int userId)
+ throws IllegalArgumentException, IllegalStateException, IOException {
+ serializer.startTag(null, TAG_FORWARDING_INTENT_FILTERS);
+ ForwardingIntentResolver fir = mForwardingIntentResolvers.get(userId);
+ if (fir != null) {
+ for (final ForwardingIntentFilter fif : fir.filterSet()) {
+ serializer.startTag(null, TAG_ITEM);
+ fif.writeToXml(serializer);
+ serializer.endTag(null, TAG_ITEM);
+ }
+ }
+ serializer.endTag(null, TAG_FORWARDING_INTENT_FILTERS);
+ }
+
void writePackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Writing package restrictions for user=" + userId);
@@ -1179,6 +1308,8 @@
writePersistentPreferredActivitiesLPr(serializer, userId);
+ writeForwardingIntentFiltersLPr(serializer, userId);
+
serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
serializer.endDocument();
@@ -1355,6 +1486,11 @@
serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform));
serializer.endTag(null, "last-platform-version");
+ serializer.startTag(null, "database-version");
+ serializer.attribute(null, "internal", Integer.toString(mInternalDatabaseVersion));
+ serializer.attribute(null, "external", Integer.toString(mExternalDatabaseVersion));
+ serializer.endTag(null, "database-version");
+
if (mVerifierDeviceIdentity != null) {
serializer.startTag(null, "verifier");
serializer.attribute(null, "device", mVerifierDeviceIdentity.toString());
@@ -1791,6 +1927,10 @@
// TODO: check whether this is okay! as it is very
// similar to how preferred-activities are treated
readPersistentPreferredActivitiesLPw(parser, 0);
+ } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)) {
+ // TODO: check whether this is okay! as it is very
+ // similar to how preferred-activities are treated
+ readForwardingIntentFiltersLPw(parser, 0);
} else if (tagName.equals("updated-package")) {
readDisabledSysPackageLPw(parser);
} else if (tagName.equals("cleaning-package")) {
@@ -1830,6 +1970,19 @@
}
} catch (NumberFormatException e) {
}
+ } else if (tagName.equals("database-version")) {
+ mInternalDatabaseVersion = mExternalDatabaseVersion = 0;
+ try {
+ String internalDbVersionString = parser.getAttributeValue(null, "internal");
+ if (internalDbVersionString != null) {
+ mInternalDatabaseVersion = Integer.parseInt(internalDbVersionString);
+ }
+ String externalDbVersionString = parser.getAttributeValue(null, "external");
+ if (externalDbVersionString != null) {
+ mExternalDatabaseVersion = Integer.parseInt(externalDbVersionString);
+ }
+ } catch (NumberFormatException ignored) {
+ }
} else if (tagName.equals("verifier")) {
final String deviceIdentity = parser.getAttributeValue(null, "device");
try {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 210e151b..14df347 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -22,9 +22,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.ActivityThread;
-import android.app.admin.DevicePolicyManager;
import android.app.IStopUserCallback;
-import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -263,46 +261,33 @@
if (userId != UserHandle.getCallingUserId()) {
checkManageUsersPermission("getting profiles related to user " + userId);
}
- synchronized (mPackagesLock) {
- // Getting the service here is not good for testing purposes. However, this service
- // is not available when UserManagerService starts up so we need a lazy load.
-
- DevicePolicyManager dpm = null;
- if (enabledOnly) {
- dpm = (DevicePolicyManager)
- mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mPackagesLock) {
+ return getProfilesLocked(userId, enabledOnly);
}
-
- UserInfo user = getUserInfoLocked(userId);
- ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
- for (int i = 0; i < mUsers.size(); i++) {
- UserInfo profile = mUsers.valueAt(i);
- if (!isProfileOf(user, profile)) {
- continue;
- }
-
- if (enabledOnly && profile.isManagedProfile()) {
- if (dpm != null) {
- if(!dpm.isProfileEnabled(profile.id)) {
- continue;
- }
- } else {
- Log.w(LOG_TAG,
- "Attempting to reach DevicePolicyManager before it was started");
- // TODO: There might be system apps that need to call this. Make sure that
- // DevicePolicyManagerService is ready at that time (otherwise, any default
- // value is a bad one).
- throw new IllegalArgumentException(String.format(
- "Attempting to get enabled profiles for %d before "
- + "DevicePolicyManagerService has been started.", userId));
- }
- }
- users.add(profile);
- }
- return users;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
+ /** Assume permissions already checked and caller's identity cleared */
+ private List<UserInfo> getProfilesLocked(int userId, boolean enabledOnly) {
+ UserInfo user = getUserInfoLocked(userId);
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo profile = mUsers.valueAt(i);
+ if (!isProfileOf(user, profile)) {
+ continue;
+ }
+ if (enabledOnly && !profile.isEnabled()) {
+ continue;
+ }
+ users.add(profile);
+ }
+ return users;
+ }
+
private boolean isProfileOf(UserInfo user, UserInfo profile) {
return user.id == profile.id ||
(user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
@@ -310,6 +295,18 @@
}
@Override
+ public void setUserEnabled(int userId) {
+ checkManageUsersPermission("enable user");
+ synchronized (mPackagesLock) {
+ UserInfo info = getUserInfoLocked(userId);
+ if (info != null && !info.isEnabled()) {
+ info.flags ^= UserInfo.FLAG_DISABLED;
+ writeUserLocked(info);
+ }
+ }
+ }
+
+ @Override
public UserInfo getUserInfo(int userId) {
checkManageUsersPermission("query user");
synchronized (mPackagesLock) {
@@ -459,13 +456,13 @@
synchronized (mPackagesLock) {
Bundle restrictions = mUserRestrictions.get(userId);
- return restrictions != null ? restrictions : Bundle.EMPTY;
+ return restrictions != null ? new Bundle(restrictions) : new Bundle();
}
}
@Override
public void setUserRestrictions(Bundle restrictions, int userId) {
- checkProfileOwnerOrManageUsersPermission("setUserRestrictions");
+ checkManageUsersPermission("setUserRestrictions");
if (restrictions == null) return;
synchronized (mPackagesLock) {
@@ -491,51 +488,14 @@
* @param message used as message if SecurityException is thrown
* @throws SecurityException if the caller is not system or root
*/
- private final void checkManageUsersPermission(String message) {
+ private static final void checkManageUsersPermission(String message) {
final int uid = Binder.getCallingUid();
-
- if (missingManageUsersPermission(uid)) {
- throw new SecurityException("You need MANAGE_USERS permission to: " + message);
- }
- }
-
- /**
- * Enforces that only the system UID, root's UID, apps that have the
- * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}
- * permission, the profile owner, or the device owner can make certain calls to the
- * UserManager.
- *
- * @param message used as message if SecurityException is thrown
- * @throws SecurityException if the caller is not system, root, or device
- * owner
- */
- private final void checkProfileOwnerOrManageUsersPermission(String message) {
- final int uid = Binder.getCallingUid();
- boolean isProfileOwner = false;
- if (mContext != null && mContext.getPackageManager() != null) {
- String[] pkgs = mContext.getPackageManager().getPackagesForUid(uid);
- DevicePolicyManager dpm =
- (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- if (dpm != null) {
- for (String pkg : pkgs) {
- if (dpm.isDeviceOwnerApp(pkg) || dpm.isProfileOwnerApp(pkg)) {
- isProfileOwner = true;
- }
- }
- }
- }
-
- if (missingManageUsersPermission(uid) && !isProfileOwner) {
- throw new SecurityException(
- "You need MANAGE_USERS permission or device owner privileges to: " + message);
- }
- }
-
- private boolean missingManageUsersPermission(int uid) {
- return uid != Process.SYSTEM_UID && uid != 0
+ if (uid != Process.SYSTEM_UID && uid != 0
&& ActivityManager.checkComponentPermission(
android.Manifest.permission.MANAGE_USERS,
- uid, -1, true) != PackageManager.PERMISSION_GRANTED;
+ uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need MANAGE_USERS permission to: " + message);
+ }
}
private void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
@@ -1240,8 +1200,7 @@
public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
if (UserHandle.getCallingUserId() != userId
|| !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
- checkProfileOwnerOrManageUsersPermission(
- "Only system or device owner can get restrictions for other users/apps");
+ checkManageUsersPermission("Only system can get restrictions for other users/apps");
}
synchronized (mPackagesLock) {
// Read the restrictions from XML
@@ -1254,8 +1213,7 @@
int userId) {
if (UserHandle.getCallingUserId() != userId
|| !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
- checkProfileOwnerOrManageUsersPermission(
- "Only system or device owner can set restrictions for other users/apps");
+ checkManageUsersPermission("Only system can set restrictions for other users/apps");
}
synchronized (mPackagesLock) {
// Write the restrictions to XML
@@ -1357,8 +1315,7 @@
@Override
public void removeRestrictions() {
- checkProfileOwnerOrManageUsersPermission(
- "Only system or device owner can remove restrictions");
+ checkManageUsersPermission("Only system can remove restrictions");
final int userHandle = UserHandle.getCallingUserId();
removeRestrictionsForUser(userHandle, true);
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index a2a49c9..9061f96 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -187,7 +187,7 @@
boolean trustMayHaveChanged = false;
for (int i = 0; i < mObsoleteAgents.size(); i++) {
- AgentInfo info = mActiveAgents.valueAt(i);
+ AgentInfo info = mObsoleteAgents.valueAt(i);
if (info.agent.isTrusted()) {
trustMayHaveChanged = true;
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 6700895..50dd27d 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -19,6 +19,9 @@
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -26,13 +29,18 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.database.Cursor;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.TvContract;
import android.tv.ITvInputClient;
import android.tv.ITvInputManager;
import android.tv.ITvInputService;
@@ -41,13 +49,14 @@
import android.tv.ITvInputSessionCallback;
import android.tv.TvInputInfo;
import android.tv.TvInputService;
-import android.util.ArrayMap;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.InputChannel;
import android.view.Surface;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.SomeArgs;
+import com.android.server.IoThread;
import com.android.server.SystemService;
import java.util.ArrayList;
@@ -63,6 +72,8 @@
private final Context mContext;
+ private final ContentResolver mContentResolver;
+
// A global lock.
private final Object mLock = new Object();
@@ -72,10 +83,17 @@
// A map from user id to UserState.
private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
+ private final Handler mLogHandler;
+
public TvInputManagerService(Context context) {
super(context);
+
mContext = context;
+ mContentResolver = context.getContentResolver();
+ mLogHandler = new LogHandler(IoThread.get().getLooper());
+
registerBroadcastReceivers();
+
synchronized (mLock) {
mUserStates.put(mCurrentUserId, new UserState());
buildTvInputListLocked(mCurrentUserId);
@@ -274,6 +292,9 @@
Slog.d(TAG, "createSessionInternalLocked(name=" + sessionState.name.getClassName()
+ ")");
}
+
+ final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
+
// Set up a callback to send the session token.
ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
@Override
@@ -286,30 +307,32 @@
if (session == null) {
removeSessionStateLocked(sessionToken, userId);
sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null,
- sessionState.seq, userId);
+ null, sessionState.seq, userId);
} else {
sendSessionTokenToClientLocked(sessionState.client, sessionState.name,
- sessionToken, sessionState.seq, userId);
+ sessionToken, channels[0], sessionState.seq, userId);
}
+ channels[0].dispose();
}
}
};
// Create a session. When failed, send a null token immediately.
try {
- service.createSession(callback);
+ service.createSession(channels[1], callback);
} catch (RemoteException e) {
Slog.e(TAG, "error in createSession", e);
removeSessionStateLocked(sessionToken, userId);
- sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null,
+ sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null, null,
sessionState.seq, userId);
}
+ channels[1].dispose();
}
private void sendSessionTokenToClientLocked(ITvInputClient client, ComponentName name,
- IBinder sessionToken, int seq, int userId) {
+ IBinder sessionToken, InputChannel channel, int seq, int userId) {
try {
- client.onSessionCreated(name, sessionToken, seq);
+ client.onSessionCreated(name, sessionToken, channel, seq);
} catch (RemoteException exception) {
Slog.e(TAG, "error in onSessionCreated", exception);
}
@@ -325,6 +348,14 @@
UserState userState = getUserStateLocked(userId);
SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
+ // Close the open log entry, if any.
+ if (sessionState.logUri != null) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = sessionState.logUri;
+ args.arg2 = System.currentTimeMillis();
+ mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
+ }
+
// Also remove the session token from the session token list of the current service.
ServiceState serviceState = userState.serviceStateMap.get(sessionState.name);
if (serviceState != null) {
@@ -384,7 +415,7 @@
UserState userState = getUserStateLocked(resolvedUserId);
ServiceState serviceState = userState.serviceStateMap.get(name);
if (serviceState == null) {
- serviceState = new ServiceState(name, resolvedUserId);
+ serviceState = new ServiceState(resolvedUserId);
userState.serviceStateMap.put(name, serviceState);
}
IBinder iBinder = client.asBinder();
@@ -468,7 +499,7 @@
// Also, add them to the session state map of the current service.
ServiceState serviceState = userState.serviceStateMap.get(name);
if (serviceState == null) {
- serviceState = new ServiceState(name, resolvedUserId);
+ serviceState = new ServiceState(resolvedUserId);
userState.serviceStateMap.put(name, serviceState);
}
serviceState.sessionTokens.add(sessionToken);
@@ -523,6 +554,10 @@
}
}
} finally {
+ if (surface != null) {
+ // surface is not used in TvInputManagerService.
+ surface.release();
+ }
Binder.restoreCallingIdentity(identity);
}
}
@@ -557,6 +592,35 @@
synchronized (mLock) {
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
+
+ long currentTime = System.currentTimeMillis();
+ long channelId = ContentUris.parseId(channelUri);
+
+ // Close the open log entry first, if any.
+ UserState userState = getUserStateLocked(resolvedUserId);
+ SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+ if (sessionState.logUri != null) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = sessionState.logUri;
+ args.arg2 = currentTime;
+ mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args)
+ .sendToTarget();
+ }
+
+ // Create a log entry and fill it later.
+ ContentValues values = new ContentValues();
+ values.put(TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
+ currentTime);
+ values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, 0);
+ values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId);
+
+ sessionState.logUri = mContentResolver.insert(
+ TvContract.WatchedPrograms.CONTENT_URI, values);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = sessionState.logUri;
+ args.arg2 = ContentUris.parseId(channelUri);
+ args.arg3 = currentTime;
+ mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget();
} catch (RemoteException e) {
Slog.e(TAG, "error in tune", e);
return;
@@ -652,7 +716,7 @@
private boolean bound;
private boolean available;
- private ServiceState(ComponentName name, int userId) {
+ private ServiceState(int userId) {
this.connection = new InputServiceConnection(userId);
}
}
@@ -664,6 +728,7 @@
private final int callingUid;
private ITvInputSession session;
+ private Uri logUri;
private SessionState(ComponentName name, ITvInputClient client, int seq, int callingUid) {
this.name = name;
@@ -738,4 +803,147 @@
}
}
}
+
+ private final class LogHandler extends Handler {
+ private static final int MSG_OPEN_ENTRY = 1;
+ private static final int MSG_UPDATE_ENTRY = 2;
+ private static final int MSG_CLOSE_ENTRY = 3;
+
+ public LogHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_OPEN_ENTRY: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Uri uri = (Uri) args.arg1;
+ long channelId = (long) args.arg2;
+ long time = (long) args.arg3;
+ onOpenEntry(uri, channelId, time);
+ args.recycle();
+ return;
+ }
+ case MSG_UPDATE_ENTRY: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Uri uri = (Uri) args.arg1;
+ long channelId = (long) args.arg2;
+ long time = (long) args.arg3;
+ onUpdateEntry(uri, channelId, time);
+ args.recycle();
+ return;
+ }
+ case MSG_CLOSE_ENTRY: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Uri uri = (Uri) args.arg1;
+ long time = (long) args.arg2;
+ onCloseEntry(uri, time);
+ args.recycle();
+ return;
+ }
+ default: {
+ Slog.w(TAG, "Unhandled message code: " + msg.what);
+ return;
+ }
+ }
+ }
+
+ private void onOpenEntry(Uri uri, long channelId, long watchStarttime) {
+ String[] projection = {
+ TvContract.Programs.TITLE,
+ TvContract.Programs.START_TIME_UTC_MILLIS,
+ TvContract.Programs.END_TIME_UTC_MILLIS,
+ TvContract.Programs.DESCRIPTION
+ };
+ String selection = TvContract.Programs.CHANNEL_ID + "=? AND "
+ + TvContract.Programs.START_TIME_UTC_MILLIS + "<=? AND "
+ + TvContract.Programs.END_TIME_UTC_MILLIS + ">?";
+ String[] selectionArgs = {
+ String.valueOf(channelId),
+ String.valueOf(watchStarttime),
+ String.valueOf(watchStarttime)
+ };
+ String sortOrder = TvContract.Programs.START_TIME_UTC_MILLIS + " ASC";
+ Cursor cursor = null;
+ try {
+ cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection,
+ selection, selectionArgs, sortOrder);
+ if (cursor != null && cursor.moveToNext()) {
+ ContentValues values = new ContentValues();
+ values.put(TvContract.WatchedPrograms.TITLE, cursor.getString(0));
+ values.put(TvContract.WatchedPrograms.START_TIME_UTC_MILLIS, cursor.getLong(1));
+ long endTime = cursor.getLong(2);
+ values.put(TvContract.WatchedPrograms.END_TIME_UTC_MILLIS, endTime);
+ values.put(TvContract.WatchedPrograms.DESCRIPTION, cursor.getString(3));
+ mContentResolver.update(uri, values, null, null);
+
+ // Schedule an update when the current program ends.
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = uri;
+ args.arg2 = channelId;
+ args.arg3 = endTime;
+ Message msg = obtainMessage(LogHandler.MSG_UPDATE_ENTRY, args);
+ sendMessageDelayed(msg, endTime - System.currentTimeMillis());
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ private void onUpdateEntry(Uri uri, long channelId, long time) {
+ String[] projection = {
+ TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
+ TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS,
+ TvContract.WatchedPrograms.TITLE,
+ TvContract.WatchedPrograms.START_TIME_UTC_MILLIS,
+ TvContract.WatchedPrograms.END_TIME_UTC_MILLIS,
+ TvContract.WatchedPrograms.DESCRIPTION
+ };
+ Cursor cursor = null;
+ try {
+ cursor = mContentResolver.query(uri, projection, null, null, null);
+ if (cursor != null && cursor.moveToNext()) {
+ long watchStartTime = cursor.getLong(0);
+ long watchEndTime = cursor.getLong(1);
+ String title = cursor.getString(2);
+ long startTime = cursor.getLong(3);
+ long endTime = cursor.getLong(4);
+ String description = cursor.getString(5);
+
+ // Do nothing if the current log entry is already closed.
+ if (watchEndTime > 0) {
+ return;
+ }
+
+ // The current program has just ended. Create a (complete) log entry off the
+ // current entry.
+ ContentValues values = new ContentValues();
+ values.put(TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
+ watchStartTime);
+ values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, time);
+ values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId);
+ values.put(TvContract.WatchedPrograms.TITLE, title);
+ values.put(TvContract.WatchedPrograms.START_TIME_UTC_MILLIS, startTime);
+ values.put(TvContract.WatchedPrograms.END_TIME_UTC_MILLIS, endTime);
+ values.put(TvContract.WatchedPrograms.DESCRIPTION, description);
+ mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ // Re-open the current log entry with the next program information.
+ onOpenEntry(uri, channelId, time);
+ }
+
+ private void onCloseEntry(Uri uri, long watchEndTime) {
+ ContentValues values = new ContentValues();
+ values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, watchEndTime);
+ mContentResolver.update(uri, values, null, null);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
index fdc604f..3c960c7 100644
--- a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
@@ -20,6 +20,8 @@
import android.content.Intent;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.system.ErrnoException;
+import android.system.Os;
import android.util.Base64;
import android.util.Slog;
@@ -28,9 +30,7 @@
import java.io.FileInputStream;
import java.io.IOException;
-import libcore.io.ErrnoException;
import libcore.io.IoUtils;
-import libcore.io.Libcore;
public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
@@ -110,16 +110,16 @@
File update = new File(updateDir.getParentFile(), "update");
File tmp = new File(updateDir.getParentFile(), "tmp");
if (current.exists()) {
- Libcore.os.symlink(updateDir.getPath(), update.getPath());
- Libcore.os.rename(update.getPath(), current.getPath());
+ Os.symlink(updateDir.getPath(), update.getPath());
+ Os.rename(update.getPath(), current.getPath());
} else {
- Libcore.os.symlink(updateDir.getPath(), current.getPath());
+ Os.symlink(updateDir.getPath(), current.getPath());
}
contexts.mkdirs();
backupContexts(contexts);
copyUpdate(contexts);
- Libcore.os.symlink(contexts.getPath(), tmp.getPath());
- Libcore.os.rename(tmp.getPath(), current.getPath());
+ Os.symlink(contexts.getPath(), tmp.getPath());
+ Os.rename(tmp.getPath(), current.getPath());
SystemProperties.set("selinux.reload_policy", "1");
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 87953fe..3eb2d5f 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -25,6 +25,7 @@
import android.app.IWallpaperManagerCallback;
import android.app.PendingIntent;
import android.app.WallpaperInfo;
+import android.app.WallpaperManager;
import android.app.backup.BackupManager;
import android.app.backup.WallpaperBackupHelper;
import android.content.BroadcastReceiver;
@@ -844,13 +845,7 @@
try {
if (componentName == null) {
- String defaultComponent =
- mContext.getString(com.android.internal.R.string.default_wallpaper_component);
- if (defaultComponent != null) {
- // See if there is a default wallpaper component specified
- componentName = ComponentName.unflattenFromString(defaultComponent);
- if (DEBUG) Slog.v(TAG, "Use default component wallpaper:" + componentName);
- }
+ componentName = WallpaperManager.getDefaultWallpaperComponent(mContext);
if (componentName == null) {
// Fall back to static image wallpaper
componentName = IMAGE_WALLPAPER;
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
index 54c9755..6e03993 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
@@ -67,10 +67,10 @@
cec_logical_address_t getLogicalAddress(cec_device_type_t deviceType);
uint16_t getPhysicalAddress();
- int getDeviceType(cec_logical_address_t addr);
+ cec_device_type_t getDeviceType(cec_logical_address_t addr);
void queueMessage(const MessageEntry& message);
void queueOutgoingMessage(const cec_message_t& message);
- void sendReportPhysicalAddress();
+ void sendReportPhysicalAddress(cec_logical_address_t srcAddr);
void sendActiveSource(cec_logical_address_t srcAddr);
void sendFeatureAbort(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
int opcode, int reason);
@@ -93,15 +93,41 @@
EVENT_TYPE_STANDBY
};
+ /*
+ * logical address pool for each device type.
+ */
+ static const cec_logical_address_t TV_ADDR_POOL[];
+ static const cec_logical_address_t PLAYBACK_ADDR_POOL[];
+ static const cec_logical_address_t RECORDER_ADDR_POOL[];
+ static const cec_logical_address_t TUNER_ADDR_POOL[];
+
static const unsigned int MAX_BUFFER_SIZE = 256;
static const uint16_t INVALID_PHYSICAL_ADDRESS = 0xFFFF;
- static const int INACTIVE_DEVICE_TYPE = -1;
static void onReceived(const hdmi_event_t* event, void* arg);
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
void updatePhysicalAddress();
void updateLogicalAddress();
+
+ // Allocate logical address. The CEC standard recommends that we try to use the address
+ // we have ever used before, in case this is to allocate an address afte the cable is
+ // connected again. If preferredAddr is given a valid one (not CEC_ADDR_UNREGISTERED), then
+ // this method checks if the address is available first. If not, it tries other addresses
+ // int the address pool available for the given type.
+ cec_logical_address_t allocateLogicalAddress(cec_device_type_t type,
+ cec_logical_address_t preferredAddr);
+
+ // Send a CEC ping message. Returns true if successful.
+ bool sendPing(cec_logical_address_t addr);
+
+ // Return the pool of logical addresses that are used for a given device type.
+ // One of the addresses in the pool will be chosen in the allocation logic.
+ bool getLogicalAddressPool(cec_device_type_t type, const cec_logical_address_t** addrPool,
+ size_t* poolSize);
+
+ // Handles the message retrieved from internal message queue. The message can be
+ // for either rx or tx.
void dispatchMessage(const MessageEntry& message);
void processIncomingMessage(const cec_message_t& msg);
@@ -159,6 +185,29 @@
std::string mOsdName;
};
+ const cec_logical_address_t HdmiCecHandler::TV_ADDR_POOL[] = {
+ CEC_ADDR_TV,
+ CEC_ADDR_FREE_USE,
+ };
+
+ const cec_logical_address_t HdmiCecHandler::PLAYBACK_ADDR_POOL[] = {
+ CEC_ADDR_PLAYBACK_1,
+ CEC_ADDR_PLAYBACK_2,
+ CEC_ADDR_PLAYBACK_3
+ };
+
+ const cec_logical_address_t HdmiCecHandler::RECORDER_ADDR_POOL[] = {
+ CEC_ADDR_RECORDER_1,
+ CEC_ADDR_RECORDER_2,
+ CEC_ADDR_RECORDER_3
+ };
+
+ const cec_logical_address_t HdmiCecHandler::TUNER_ADDR_POOL[] = {
+ CEC_ADDR_TUNER_1,
+ CEC_ADDR_TUNER_2,
+ CEC_ADDR_TUNER_3,
+ CEC_ADDR_TUNER_4
+ };
HdmiCecHandler::HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj) :
mDevice(device),
@@ -176,39 +225,15 @@
return mPhysicalAddress;
}
-void HdmiCecHandler::updatePhysicalAddress() {
- uint16_t addr;
- if (!mDevice->get_physical_address(mDevice, &addr)) {
- mPhysicalAddress = addr;
- } else {
- mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
- }
-}
-
-void HdmiCecHandler::updateLogicalAddress() {
- std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
- for (; it != mLogicalDevices.end(); ++it) {
- cec_logical_address_t addr;
- if (!mDevice->get_logical_address(mDevice, it->first, &addr)) {
- it->second = addr;
- }
- }
-}
-
cec_logical_address_t HdmiCecHandler::initLogicalDevice(cec_device_type_t type) {
- cec_logical_address_t addr;
- int res = mDevice->allocate_logical_address(mDevice, type, &addr);
-
- if (res != 0) {
- ALOGE("Logical Address Allocation failed: %d", res);
- } else {
- ALOGV("Logical Address Allocation success: %d", addr);
+ cec_logical_address addr = allocateLogicalAddress(type, CEC_ADDR_UNREGISTERED);
+ if (addr != CEC_ADDR_UNREGISTERED && !mDevice->add_logical_address(mDevice, addr)) {
mLogicalDevices.insert(std::pair<cec_device_type_t, cec_logical_address_t>(type, addr));
// Broadcast <Report Physical Address> when a new logical address was allocated to let
// other devices discover the new logical device and its logical - physical address
// association.
- sendReportPhysicalAddress();
+ sendReportPhysicalAddress(addr);
}
return addr;
}
@@ -229,14 +254,14 @@
return CEC_ADDR_UNREGISTERED;
}
-int HdmiCecHandler::getDeviceType(cec_logical_address_t addr) {
+cec_device_type_t HdmiCecHandler::getDeviceType(cec_logical_address_t addr) {
std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
for (; it != mLogicalDevices.end(); ++it) {
if (it->second == addr) {
return it->first;
}
}
- return INACTIVE_DEVICE_TYPE;
+ return CEC_DEVICE_INACTIVE;
}
void HdmiCecHandler::queueMessage(const MessageEntry& entry) {
@@ -256,26 +281,26 @@
queueMessage(entry);
}
-void HdmiCecHandler::sendReportPhysicalAddress() {
+void HdmiCecHandler::sendReportPhysicalAddress(cec_logical_address_t addr) {
if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
ALOGE("Invalid physical address.");
return;
}
-
- // Report physical address for each logical one hosted in it.
- std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
- while (it != mLogicalDevices.end()) {
- cec_message_t msg;
- msg.initiator = it->second; // logical address
- msg.destination = CEC_ADDR_BROADCAST;
- msg.length = 4;
- msg.body[0] = CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS;
- msg.body[1] = (mPhysicalAddress >> 8) & 0xff;
- msg.body[2] = mPhysicalAddress & 0xff;
- msg.body[3] = it->first; // device type
- queueOutgoingMessage(msg);
- ++it;
+ cec_device_type_t deviceType = getDeviceType(addr);
+ if (deviceType == CEC_DEVICE_INACTIVE) {
+ ALOGE("Invalid logical address: %d", addr);
+ return;
}
+
+ cec_message_t msg;
+ msg.initiator = addr;
+ msg.destination = CEC_ADDR_BROADCAST;
+ msg.length = 4;
+ msg.body[0] = CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS;
+ msg.body[1] = (mPhysicalAddress >> 8) & 0xff;
+ msg.body[2] = mPhysicalAddress & 0xff;
+ msg.body[3] = deviceType;
+ queueOutgoingMessage(msg);
}
void HdmiCecHandler::sendActiveSource(cec_logical_address_t srcAddr) {
@@ -410,6 +435,99 @@
}
}
+void HdmiCecHandler::updatePhysicalAddress() {
+ uint16_t addr;
+ if (!mDevice->get_physical_address(mDevice, &addr)) {
+ mPhysicalAddress = addr;
+ } else {
+ mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+ }
+}
+
+void HdmiCecHandler::updateLogicalAddress() {
+ mDevice->clear_logical_address(mDevice);
+ std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
+ for (; it != mLogicalDevices.end(); ++it) {
+ cec_logical_address_t addr;
+ cec_logical_address_t preferredAddr = it->second;
+ cec_device_type_t deviceType = it->first;
+ addr = allocateLogicalAddress(deviceType, preferredAddr);
+ if (!mDevice->add_logical_address(mDevice, addr)) {
+ it->second = addr;
+ } else {
+ it->second = CEC_ADDR_UNREGISTERED;
+ }
+ }
+}
+
+cec_logical_address_t HdmiCecHandler::allocateLogicalAddress(cec_device_type_t type,
+ cec_logical_address_t preferredAddr) {
+ const cec_logical_address_t* addrPool;
+ size_t poolSize;
+ if (getLogicalAddressPool(type, &addrPool, &poolSize) < 0) {
+ return CEC_ADDR_UNREGISTERED;
+ }
+ unsigned start = 0;
+
+ // Find the index of preferred address in the pool. If not found, the start
+ // position will be 0. This happens when the passed preferredAddr is set to
+ // CEC_ADDR_UNREGISTERED, meaning that no preferred address is given.
+ for (unsigned i = 0; i < poolSize; i++) {
+ if (addrPool[i] == preferredAddr) {
+ start = i;
+ break;
+ }
+ }
+ for (unsigned i = 0; i < poolSize; i++) {
+ cec_logical_address_t addr = addrPool[(start + i) % poolSize];
+ if (!sendPing(addr)) {
+ // Failure in pinging means the address is available, not taken by any device.
+ ALOGV("Logical Address Allocation success: %d", addr);
+ return addr;
+ }
+ }
+ ALOGE("Logical Address Allocation failed");
+ return CEC_ADDR_UNREGISTERED;
+}
+
+bool HdmiCecHandler::sendPing(cec_logical_address addr) {
+ cec_message_t msg;
+ msg.initiator = msg.destination = addr;
+ msg.length = 0;
+ return !mDevice->send_message(mDevice, &msg);
+
+}
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+bool HdmiCecHandler::getLogicalAddressPool(cec_device_type_t deviceType,
+ const cec_logical_address_t** addrPool, size_t* poolSize) {
+ switch (deviceType) {
+ case CEC_DEVICE_TV:
+ *addrPool = TV_ADDR_POOL;
+ *poolSize = ARRAY_SIZE(TV_ADDR_POOL);
+ break;
+ case CEC_DEVICE_RECORDER:
+ *addrPool = RECORDER_ADDR_POOL;
+ *poolSize = ARRAY_SIZE(RECORDER_ADDR_POOL);
+ break;
+ case CEC_DEVICE_TUNER:
+ *addrPool = TUNER_ADDR_POOL;
+ *poolSize = ARRAY_SIZE(TUNER_ADDR_POOL);
+ break;
+ case CEC_DEVICE_PLAYBACK:
+ *addrPool = PLAYBACK_ADDR_POOL;
+ *poolSize = ARRAY_SIZE(PLAYBACK_ADDR_POOL);
+ break;
+ default:
+ ALOGE("Unsupported device type: %d", deviceType);
+ return false;
+ }
+ return true;
+}
+
+#undef ARRAY_SIZE
+
void HdmiCecHandler::dispatchMessage(const MessageEntry& entry) {
int type = entry.first;
mMessageQueueLock.unlock();
@@ -434,7 +552,7 @@
void HdmiCecHandler::processIncomingMessage(const cec_message_t& msg) {
int opcode = msg.body[0];
if (opcode == CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS) {
- sendReportPhysicalAddress();
+ sendReportPhysicalAddress(msg.destination);
} else if (opcode == CEC_MESSAGE_REQUEST_ACTIVE_SOURCE) {
handleRequestActiveSource();
} else if (opcode == CEC_MESSAGE_GET_OSD_NAME) {
@@ -507,7 +625,7 @@
JNIEnv* env = AndroidRuntime::getJNIEnv();
jint activeDeviceType = env->CallIntMethod(mCallbacksObj,
gHdmiCecServiceClassInfo.getActiveSource);
- if (activeDeviceType != INACTIVE_DEVICE_TYPE) {
+ if (activeDeviceType != CEC_DEVICE_INACTIVE) {
sendActiveSource(getLogicalAddress(static_cast<cec_device_type_t>(activeDeviceType)));
}
checkAndClearExceptionFromCallback(env, __FUNCTION__);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 4085991..34ae8b4 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -870,7 +870,7 @@
if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) {
if (policyFlags & POLICY_FLAG_INTERACTIVE) {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
- } else if (policyFlags & (POLICY_FLAG_WAKE | POLICY_FLAG_WAKE_DROPPED)) {
+ } else if (policyFlags & POLICY_FLAG_WAKE) {
JNIEnv* env = jniEnv();
jint wmActions = env->CallIntMethod(mServiceObj,
gServiceClassInfo.interceptWakeMotionBeforeQueueing,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
index 629dea2..1647425 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
@@ -53,7 +53,6 @@
private static final String ATTR_NAME = "name";
private static final String ATTR_PACKAGE = "package";
private static final String ATTR_USERID = "userId";
- private static final String ATTR_ENABLED = "profileEnabled";
private AtomicFile fileForWriting;
@@ -104,8 +103,7 @@
*/
static DeviceOwner createWithProfileOwner(String packageName, String ownerName, int userId) {
DeviceOwner owner = new DeviceOwner();
- owner.mProfileOwners.put(
- userId, new OwnerInfo(ownerName, packageName, false /* disabled */));
+ owner.mProfileOwners.put(userId, new OwnerInfo(ownerName, packageName));
return owner;
}
@@ -122,7 +120,7 @@
}
void setProfileOwner(String packageName, String ownerName, int userId) {
- mProfileOwners.put(userId, new OwnerInfo(ownerName, packageName, false /* disabled */));
+ mProfileOwners.put(userId, new OwnerInfo(ownerName, packageName));
}
void removeProfileOwner(int userId) {
@@ -139,19 +137,6 @@
return profileOwner != null ? profileOwner.name : null;
}
- boolean isProfileEnabled(int userId) {
- OwnerInfo profileOwner = mProfileOwners.get(userId);
- return profileOwner != null ? profileOwner.enabled : true;
- }
-
- void setProfileEnabled(int userId) {
- OwnerInfo profileOwner = mProfileOwners.get(userId);
- if (profileOwner == null) {
- throw new IllegalArgumentException("No profile owner exists.");
- }
- profileOwner.enabled = true;
- }
-
boolean hasDeviceOwner() {
return mDeviceOwner != null;
}
@@ -203,12 +188,9 @@
} else if (tag.equals(TAG_PROFILE_OWNER)) {
String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME);
- Boolean profileEnabled = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_ENABLED));
int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USERID));
mProfileOwners.put(userId,
- new OwnerInfo(
- profileOwnerPackageName, profileOwnerName, profileEnabled));
+ new OwnerInfo(profileOwnerName, profileOwnerPackageName));
} else {
throw new XmlPullParserException(
"Unexpected tag in device owner file: " + tag);
@@ -251,7 +233,6 @@
out.startTag(null, TAG_PROFILE_OWNER);
out.attribute(null, ATTR_PACKAGE, owner.getValue().packageName);
out.attribute(null, ATTR_NAME, owner.getValue().name);
- out.attribute(null, ATTR_ENABLED, String.valueOf(owner.getValue().enabled));
out.attribute(null, ATTR_USERID, Integer.toString(owner.getKey()));
out.endTag(null, TAG_PROFILE_OWNER);
}
@@ -292,16 +273,10 @@
static class OwnerInfo {
public String name;
public String packageName;
- public boolean enabled = true; // only makes sense for managed profiles
-
- public OwnerInfo(String name, String packageName, boolean enabled) {
- this(name, packageName);
- this.enabled = enabled;
- }
public OwnerInfo(String name, String packageName) {
this.name = name;
this.packageName = packageName;
}
}
-}
\ No newline at end of file
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b82a126..aaa97eb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2903,11 +2903,10 @@
int userId = UserHandle.getCallingUserId();
Slog.d(LOG_TAG, "Enabling the profile for: " + userId);
- mDeviceOwner.setProfileEnabled(userId);
- mDeviceOwner.writeOwnerFile();
-
+ UserManager um = UserManager.get(mContext);
long id = Binder.clearCallingIdentity();
try {
+ um.setUserEnabled(userId);
Intent intent = new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED);
intent.putExtra(Intent.EXTRA_USER, new UserHandle(UserHandle.getCallingUserId()));
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
@@ -2948,23 +2947,6 @@
return null;
}
- @Override
- public boolean isProfileEnabled(int userHandle) {
- if (!mHasFeature) {
- // If device policy management is not enabled, then the userHandle cannot belong to a
- // managed profile. All other profiles are considered enabled.
- return true;
- }
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
-
- synchronized (this) {
- if (mDeviceOwner != null) {
- return mDeviceOwner.isProfileEnabled(userHandle);
- }
- }
- return true;
- }
-
private boolean isDeviceProvisioned() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0) > 0;
@@ -3099,6 +3081,51 @@
}
}
+ public void forwardMatchingIntents(ComponentName who, IntentFilter filter, int flags) {
+ int callingUserId = UserHandle.getCallingUserId();
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+ IPackageManager pm = AppGlobals.getPackageManager();
+ long id = Binder.clearCallingIdentity();
+ try {
+ if ((flags & DevicePolicyManager.FLAG_TO_PRIMARY_USER) != 0) {
+ pm.addForwardingIntentFilter(filter, callingUserId, UserHandle.USER_OWNER);
+ }
+ if ((flags & DevicePolicyManager.FLAG_TO_MANAGED_PROFILE) != 0) {
+ pm.addForwardingIntentFilter(filter, UserHandle.USER_OWNER, callingUserId);
+ }
+ } catch (RemoteException re) {
+ // Shouldn't happen
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ }
+ }
+
+ public void clearForwardingIntentFilters(ComponentName who) {
+ int callingUserId = UserHandle.getCallingUserId();
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ IPackageManager pm = AppGlobals.getPackageManager();
+ long id = Binder.clearCallingIdentity();
+ try {
+ pm.clearForwardingIntentFilters(callingUserId);
+ pm.clearForwardingIntentFilters(UserHandle.USER_OWNER);
+ } catch (RemoteException re) {
+ // Shouldn't happen
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ }
+ }
+
@Override
public Bundle getApplicationRestrictions(ComponentName who, String packageName) {
final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
@@ -3118,4 +3145,24 @@
}
}
}
+
+ @Override
+ public void setUserRestriction(ComponentName who, String key, boolean enabled) {
+ final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+ UserManager um = UserManager.get(mContext);
+ long id = Binder.clearCallingIdentity();
+ try {
+ um.setUserRestriction(key, enabled, userHandle);
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 259a6d3..3d82027 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -29,6 +29,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.media.AudioService;
+import android.os.Build;
import android.os.Environment;
import android.os.FactoryTest;
import android.os.Handler;
@@ -59,6 +60,7 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.clipboard.ClipboardService;
import com.android.server.content.ContentService;
+import com.android.server.devicepolicy.DevicePolicyManagerService;
import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;
import com.android.server.hdmi.HdmiControlService;
@@ -111,10 +113,10 @@
*/
private static final String BACKUP_MANAGER_SERVICE_CLASS =
"com.android.server.backup.BackupManagerService$Lifecycle";
- private static final String DEVICE_POLICY_MANAGER_SERVICE_CLASS =
- "com.android.server.devicepolicy.DevicePolicyManagerService$Lifecycle";
private static final String APPWIDGET_SERVICE_CLASS =
"com.android.server.appwidget.AppWidgetService";
+ private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS =
+ "com.android.server.voiceinteraction.VoiceInteractionManagerService";
private static final String PRINT_MANAGER_SERVICE_CLASS =
"com.android.server.print.PrintManagerService";
private static final String USB_SERVICE_CLASS =
@@ -199,6 +201,10 @@
// as efficient as possible with its memory usage.
VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
+ // Some devices rely on runtime fingerprint generation, so make sure
+ // we've defined it before booting further.
+ Build.ensureFingerprintProperty();
+
// Within the system server, it is an error to access Environment paths without
// explicitly specifying a user.
Environment.setUserRequired(true);
@@ -291,6 +297,7 @@
// Activity manager runs the show.
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
+ mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
}
private void startCoreServices() {
@@ -547,9 +554,9 @@
}
try {
- if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
- mSystemServiceManager.startService(DEVICE_POLICY_MANAGER_SERVICE_CLASS);
- }
+ // Always start the Device Policy Manager, so that the API is compatible with
+ // API8.
+ mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
} catch (Throwable e) {
reportWtf("starting DevicePolicyService", e);
}
@@ -827,6 +834,15 @@
} catch (Throwable e) {
reportWtf("starting Recognition Service", e);
}
+
+ try {
+ if (pm.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
+ Slog.i(TAG, "Voice Recognition Service");
+ mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
+ }
+ } catch (Throwable e) {
+ reportWtf("starting Voice Recognition Service", e);
+ }
}
try {
diff --git a/services/voiceinteraction/Android.mk b/services/voiceinteraction/Android.mk
new file mode 100644
index 0000000..c9e5dd0
--- /dev/null
+++ b/services/voiceinteraction/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := services.voiceinteraction
+
+LOCAL_SRC_FILES += \
+ $(call all-java-files-under,java)
+
+LOCAL_JAVA_LIBRARIES := services.core
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
new file mode 100644
index 0000000..045c0f6
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.voiceinteraction;
+
+import android.Manifest;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.voice.IVoiceInteractionService;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.Slog;
+import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.SystemService;
+import com.android.server.UiThread;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+
+/**
+ * SystemService that publishes an IVoiceInteractionManagerService.
+ */
+public class VoiceInteractionManagerService extends SystemService {
+
+ static final String TAG = "VoiceInteractionManagerService";
+
+ final Context mContext;
+ final ContentResolver mResolver;
+
+ public VoiceInteractionManagerService(Context context) {
+ super(context);
+ mContext = context;
+ mResolver = context.getContentResolver();
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.VOICE_INTERACTION_MANAGER_SERVICE, mServiceStub);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ mServiceStub.systemRunning(isSafeMode());
+ }
+ }
+
+ @Override
+ public void onSwitchUser(int userHandle) {
+ mServiceStub.switchUser(userHandle);
+ }
+
+ // implementation entry point and binder service
+ private final VoiceInteractionManagerServiceStub mServiceStub
+ = new VoiceInteractionManagerServiceStub();
+
+ class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub {
+
+ VoiceInteractionManagerServiceImpl mImpl;
+
+ private boolean mSafeMode;
+ private int mCurUser;
+
+ public void systemRunning(boolean safeMode) {
+ mSafeMode = safeMode;
+
+ mPackageMonitor.register(mContext, BackgroundThread.getHandler().getLooper(),
+ UserHandle.ALL, true);
+ new SettingsObserver(UiThread.getHandler());
+
+ synchronized (this) {
+ mCurUser = ActivityManager.getCurrentUser();
+ switchImplementationIfNeededLocked();
+ }
+ }
+
+ public void switchUser(int userHandle) {
+ synchronized (this) {
+ mCurUser = userHandle;
+ switchImplementationIfNeededLocked();
+ }
+ }
+
+ void switchImplementationIfNeededLocked() {
+ if (!mSafeMode) {
+ String curService = Settings.Secure.getStringForUser(
+ mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser);
+ ComponentName serviceComponent = null;
+ if (curService != null && !curService.isEmpty()) {
+ try {
+ serviceComponent = ComponentName.unflattenFromString(curService);
+ } catch (RuntimeException e) {
+ Slog.wtf(TAG, "Bad voice interaction service name " + curService, e);
+ serviceComponent = null;
+ }
+ }
+ if (mImpl == null || mImpl.mUser != mCurUser
+ || !mImpl.mComponent.equals(serviceComponent)) {
+ if (mImpl != null) {
+ mImpl.shutdownLocked();
+ }
+ if (serviceComponent != null) {
+ mImpl = new VoiceInteractionManagerServiceImpl(mContext,
+ UiThread.getHandler(), this, mCurUser, serviceComponent);
+ mImpl.startLocked();
+ } else {
+ mImpl = null;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void startVoiceActivity(Intent intent, String resolvedType,
+ IVoiceInteractionService service, Bundle args) {
+ synchronized (this) {
+ if (mImpl == null || service.asBinder() != mImpl.mService.asBinder()) {
+ throw new SecurityException(
+ "Caller is not the current voice interaction service");
+ }
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.startVoiceActivityLocked(callingPid, callingUid,
+ intent, resolvedType, args);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
+ @Override
+ public int deliverNewSession(IBinder token, IVoiceInteractionSession session,
+ IVoiceInteractor interactor) {
+ synchronized (this) {
+ if (mImpl == null) {
+ Slog.w(TAG, "deliverNewSession without running voice interaction service");
+ return ActivityManager.START_CANCELED;
+ }
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mImpl.deliverNewSessionLocked(callingPid, callingUid, token, session,
+ interactor);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump PowerManager from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+ synchronized (this) {
+ pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n");
+ if (mImpl == null) {
+ pw.println(" (No active implementation)");
+ return;
+ }
+ mImpl.dumpLocked(fd, pw, args);
+ }
+ }
+
+ class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.VOICE_INTERACTION_SERVICE), false, this);
+ }
+
+ @Override public void onChange(boolean selfChange) {
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ switchImplementationIfNeededLocked();
+ }
+ }
+ }
+
+ PackageMonitor mPackageMonitor = new PackageMonitor() {
+ @Override
+ public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
+ return super.onHandleForceStop(intent, packages, uid, doit);
+ }
+
+ @Override
+ public void onHandleUserStop(Intent intent, int userHandle) {
+ super.onHandleUserStop(intent, userHandle);
+ }
+
+ @Override
+ public void onPackageDisappeared(String packageName, int reason) {
+ super.onPackageDisappeared(packageName, reason);
+ }
+
+ @Override
+ public void onPackageAppeared(String packageName, int reason) {
+ super.onPackageAppeared(packageName, reason);
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ super.onPackageModified(packageName);
+ }
+
+ @Override
+ public void onSomePackagesChanged() {
+ super.onSomePackagesChanged();
+ }
+ };
+ }
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
new file mode 100644
index 0000000..6bbd1c1
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.voiceinteraction;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionService;
+import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.IVoiceInteractionSessionService;
+import android.service.voice.VoiceInteractionService;
+import android.service.voice.VoiceInteractionServiceInfo;
+import android.util.Slog;
+import com.android.internal.app.IVoiceInteractor;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+class VoiceInteractionManagerServiceImpl {
+ final static String TAG = "VoiceInteractionServiceManager";
+
+ final boolean mValid;
+
+ final Context mContext;
+ final Handler mHandler;
+ final Object mLock;
+ final int mUser;
+ final ComponentName mComponent;
+ final IActivityManager mAm;
+ final VoiceInteractionServiceInfo mInfo;
+ final ComponentName mSessionComponentName;
+ boolean mBound = false;
+ IVoiceInteractionService mService;
+
+ SessionConnection mActiveSession;
+
+ final ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ mService = IVoiceInteractionService.Stub.asInterface(service);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+ };
+
+ final class SessionConnection implements ServiceConnection {
+ final IBinder mToken = new Binder();
+ final Intent mIntent;
+ final String mResolvedType;
+ final Bundle mArgs;
+ boolean mBound;
+ IVoiceInteractionSessionService mService;
+ IVoiceInteractionSession mSession;
+ IVoiceInteractor mInteractor;
+
+ SessionConnection(Intent intent, String resolvedType, Bundle args) {
+ mIntent = intent;
+ mResolvedType = resolvedType;
+ mArgs = args;
+ Intent serviceIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
+ serviceIntent.setComponent(mSessionComponentName);
+ mBound = mContext.bindServiceAsUser(serviceIntent, this,
+ Context.BIND_AUTO_CREATE, new UserHandle(mUser));
+ if (!mBound) {
+ Slog.w(TAG, "Failed binding to voice interaction session service " + mComponent);
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ mService = IVoiceInteractionSessionService.Stub.asInterface(service);
+ if (mActiveSession == this) {
+ try {
+ mService.newSession(mToken, mArgs);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed making new session", e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+
+ public void cancel() {
+ if (mBound) {
+ mContext.unbindService(this);
+ mBound = false;
+ mService = null;
+ mSession = null;
+ mInteractor = null;
+ }
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mToken="); pw.println(mToken);
+ pw.print(prefix); pw.print("mIntent="); pw.println(mIntent);
+ pw.print(" mResolvedType="); pw.println(mResolvedType);
+ pw.print(prefix); pw.print("mArgs="); pw.println(mArgs);
+ pw.print(prefix); pw.print("mBound="); pw.println(mBound);
+ if (mBound) {
+ pw.print(prefix); pw.print("mService="); pw.println(mService);
+ pw.print(prefix); pw.print("mSession="); pw.println(mSession);
+ pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor);
+ }
+ }
+ };
+
+ VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock,
+ int userHandle, ComponentName service) {
+ mContext = context;
+ mHandler = handler;
+ mLock = lock;
+ mUser = userHandle;
+ mComponent = service;
+ mAm = ActivityManagerNative.getDefault();
+ VoiceInteractionServiceInfo info;
+ try {
+ info = new VoiceInteractionServiceInfo(context.getPackageManager(), service);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Voice interaction service not found: " + service);
+ mInfo = null;
+ mSessionComponentName = null;
+ mValid = false;
+ return;
+ }
+ mInfo = info;
+ if (mInfo.getParseError() != null) {
+ Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError());
+ mSessionComponentName = null;
+ mValid = false;
+ return;
+ }
+ mValid = true;
+ mSessionComponentName = new ComponentName(service.getPackageName(),
+ mInfo.getSessionService());
+ }
+
+ public void startVoiceActivityLocked(int callingPid, int callingUid, Intent intent,
+ String resolvedType, Bundle args) {
+ if (mActiveSession != null) {
+ mActiveSession.cancel();
+ mActiveSession = null;
+ }
+ mActiveSession = new SessionConnection(intent, resolvedType, args);
+ intent.addCategory(Intent.CATEGORY_VOICE);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ }
+
+ public int deliverNewSessionLocked(int callingPid, int callingUid, IBinder token,
+ IVoiceInteractionSession session, IVoiceInteractor interactor) {
+ try {
+ if (mActiveSession == null || token != mActiveSession.mToken) {
+ Slog.w(TAG, "deliverNewSession does not match active session");
+ return ActivityManager.START_CANCELED;
+ }
+ mActiveSession.mSession = session;
+ mActiveSession.mInteractor = interactor;
+ return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid,
+ mActiveSession.mIntent, mActiveSession.mResolvedType,
+ mActiveSession.mSession, mActiveSession.mInteractor,
+ 0, null, null, null, mUser);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Unexpected remote error", e);
+ }
+ }
+
+ public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!mValid) {
+ pw.print(" NOT VALID: ");
+ if (mInfo == null) {
+ pw.println("no info");
+ } else {
+ pw.println(mInfo.getParseError());
+ }
+ return;
+ }
+ pw.print(" mComponent="); pw.println(mComponent.flattenToShortString());
+ pw.print(" Session service="); pw.println(mInfo.getSessionService());
+ pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity());
+ pw.print(" mBound="); pw.print(mBound); pw.print(" mService="); pw.println(mService);
+ if (mActiveSession != null) {
+ pw.println(" Active session:");
+ mActiveSession.dump(" ", pw);
+ }
+ }
+
+ void startLocked() {
+ Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
+ intent.setComponent(mComponent);
+ mBound = mContext.bindServiceAsUser(intent, mConnection,
+ Context.BIND_AUTO_CREATE, new UserHandle(mUser));
+ if (!mBound) {
+ Slog.w(TAG, "Failed binding to voice interaction service " + mComponent);
+ }
+ }
+
+ void shutdownLocked() {
+ if (mBound) {
+ mContext.unbindService(mConnection);
+ mBound = false;
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d5c7caa..407a8d1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1094,6 +1094,10 @@
public static final int SIM_STATE_NETWORK_LOCKED = 4;
/** SIM card state: Ready */
public static final int SIM_STATE_READY = 5;
+ /** SIM card state: SIM Card Error, Sim Card is present but faulty
+ *@hide
+ */
+ public static final int SIM_STATE_CARD_IO_ERROR = 6;
/**
* @return true if a ICC card is present
@@ -1120,6 +1124,7 @@
* @see #SIM_STATE_PUK_REQUIRED
* @see #SIM_STATE_NETWORK_LOCKED
* @see #SIM_STATE_READY
+ * @see #SIM_STATE_CARD_IO_ERROR
*/
public int getSimState() {
String prop = SystemProperties.get(TelephonyProperties.PROPERTY_SIM_STATE);
@@ -1138,6 +1143,9 @@
else if ("READY".equals(prop)) {
return SIM_STATE_READY;
}
+ else if ("CARD_IO_ERROR".equals(prop)) {
+ return SIM_STATE_CARD_IO_ERROR;
+ }
else {
return SIM_STATE_UNKNOWN;
}
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index 236bb2f..8029713 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -28,6 +28,8 @@
public static final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY";
/* ABSENT means ICC is missing */
public static final String INTENT_VALUE_ICC_ABSENT = "ABSENT";
+ /* CARD_IO_ERROR means for three consecutive times there was SIM IO error */
+ static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = "CARD_IO_ERROR";
/* LOCKED means ICC is locked by pin or by network */
public static final String INTENT_VALUE_ICC_LOCKED = "LOCKED";
/* READY means ICC is ready to access */
@@ -63,7 +65,8 @@
NETWORK_LOCKED,
READY,
NOT_READY,
- PERM_DISABLED;
+ PERM_DISABLED,
+ CARD_IO_ERROR;
public boolean isPinLocked() {
return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
@@ -72,7 +75,7 @@
public boolean iccCardExist() {
return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)
|| (this == NETWORK_LOCKED) || (this == READY)
- || (this == PERM_DISABLED));
+ || (this == PERM_DISABLED) || (this == CARD_IO_ERROR));
}
}
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index ac741e7..5c2583b 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -873,9 +873,6 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.hwui.TEST" />
-
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
diff --git a/tests/HwAccelerationTest/res/layout/projection_clipping.xml b/tests/HwAccelerationTest/res/layout/projection_clipping.xml
index 7caf90a..7177fc8f 100644
--- a/tests/HwAccelerationTest/res/layout/projection_clipping.xml
+++ b/tests/HwAccelerationTest/res/layout/projection_clipping.xml
@@ -6,7 +6,7 @@
<FrameLayout
android:translationX="50dp"
android:translationY="50dp"
- android:translationZ="30dp"
+ android:elevation="30dp"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@drawable/round_rect_background">
diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
index 09531fd..1d209dd 100644
--- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
+++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
@@ -1,17 +1,13 @@
package com.example.renderthread;
-import android.animation.TimeInterpolator;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
-import android.os.SystemClock;
-import android.view.RenderNode;
import android.view.HardwareRenderer;
-import android.view.ThreadedRenderer;
+import android.view.RenderNodeAnimator;
import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
@@ -23,6 +19,8 @@
public class MainActivity extends Activity implements OnItemClickListener {
+ static final int TRANSLATION_Y = 1;
+ static final int DELTA_TYPE_DELTA = 1;
static final int DURATION = 400;
static final String KEY_NAME = "name";
@@ -66,82 +64,21 @@
}
}
- private static class DisplayListAnimator {
- private static final TimeInterpolator sDefaultInterpolator =
- new AccelerateDecelerateInterpolator();
-
- RenderNode mDisplayList;
- float mFromValue;
- float mDelta;
- long mDuration = DURATION * 2;
- long mStartTime;
-
- DisplayListAnimator(View view, float translateXBy) {
- mDelta = translateXBy;
- mFromValue = view.getTranslationY();
- mDisplayList = view.getDisplayList();
- }
-
- boolean animate(long currentTime) {
- if (mStartTime == 0) mStartTime = currentTime;
-
- float fraction = (float)(currentTime - mStartTime) / mDuration;
- if (fraction > 1) {
- return false;
- }
- fraction = sDefaultInterpolator.getInterpolation(fraction);
- float translation = mFromValue + (mDelta * fraction);
- mDisplayList.setTranslationY(translation);
- return fraction < 1f;
- }
- }
-
- private static class AnimationExecutor implements Runnable {
- DisplayListAnimator[] mAnimations;
- ThreadedRenderer mRenderer;
-
- AnimationExecutor(ThreadedRenderer renderer, DisplayListAnimator[] animations) {
- mRenderer = renderer;
- mAnimations = animations;
- ThreadedRenderer.postToRenderThread(this);
- }
-
- @Override
- public void run() {
- boolean hasMore = false;
- long now = SystemClock.uptimeMillis();
- for (DisplayListAnimator animator : mAnimations) {
- hasMore |= animator.animate(now);
- }
- mRenderer.repeatLastDraw();
- if (hasMore) {
- ThreadedRenderer.postToRenderThread(this);
- }
- }
-
- }
-
@Override
public void onItemClick(final AdapterView<?> adapterView, View clickedView,
int clickedPosition, long clickedId) {
int topPosition = adapterView.getFirstVisiblePosition();
int dy = adapterView.getHeight();
- final DisplayListAnimator[] animators = new DisplayListAnimator[adapterView.getChildCount()];
for (int i = 0; i < adapterView.getChildCount(); i++) {
int pos = topPosition + i;
View child = adapterView.getChildAt(i);
float delta = (pos - clickedPosition) * 1.1f;
if (delta == 0) delta = -1;
- animators[i] = new DisplayListAnimator(child, dy * delta);
+ RenderNodeAnimator animator = new RenderNodeAnimator(
+ TRANSLATION_Y, DELTA_TYPE_DELTA, dy * delta);
+ animator.setDuration(DURATION);
+ animator.start(child);
}
- adapterView.invalidate();
- adapterView.post(new Runnable() {
-
- @Override
- public void run() {
- new AnimationExecutor((ThreadedRenderer) adapterView.getHardwareRenderer(), animators);
- }
- });
//mHandler.postDelayed(mLaunchActivity, (long) (DURATION * .4));
mLaunchActivity.run();
}
diff --git a/tests/VoiceInteraction/Android.mk b/tests/VoiceInteraction/Android.mk
new file mode 100644
index 0000000..8decca7
--- /dev/null
+++ b/tests/VoiceInteraction/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := VoiceInteraction
+
+include $(BUILD_PACKAGE)
diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml
new file mode 100644
index 0000000..ac0f701
--- /dev/null
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.voiceinteraction">
+
+ <application>
+ <activity android:name="VoiceInteractionMain" android:label="Voice Interaction">
+ <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>
+ <service android:name="MainInteractionService"
+ android:permission="android.permission.BIND_VOICE_INTERACTION"
+ android:process=":interactor">
+ <meta-data android:name="android.voice_interaction"
+ android:resource="@xml/interaction_service" />
+ <intent-filter>
+ <action android:name="android.service.voice.VoiceInteractionService" />
+ </intent-filter>
+ </service>
+ <service android:name="MainInteractionSessionService"
+ android:permission="android.permission.BIND_VOICE_INTERACTION"
+ android:process=":session">
+ </service>
+ <activity android:name="TestInteractionActivity" android:label="Voice Interaction Target">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.VOICE" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/packages/SystemUI/res/layout/notification_adaptive_wrapper.xml b/tests/VoiceInteraction/res/layout/main.xml
similarity index 61%
copy from packages/SystemUI/res/layout/notification_adaptive_wrapper.xml
copy to tests/VoiceInteraction/res/layout/main.xml
index 15d0890..3d7a418 100644
--- a/packages/SystemUI/res/layout/notification_adaptive_wrapper.xml
+++ b/tests/VoiceInteraction/res/layout/main.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,8 +13,19 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<SizeAdaptiveLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="@android:color/background_dark"
- android:id="@+id/notification_adaptive_wrapper"
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ >
+
+ <Button android:id="@+id/start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/start"
+ />
+
+</LinearLayout>
+
+
diff --git a/tests/VoiceInteraction/res/layout/test_interaction.xml b/tests/VoiceInteraction/res/layout/test_interaction.xml
new file mode 100644
index 0000000..2abf65194
--- /dev/null
+++ b/tests/VoiceInteraction/res/layout/test_interaction.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ >
+
+ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="We are interacting!"
+ />
+
+ <TextView android:id="@+id/log"
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1"
+ android:layout_marginTop="10dp"
+ android:textSize="12sp"
+ android:textColor="#ffffffff"
+ />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_adaptive_wrapper.xml b/tests/VoiceInteraction/res/values/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/notification_adaptive_wrapper.xml
copy to tests/VoiceInteraction/res/values/strings.xml
index 15d0890..12edb31 100644
--- a/packages/SystemUI/res/layout/notification_adaptive_wrapper.xml
+++ b/tests/VoiceInteraction/res/values/strings.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,8 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<SizeAdaptiveLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="@android:color/background_dark"
- android:id="@+id/notification_adaptive_wrapper"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+
+<resources>
+
+ <string name="start">Start!</string>
+
+</resources>
+
diff --git a/tests/VoiceInteraction/res/xml/interaction_service.xml b/tests/VoiceInteraction/res/xml/interaction_service.xml
new file mode 100644
index 0000000..45bd994d
--- /dev/null
+++ b/tests/VoiceInteraction/res/xml/interaction_service.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:sessionService="com.android.test.voiceinteraction.MainInteractionSessionService" />
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
new file mode 100644
index 0000000..008d97b
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.voiceinteraction;
+
+import android.content.Intent;
+import android.service.voice.VoiceInteractionService;
+import android.util.Log;
+
+public class MainInteractionService extends VoiceInteractionService {
+ static final String TAG = "MainInteractionService";
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.i(TAG, "Creating " + this);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ startVoiceActivity(new Intent(this, TestInteractionActivity.class), null);
+ stopSelf(startId);
+ return START_NOT_STICKY;
+ }
+}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
new file mode 100644
index 0000000..0fc563b
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.voiceinteraction;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.util.Log;
+
+public class MainInteractionSession extends VoiceInteractionSession {
+ static final String TAG = "MainInteractionSession";
+
+ final Bundle mArgs;
+
+ MainInteractionSession(Context context, Bundle args) {
+ super(context);
+ mArgs = args;
+ }
+
+ @Override
+ public boolean[] onGetSupportedCommands(Caller caller, String[] commands) {
+ return new boolean[commands.length];
+ }
+
+ @Override
+ public void onConfirm(Caller caller, Request request, String prompt, Bundle extras) {
+ Log.i(TAG, "onConform: prompt=" + prompt + " extras=" + extras);
+ request.sendConfirmResult(true, null);
+ }
+
+ @Override
+ public void onCommand(Caller caller, Request request, String command, Bundle extras) {
+ Log.i(TAG, "onCommand: command=" + command + " extras=" + extras);
+ request.sendCommandResult(true, null);
+ }
+
+ @Override
+ public void onCancel(Request request) {
+ Log.i(TAG, "onCancel");
+ request.sendCancelResult();
+ }
+}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSessionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSessionService.java
new file mode 100644
index 0000000..8864d71
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSessionService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.voiceinteraction;
+
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.service.voice.VoiceInteractionSessionService;
+
+public class MainInteractionSessionService extends VoiceInteractionSessionService {
+ @Override
+ public VoiceInteractionSession onNewSession(Bundle args) {
+ return new MainInteractionSession(this, args);
+ }
+}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
new file mode 100644
index 0000000..9c772ff
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.voiceinteraction;
+
+import android.app.Activity;
+import android.app.VoiceInteractor;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestInteractionActivity extends Activity {
+ static final String TAG = "TestInteractionActivity";
+
+ VoiceInteractor mInteractor;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (!isVoiceInteraction()) {
+ Log.w(TAG, "Not running as a voice interaction!");
+ finish();
+ return;
+ }
+
+ setContentView(R.layout.test_interaction);
+
+ mInteractor = getVoiceInteractor();
+ VoiceInteractor.ConfirmationRequest req = new VoiceInteractor.ConfirmationRequest(
+ "This is a confirmation", null) {
+ @Override
+ public void onCancel() {
+ Log.i(TAG, "Canceled!");
+ getActivity().finish();
+ }
+
+ @Override
+ public void onConfirmationResult(boolean confirmed, Bundle result) {
+ Log.i(TAG, "Confirmation result: confirmed=" + confirmed + " result=" + result);
+ getActivity().finish();
+ }
+ };
+ mInteractor.submitRequest(req);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java
new file mode 100644
index 0000000..5d212a4
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.voiceinteraction;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+public class VoiceInteractionMain extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.main);
+ findViewById(R.id.start).setOnClickListener(mStartListener);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ View.OnClickListener mStartListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ startService(new Intent(VoiceInteractionMain.this, MainInteractionService.class));
+ }
+ };
+}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 6d9d62e..d0581f6 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -2462,7 +2462,7 @@
status_t
writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
- const char* startTag, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs)
+ const Vector<String8>& startTags, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs)
{
status_t err;
ResXMLTree tree;
@@ -2476,15 +2476,18 @@
tree.restart();
- if (startTag != NULL) {
+ if (!startTags.isEmpty()) {
bool haveStart = false;
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code != ResXMLTree::START_TAG) {
continue;
}
String8 tag(tree.getElementName(&len));
- if (tag == startTag) {
- haveStart = true;
+ const size_t numStartTags = startTags.size();
+ for (size_t i = 0; i < numStartTags; i++) {
+ if (tag == startTags[i]) {
+ haveStart = true;
+ }
}
break;
}
@@ -2571,15 +2574,17 @@
for (size_t k=0; k<K; k++) {
const sp<AaptDir>& d = dirs.itemAt(k);
const String8& dirName = d->getLeaf();
+ Vector<String8> startTags;
const char* startTag = NULL;
const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL;
if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
tagAttrPairs = &kLayoutTagAttrPairs;
} else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
- startTag = "PreferenceScreen";
+ startTags.add(String8("PreferenceScreen"));
+ startTags.add(String8("preference-headers"));
tagAttrPairs = &kXmlTagAttrPairs;
} else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
- startTag = "menu";
+ startTags.add(String8("menu"));
tagAttrPairs = NULL;
} else {
continue;
@@ -2592,7 +2597,7 @@
const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
const size_t M = files.size();
for (size_t j=0; j<M; j++) {
- err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
+ err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs);
if (err < 0) {
return err;
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 25bb26e..b445b8a 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1340,7 +1340,6 @@
name,
locale,
SourcePos(in->getPrintableSource(), block.getLineNumber()));
- curIsPseudolocalizable = fileIsTranslatable;
}
if (formatted == false16) {
@@ -1352,7 +1351,7 @@
curType = string16;
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
curIsStyled = true;
- curIsPseudolocalizable = (translatable != false16);
+ curIsPseudolocalizable = fileIsTranslatable && (translatable != false16);
} else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) {
curTag = &drawable16;
curType = drawable16;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
index 49027c6..08512e7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
@@ -23,8 +23,10 @@
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.SystemViewCookie;
import com.android.internal.R;
import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.util.Predicate;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuItemImpl;
import com.android.internal.widget.ActionBarAccessor;
@@ -56,6 +58,8 @@
import java.util.ArrayList;
+import static com.android.ide.common.rendering.api.SystemViewCookie.ACTION_BAR_OVERFLOW;
+
/**
* A layout representing the action bar.
*/
@@ -164,10 +168,33 @@
}
// Set action bar to be split, if needed.
- mActionBarView.setSplitView((ActionBarContainer) findViewById(R.id.split_action_bar));
+ ActionBarContainer splitView = (ActionBarContainer) findViewById(R.id.split_action_bar);
+ mActionBarView.setSplitView(splitView);
mActionBarView.setSplitActionBar(mSplit);
inflateMenus();
+
+ // Find if the Overflow Menu Button (the three dots) exists. If yes,
+ // add the view cookie.
+ Predicate<View> overflowMenuButtonTest = new Predicate<View>() {
+ @Override
+ public boolean apply(View view) {
+ return view.getClass().getName()
+ .equals("android.widget.ActionMenuPresenter$OverflowMenuButton");
+ }
+ };
+ View overflowMenu = null;
+ if (mSplit) {
+ if (splitView != null) {
+ overflowMenu = splitView.findViewByPredicate(overflowMenuButtonTest);
+ }
+ }
+ else {
+ overflowMenu = mActionBarView.findViewByPredicate(overflowMenuButtonTest);
+ }
+ if (overflowMenu != null) {
+ mBridgeContext.addViewKey(overflowMenu, new SystemViewCookie(ACTION_BAR_OVERFLOW));
+ }
}
}